feat: add year button implementation.

This commit is contained in:
rabbitism
2024-06-04 01:44:39 +08:00
parent 1c0972c14d
commit bbac227aae
7 changed files with 212 additions and 60 deletions

View File

@@ -7,30 +7,6 @@
x:Class="Ursa.Demo.Pages.DatePickerDemo"> x:Class="Ursa.Demo.Pages.DatePickerDemo">
<StackPanel Margin="20" HorizontalAlignment="Left"> <StackPanel Margin="20" HorizontalAlignment="Left">
<u:CalendarMonthView /> <u:CalendarMonthView />
<u:CalendarDayButton />
<u:CalendarDayButton IsSelected="True" />
<u:CalendarDayButton IsBlackout="True" />
<StackPanel Orientation="Horizontal">
<u:CalendarDayButton IsStartDate="True" />
<u:CalendarDayButton IsInRange="True" />
<u:CalendarDayButton IsInRange="True" />
<u:CalendarDayButton IsInRange="True" />
<u:CalendarDayButton IsEndDate="True" />
</StackPanel>
<StackPanel Orientation="Horizontal" DataValidationErrors.Errors="{Binding items}">
<u:CalendarDayButton IsPreviewStartDate="True" />
<u:CalendarDayButton IsInRange="True" />
<u:CalendarDayButton IsInRange="True" />
<u:CalendarDayButton IsInRange="True" />
<u:CalendarDayButton IsPreviewEndDate="True" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<u:CalendarDayButton IsInRange="True" />
<u:CalendarDayButton IsInRange="True" IsStartDate="True" />
<u:CalendarDayButton IsInRange="True" />
<u:CalendarDayButton IsEndDate="True" IsInRange="True" />
<u:CalendarDayButton IsInRange="True" />
</StackPanel>
<u:Calendar/> <u:Calendar/>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@@ -26,7 +26,8 @@ public class Calendar: TemplatedControl
public const string PART_MonthView = "PART_MonthView"; public const string PART_MonthView = "PART_MonthView";
public const string PART_YearView = "PART_YearView"; public const string PART_YearView = "PART_YearView";
private CalendarMonthView? _monthGrid; private CalendarMonthView? _monthView;
private CalendarYearView? _yearView;
private DatePickerState _state = DatePickerState.None; private DatePickerState _state = DatePickerState.None;
@@ -78,22 +79,23 @@ public class Calendar: TemplatedControl
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
base.OnApplyTemplate(e); base.OnApplyTemplate(e);
if (_monthGrid is not null) if (_monthView is not null)
{ {
_monthGrid.OnCalendarDayButtonPressed -= OnCalendarDayButtonPressed; _monthView.OnDateSelected -= OnDateSelected;
_monthGrid.OnCalendarDayButtonPointerEnter -= OnCalendarDayButtonPointerEnter; _monthView.OnDatePreviewed -= OnDatePreviewed;
} }
_monthGrid = e.NameScope.Find<CalendarMonthView>(PART_MonthView); _monthView = e.NameScope.Find<CalendarMonthView>(PART_MonthView);
if(_monthGrid is not null) _yearView = e.NameScope.Find<CalendarYearView>(PART_YearView);
if(_monthView is not null)
{ {
_monthGrid.OnCalendarDayButtonPressed += OnCalendarDayButtonPressed; _monthView.OnDateSelected += OnDateSelected;
_monthGrid.OnCalendarDayButtonPointerEnter += OnCalendarDayButtonPointerEnter; _monthView.OnDatePreviewed += OnDatePreviewed;
} }
} }
private void OnCalendarDayButtonPointerEnter(object sender, CalendarDayButtonEventArgs e) private void OnDatePreviewed(object sender, CalendarDayButtonEventArgs e)
{ {
if(_monthGrid is null) if(_monthView is null)
{ {
return; return;
} }
@@ -101,38 +103,38 @@ public class Calendar: TemplatedControl
if (_state is DatePickerState.None) return; if (_state is DatePickerState.None) return;
if (_state == DatePickerState.PreviewStart) if (_state == DatePickerState.PreviewStart)
{ {
_monthGrid.MarkPreview(date, EndDate); _monthView.MarkPreview(date, EndDate);
} }
else if (_state == DatePickerState.PreviewEnd) else if (_state == DatePickerState.PreviewEnd)
{ {
_monthGrid.MarkPreview(StartDate, date); _monthView.MarkPreview(StartDate, date);
} }
} }
private void OnCalendarDayButtonPressed(object sender, CalendarDayButtonEventArgs e) private void OnDateSelected(object sender, CalendarDayButtonEventArgs e)
{ {
if(_monthGrid is null) if(_monthView is null)
{ {
return; return;
} }
var date = e.Date; var date = e.Date;
if (_state == DatePickerState.None) if (_state == DatePickerState.None)
{ {
_monthGrid.ClearSelection(); _monthView.ClearSelection();
_monthGrid.ClearPreview(); _monthView.ClearPreview();
_monthGrid.MarkSelection(date, null); _monthView.MarkSelection(date, null);
_state = DatePickerState.PreviewEnd; _state = DatePickerState.PreviewEnd;
StartDate = date; StartDate = date;
} }
else if (_state == DatePickerState.PreviewStart) else if (_state == DatePickerState.PreviewStart)
{ {
_monthGrid.MarkSelection(date, EndDate); _monthView.MarkSelection(date, EndDate);
_state = DatePickerState.SelectStart; _state = DatePickerState.SelectStart;
StartDate = date; StartDate = date;
} }
else if (_state == DatePickerState.PreviewEnd) else if (_state == DatePickerState.PreviewEnd)
{ {
_monthGrid.MarkSelection(StartDate, date); _monthView.MarkSelection(StartDate, date);
_state = DatePickerState.None; _state = DatePickerState.None;
EndDate = date; EndDate = date;
} }

View File

@@ -4,8 +4,10 @@ using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity;
using Irihi.Avalonia.Shared.Common; using Irihi.Avalonia.Shared.Common;
using Irihi.Avalonia.Shared.Helpers; using Irihi.Avalonia.Shared.Helpers;
using Ursa.EventArgs;
namespace Ursa.Controls; namespace Ursa.Controls;
@@ -128,6 +130,24 @@ public class CalendarDayButton: ContentControl
PseudoClasses.Set(PC_NotCurrentMonth, value); PseudoClasses.Set(PC_NotCurrentMonth, value);
} }
} }
public static readonly RoutedEvent<CalendarDayButtonEventArgs> DateSelectedEvent = RoutedEvent.Register<CalendarDayButton, CalendarDayButtonEventArgs>(
nameof(DateSelected), RoutingStrategies.Bubble);
public event EventHandler<CalendarDayButtonEventArgs> DateSelected
{
add => AddHandler(DateSelectedEvent, value);
remove => RemoveHandler(DateSelectedEvent, value);
}
public static readonly RoutedEvent<CalendarDayButtonEventArgs> DatePreviewedEvent = RoutedEvent.Register<CalendarDayButton, CalendarDayButtonEventArgs>(
nameof(DatePreviewed), RoutingStrategies.Bubble);
public event EventHandler<CalendarDayButtonEventArgs> DatePreviewed
{
add => AddHandler(DateSelectedEvent, value);
remove => RemoveHandler(DateSelectedEvent, value);
}
static CalendarDayButton() static CalendarDayButton()
{ {
@@ -145,4 +165,22 @@ public class CalendarDayButton: ContentControl
PseudoClasses.Set(PC_InRange, IsInRange); PseudoClasses.Set(PC_InRange, IsInRange);
PseudoClasses.Set(PseudoClassName.PC_Selected, IsSelected); PseudoClasses.Set(PseudoClassName.PC_Selected, IsSelected);
} }
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
base.OnPointerPressed(e);
if (this.DataContext is DateTime d)
{
RaiseEvent(new CalendarDayButtonEventArgs(d) { RoutedEvent = DateSelectedEvent, Source = this });
}
}
protected override void OnPointerEntered(PointerEventArgs e)
{
base.OnPointerEntered(e);
if (this.DataContext is DateTime d)
{
RaiseEvent(new CalendarDayButtonEventArgs(d) { RoutedEvent = DateSelectedEvent, Source = this });
}
}
} }

View File

@@ -88,14 +88,26 @@ public class CalendarMonthView : TemplatedControl
var cell = new CalendarDayButton(); var cell = new CalendarDayButton();
cell.SetValue(Grid.RowProperty, i); cell.SetValue(Grid.RowProperty, i);
cell.SetValue(Grid.ColumnProperty, j); cell.SetValue(Grid.ColumnProperty, j);
cell.PointerPressed += OnDayButtonPressed; cell.AddHandler(CalendarDayButton.DateSelectedEvent, OnCellDateSelected);
cell.PointerEntered += OnDayButtonPointerEnter; cell.AddHandler(CalendarDayButton.DatePreviewedEvent, OnCellDatePreviewed);
children.Add(cell); children.Add(cell);
} }
_grid?.Children.AddRange(children); _grid?.Children.AddRange(children);
} }
private void OnCellDatePreviewed(object sender, CalendarDayButtonEventArgs e)
{
OnDatePreviewed?.Invoke(sender, e);
}
private void OnCellDateSelected(object sender, CalendarDayButtonEventArgs e)
{
OnDateSelected?.Invoke(sender, e);
}
private void SetDayButtons(DateTime date) private void SetDayButtons(DateTime date)
{ {
if (_grid is null) return; if (_grid is null) return;
@@ -117,18 +129,6 @@ public class CalendarMonthView : TemplatedControl
FadeOutDayButtons(); FadeOutDayButtons();
} }
private void OnDayButtonPressed(object sender, PointerPressedEventArgs e)
{
if (sender is CalendarDayButton { DataContext: DateTime d })
OnCalendarDayButtonPressed?.Invoke(this, new CalendarDayButtonEventArgs(d));
}
private void OnDayButtonPointerEnter(object sender, PointerEventArgs e)
{
if (sender is CalendarDayButton { DataContext: DateTime d })
OnCalendarDayButtonPointerEnter?.Invoke(this, new CalendarDayButtonEventArgs(d));
}
private int PreviousMonthDays(DateTime date) private int PreviousMonthDays(DateTime date)
{ {
var firstDay = date.GetFirstDayOfMonth(); var firstDay = date.GetFirstDayOfMonth();
@@ -151,8 +151,8 @@ public class CalendarMonthView : TemplatedControl
} }
public event EventHandler<CalendarDayButtonEventArgs>? OnCalendarDayButtonPressed; public event EventHandler<CalendarDayButtonEventArgs>? OnDateSelected;
public event EventHandler<CalendarDayButtonEventArgs>? OnCalendarDayButtonPointerEnter; public event EventHandler<CalendarDayButtonEventArgs>? OnDatePreviewed;
public void MarkSelection(DateTime? start, DateTime? end) public void MarkSelection(DateTime? start, DateTime? end)
{ {
@@ -164,12 +164,14 @@ public class CalendarMonthView : TemplatedControl
if (d == start) if (d == start)
{ {
button.IsStartDate = true; button.IsStartDate = true;
button.IsPreviewStartDate = false;
button.IsEndDate = false; button.IsEndDate = false;
button.IsInRange = false; button.IsInRange = false;
} }
else if (d == end) else if (d == end)
{ {
button.IsEndDate = true; button.IsEndDate = true;
button.IsPreviewEndDate = false;
button.IsStartDate = false; button.IsStartDate = false;
button.IsInRange = false; button.IsInRange = false;
} }

View File

@@ -0,0 +1,46 @@
using System.Diagnostics;
using Avalonia.Controls;
using Avalonia.Controls.Mixins;
namespace Ursa.Controls;
internal enum CalendarYearViewMode
{
Month,
Year,
// The button represents ten year, with one year before and one year after, 12 in total.
YearRange,
}
public class CalendarYearButton: ContentControl
{
static CalendarYearButton()
{
PressedMixin.Attach<CalendarYearButton>();
}
internal int Year { get; private set; }
internal int Month { get; private set; }
internal int StartYear { get; private set; }
internal int EndYear { get; private set; }
internal CalendarYearViewMode Mode { get; private set; }
internal void SetValues(CalendarYearViewMode mode, DateTime contextDate, int? month = null, int? year = null, int? startYear = null, int? endYear = null)
{
Debug.Assert(!(month is null && year is null && startYear is null && endYear is null));
Mode = mode;
Month = month ?? 0;
Year = year ?? 0;
StartYear = startYear ?? 0;
EndYear = endYear ?? 0;
Content = Mode switch
{
CalendarYearViewMode.Month => DateTimeHelper.GetCurrentDateTimeFormatInfo().AbbreviatedMonthNames[Month],
CalendarYearViewMode.Year => Year.ToString(),
CalendarYearViewMode.YearRange => StartYear + "-" + EndYear,
_ => Content
};
}
}

View File

@@ -0,0 +1,8 @@
using Avalonia.Interactivity;
namespace Ursa.Controls;
public class CalendarYearButtonEventArgs: RoutedEventArgs
{
}

View File

@@ -1,3 +1,8 @@
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
namespace Ursa.Controls; namespace Ursa.Controls;
/// <summary> /// <summary>
@@ -6,7 +11,82 @@ namespace Ursa.Controls;
/// 2. show 12 years, one year per button (but only 10 buttons clickable) /// 2. show 12 years, one year per button (but only 10 buttons clickable)
/// 3. show 120 years, ten year per button (but only 10 buttons clickable) /// 3. show 120 years, ten year per button (but only 10 buttons clickable)
/// </summary> /// </summary>
public class CalendarYearView [TemplatePart(PART_Grid, typeof(Grid))]
public class CalendarYearView: TemplatedControl
{ {
public const string PART_Grid = "PART_Grid";
private CalendarYearViewMode _mode = CalendarYearViewMode.Month;
internal CalendarYearViewMode Mode
{
get => _mode;
set
{
_mode = value;
RefreshButtons();
}
}
private DateTime _contextDate = DateTime.Today;
internal DateTime ContextDate
{
get => _contextDate;
set
{
_contextDate = value;
RefreshButtons();
}
}
private Grid? _grid;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_grid = e.NameScope.Find<Grid>(PART_Grid);
GenerateGridElements();
}
public void GenerateGridElements()
{
if (_grid is null)
{
return;
}
_grid.Children.Clear();
for (var i = 0; i < 12; i++)
{
var button = new CalendarYearButton();
Grid.SetRow(button, i / 3);
Grid.SetColumn(button, i % 3);
_grid.Children.Add(button);
}
}
private void RefreshButtons()
{
if (_grid is null) return;
var mode = Mode;
var contextDate = ContextDate;
for (var i = 0; i < 12; i++)
{
var child = _grid.Children[i] as CalendarYearButton;
if (child is null) continue;
switch (mode)
{
case CalendarYearViewMode.Month:
child.SetValues(CalendarYearViewMode.Month, contextDate, month: i);
break;
case CalendarYearViewMode.Year:
child.SetValues(CalendarYearViewMode.Year, contextDate, year: ContextDate.Year + i);
break;
case CalendarYearViewMode.YearRange:
var startYear = ContextDate.Year - 1;
var endYear = ContextDate.Year + 10;
child.SetValues(CalendarYearViewMode.YearRange, contextDate, startYear: startYear, endYear: endYear);
break;
}
}
}
} }