Skip to content

Commit

Permalink
Add ability to specify a selection direction for the calendar (#189)
Browse files Browse the repository at this point in the history
For example "StartToEnd" where after selecting a date, only later dates
can be selected.
  • Loading branch information
ME-MarvinE authored Dec 3, 2024
2 parents 64b25df + c9b2658 commit 96788b1
Showing 9 changed files with 191 additions and 6 deletions.
11 changes: 11 additions & 0 deletions XCalendar.Core/Enums/SelectionDirection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace XCalendar.Core.Enums
{
public enum SelectionDirection
{
Any,
StartToEnd,
EndToStart,
Confined,
ConfinedReverse,
}
}
1 change: 1 addition & 0 deletions XCalendar.Core/Interfaces/ICalendar.cs
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@ namespace XCalendar.Core.Interfaces
DateTime? RangeSelectionStart { get; set; }
DateTime? RangeSelectionEnd { get; set; }
SelectionType SelectionType { get; set; }
SelectionDirection SelectionDirection { get; set; }
#endregion

#region Events
127 changes: 121 additions & 6 deletions XCalendar.Core/Models/Calendar.cs
Original file line number Diff line number Diff line change
@@ -54,6 +54,7 @@ public class Calendar : Calendar<CalendarDay, Event>
private DateTime? _rangeSelectionStart;
private DateTime? _rangeSelectionEnd;
private SelectionType _selectionType = SelectionType.None;
private SelectionDirection _selectionDirection = SelectionDirection.Any;
private ObservableRangeCollection<TEvent> _events = new ObservableRangeCollection<TEvent>();
#endregion

@@ -447,6 +448,25 @@ public SelectionType SelectionType
}
}
}
/// <summary>
/// The direction a selected date has to be relative to the earliest or latest selected date in order to be applied to the selection.
/// </summary>
public SelectionDirection SelectionDirection
{
get
{
return _selectionDirection;
}
set
{
if (_selectionDirection != value)
{
_selectionDirection = value;

OnPropertyChanged(nameof(SelectionDirection));
}
}
}
public ObservableRangeCollection<TEvent> Events
{
get
@@ -537,6 +557,9 @@ protected virtual void OnDaysUpdating()
/// <param name="dateTime">The <see cref="DateTime"/> to select/unselect.</param>
public virtual void ChangeDateSelection(DateTime dateTime)
{
DateTime? startSelectedDate = SelectedDates.Any() ? SelectedDates.Min() : (DateTime?)null;
DateTime? endSelectedDate = SelectedDates.Skip(1).Any() ? SelectedDates.Max() : (DateTime?)null;

switch (SelectionType)
{
case SelectionType.None:
@@ -546,11 +569,41 @@ public virtual void ChangeDateSelection(DateTime dateTime)
switch (SelectionAction)
{
case SelectionAction.Add:
if (!SelectedDates.Any(x => x.Date == dateTime.Date))
{
SelectedDates.Add(dateTime.Date);
bool isCorrectDirection;

switch (SelectionDirection)
{
case SelectionDirection.Any:
isCorrectDirection = true;
break;

case SelectionDirection.StartToEnd:
isCorrectDirection = startSelectedDate == null || dateTime.Date >= startSelectedDate.Value.Date;
break;

case SelectionDirection.EndToStart:
isCorrectDirection = endSelectedDate == null || dateTime.Date <= endSelectedDate.Value.Date;
break;

case SelectionDirection.Confined:
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || (dateTime.Date >= startSelectedDate.Value.Date && dateTime.Date <= endSelectedDate.Value.Date);
break;

case SelectionDirection.ConfinedReverse:
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || dateTime.Date <= startSelectedDate.Value.Date || dateTime.Date >= endSelectedDate.Value.Date;
break;

default:
throw new NotImplementedException();
}

if (isCorrectDirection && !SelectedDates.Any(x => x.Date == dateTime.Date))
{
SelectedDates.Add(dateTime.Date);
}
break;
}
break;

case SelectionAction.Remove:
if (SelectedDates.Any(x => x.Date == dateTime.Date))
@@ -566,12 +619,43 @@ public virtual void ChangeDateSelection(DateTime dateTime)
}
else
{
SelectedDates.Add(dateTime.Date);
bool isCorrectDirection;

switch (SelectionDirection)
{
case SelectionDirection.Any:
isCorrectDirection = true;
break;

case SelectionDirection.StartToEnd:
isCorrectDirection = startSelectedDate == null || dateTime.Date >= startSelectedDate.Value.Date;
break;

case SelectionDirection.EndToStart:
isCorrectDirection = endSelectedDate == null || dateTime.Date <= endSelectedDate.Value.Date;
break;

case SelectionDirection.Confined:
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || (dateTime.Date >= startSelectedDate.Value.Date && dateTime.Date <= endSelectedDate.Value.Date);
break;

case SelectionDirection.ConfinedReverse:
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || dateTime.Date <= startSelectedDate.Value.Date || dateTime.Date >= endSelectedDate.Value.Date;
break;

default:
throw new NotImplementedException();
}

if (isCorrectDirection)
{
SelectedDates.Add(dateTime.Date);
}
}
break;

case SelectionAction.Replace:
if (SelectedDates.Count != 1 || (SelectedDates.Count == 1 && SelectedDates.First().Date != dateTime.Date))
if (SelectedDates.Count != 1 || (SelectedDates.Count == 1 && SelectedDates.First() != dateTime.Date))
{
SelectedDates.Replace(dateTime.Date);
}
@@ -593,7 +677,38 @@ public virtual void ChangeDateSelection(DateTime dateTime)
}
else if (dateTime != RangeSelectionStart)
{
RangeSelectionEnd = dateTime;
bool isCorrectDirection;

switch (SelectionDirection)
{
case SelectionDirection.Any:
isCorrectDirection = true;
break;

case SelectionDirection.StartToEnd:
isCorrectDirection = startSelectedDate == null || dateTime.Date >= startSelectedDate.Value.Date;
break;

case SelectionDirection.EndToStart:
isCorrectDirection = endSelectedDate == null || dateTime.Date <= endSelectedDate.Value.Date;
break;

case SelectionDirection.Confined:
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || (dateTime.Date >= startSelectedDate.Value.Date && dateTime.Date <= endSelectedDate.Value.Date);
break;

case SelectionDirection.ConfinedReverse:
isCorrectDirection = startSelectedDate == null || endSelectedDate == null || dateTime.Date <= startSelectedDate.Value.Date || dateTime.Date >= endSelectedDate.Value.Date;
break;

default:
throw new NotImplementedException();
}

if (isCorrectDirection)
{
RangeSelectionEnd = dateTime;
}
}
break;

Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ public static class PopupHelper
public static List<NavigationLoopMode> AllNavigationLoopModes { get; set; } = Enum.GetValues(typeof(NavigationLoopMode)).Cast<NavigationLoopMode>().ToList();
public static List<DayOfWeek> AllDaysOfWeek { get; set; } = DayOfWeek.Monday.GetWeekAsFirst();
public static List<StackOrientation> AllStackOrientations { get; set; } = Enum.GetValues(typeof(StackOrientation)).Cast<StackOrientation>().ToList();
public static List<SelectionDirection> AllSelectionDirections { get; set; } = Enum.GetValues(typeof(SelectionDirection)).Cast<SelectionDirection>().ToList();
#endregion

#region Methods
Original file line number Diff line number Diff line change
@@ -103,6 +103,7 @@ public class PlaygroundViewModel : BaseViewModel
public ICommand ChangeCalendarVisibilityCommand { get; set; }
public ICommand UpdateCurrentCultureCommand { get; set; }
public ICommand ShowDayEventsOrientationDialogCommand { get; set; }
public ICommand ShowSelectionDirectionDialogCommand { get; set; }

#endregion

@@ -136,6 +137,7 @@ public PlaygroundViewModel()
ChangeCalendarVisibilityCommand = new Command<bool>(ChangeCalendarVisibility);
UpdateCurrentCultureCommand = new Command(UpdateCurrentCulture);
ShowDayEventsOrientationDialogCommand = new Command(ShowDayEventsOrientationDialog);
ShowSelectionDirectionDialogCommand = new Command(ShowSelectionDirectionDialog);

List<ColoredEvent> events = new List<ColoredEvent>()
{
@@ -384,6 +386,10 @@ public async void ShowDayEventsOrientationDialog()
{
DayEventsOrientation = await PopupHelper.ShowSelectItemDialogAsync(DayEventsOrientation, PopupHelper.AllStackOrientations);
}
public async void ShowSelectionDirectionDialog()
{
Calendar.SelectionDirection = await PopupHelper.ShowSelectItemDialogAsync(Calendar.SelectionDirection, PopupHelper.AllSelectionDirections);
}
#endregion
}
}
Original file line number Diff line number Diff line change
@@ -225,6 +225,28 @@
</Label>
</Grid>

<Grid>
<Label
Grid.Column="0"
FontSize="{StaticResource SmallFontSize}"
HorizontalTextAlignment="Center"
Text="SelectionDirection"
VerticalTextAlignment="Center"/>
<Label
Grid.Column="1"
FontSize="{StaticResource SmallFontSize}"
HorizontalTextAlignment="Center"
Text="{Binding Calendar.SelectionDirection}"
TextColor="{StaticResource TappableSettingTextColor}"
VerticalTextAlignment="Center">

<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ShowSelectionDirectionDialogCommand}"/>
</Label.GestureRecognizers>

</Label>
</Grid>

<Grid>
<Label
Grid.Column="0"
1 change: 1 addition & 0 deletions XCalendarMauiSample/Helpers/PopupHelper.cs
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ public static class PopupHelper
public static List<NavigationLoopMode> AllNavigationLoopModes { get; set; } = Enum.GetValues(typeof(NavigationLoopMode)).Cast<NavigationLoopMode>().ToList();
public static List<DayOfWeek> AllDaysOfWeek { get; set; } = DayOfWeek.Monday.GetWeekAsFirst();
public static List<StackOrientation> AllStackOrientations { get; set; } = Enum.GetValues(typeof(StackOrientation)).Cast<StackOrientation>().ToList();
public static List<SelectionDirection> AllSelectionDirections { get; set; } = Enum.GetValues(typeof(SelectionDirection)).Cast<SelectionDirection>().ToList();
#endregion

#region Methods
6 changes: 6 additions & 0 deletions XCalendarMauiSample/ViewModels/PlaygroundViewModel.cs
Original file line number Diff line number Diff line change
@@ -74,6 +74,7 @@ public class PlaygroundViewModel : BaseViewModel
#region Commands
public ICommand ShowCustomDayNamesOrderDialogCommand { get; set; }
public ICommand ShowSelectionActionDialogCommand { get; set; }
public ICommand ShowSelectionDirectionDialogCommand { get; set; }
public ICommand ShowNavigationLoopModeDialogCommand { get; set; }
public ICommand ShowNavigationTimeUnitDialogCommand { get; set; }
public ICommand ShowPageStartModeDialogCommand { get; set; }
@@ -131,6 +132,7 @@ public PlaygroundViewModel()
ChangeCalendarVisibilityCommand = new Command<bool>(ChangeCalendarVisibility);
UpdateCurrentCultureCommand = new Command(UpdateCurrentCulture);
ShowDayEventsOrientationDialogCommand = new Command(ShowDayEventsOrientationDialog);
ShowSelectionDirectionDialogCommand = new Command(ShowSelectionDirectionDialog);

List<ColoredEvent> events = new List<ColoredEvent>()
{
@@ -379,6 +381,10 @@ public async void ShowDayEventsOrientationDialog()
{
DayEventsOrientation = await PopupHelper.ShowSelectItemDialogAsync(DayEventsOrientation, PopupHelper.AllStackOrientations);
}
public async void ShowSelectionDirectionDialog()
{
Calendar.SelectionDirection = await PopupHelper.ShowSelectItemDialogAsync(Calendar.SelectionDirection, PopupHelper.AllSelectionDirections);
}
#endregion
}
}
22 changes: 22 additions & 0 deletions XCalendarMauiSample/Views/PlaygroundPage.xaml
Original file line number Diff line number Diff line change
@@ -222,6 +222,28 @@
</Label>
</Grid>

<Grid>
<Label
Grid.Column="0"
FontSize="{StaticResource SmallFontSize}"
HorizontalTextAlignment="Center"
Text="SelectionDirection"
VerticalTextAlignment="Center"/>
<Label
Grid.Column="1"
FontSize="{StaticResource SmallFontSize}"
HorizontalTextAlignment="Center"
Text="{Binding Calendar.SelectionDirection}"
TextColor="{StaticResource TappableSettingTextColor}"
VerticalTextAlignment="Center">

<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ShowSelectionDirectionDialogCommand}"/>
</Label.GestureRecognizers>

</Label>
</Grid>

<Grid>
<Label
Grid.Column="0"

0 comments on commit 96788b1

Please sign in to comment.