From bbac227aae4141d2713ec30d20dd0de6d6776c8f Mon Sep 17 00:00:00 2001 From: rabbitism Date: Tue, 4 Jun 2024 01:44:39 +0800 Subject: [PATCH] feat: add year button implementation. --- demo/Ursa.Demo/Pages/DatePickerDemo.axaml | 24 ------ src/Ursa/Controls/DateTimePicker/Calendar.cs | 40 ++++----- .../DateTimePicker/CalendarDayButton.cs | 38 +++++++++ .../DateTimePicker/CalendarMonthView.cs | 34 ++++---- .../DateTimePicker/CalendarYearButton.cs | 46 +++++++++++ .../CalendarYearButtonEventArgs.cs | 8 ++ .../DateTimePicker/CalendarYearView.cs | 82 ++++++++++++++++++- 7 files changed, 212 insertions(+), 60 deletions(-) create mode 100644 src/Ursa/Controls/DateTimePicker/CalendarYearButton.cs create mode 100644 src/Ursa/Controls/DateTimePicker/CalendarYearButtonEventArgs.cs diff --git a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml index 44f1296..a946536 100644 --- a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml +++ b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml @@ -7,30 +7,6 @@ x:Class="Ursa.Demo.Pages.DatePickerDemo"> - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ursa/Controls/DateTimePicker/Calendar.cs b/src/Ursa/Controls/DateTimePicker/Calendar.cs index 11a4f02..02288ba 100644 --- a/src/Ursa/Controls/DateTimePicker/Calendar.cs +++ b/src/Ursa/Controls/DateTimePicker/Calendar.cs @@ -26,7 +26,8 @@ public class Calendar: TemplatedControl public const string PART_MonthView = "PART_MonthView"; public const string PART_YearView = "PART_YearView"; - private CalendarMonthView? _monthGrid; + private CalendarMonthView? _monthView; + private CalendarYearView? _yearView; private DatePickerState _state = DatePickerState.None; @@ -78,22 +79,23 @@ public class Calendar: TemplatedControl protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); - if (_monthGrid is not null) + if (_monthView is not null) { - _monthGrid.OnCalendarDayButtonPressed -= OnCalendarDayButtonPressed; - _monthGrid.OnCalendarDayButtonPointerEnter -= OnCalendarDayButtonPointerEnter; + _monthView.OnDateSelected -= OnDateSelected; + _monthView.OnDatePreviewed -= OnDatePreviewed; } - _monthGrid = e.NameScope.Find(PART_MonthView); - if(_monthGrid is not null) + _monthView = e.NameScope.Find(PART_MonthView); + _yearView = e.NameScope.Find(PART_YearView); + if(_monthView is not null) { - _monthGrid.OnCalendarDayButtonPressed += OnCalendarDayButtonPressed; - _monthGrid.OnCalendarDayButtonPointerEnter += OnCalendarDayButtonPointerEnter; + _monthView.OnDateSelected += OnDateSelected; + _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; } @@ -101,38 +103,38 @@ public class Calendar: TemplatedControl if (_state is DatePickerState.None) return; if (_state == DatePickerState.PreviewStart) { - _monthGrid.MarkPreview(date, EndDate); + _monthView.MarkPreview(date, EndDate); } 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; } var date = e.Date; if (_state == DatePickerState.None) { - _monthGrid.ClearSelection(); - _monthGrid.ClearPreview(); - _monthGrid.MarkSelection(date, null); + _monthView.ClearSelection(); + _monthView.ClearPreview(); + _monthView.MarkSelection(date, null); _state = DatePickerState.PreviewEnd; StartDate = date; } else if (_state == DatePickerState.PreviewStart) { - _monthGrid.MarkSelection(date, EndDate); + _monthView.MarkSelection(date, EndDate); _state = DatePickerState.SelectStart; StartDate = date; } else if (_state == DatePickerState.PreviewEnd) { - _monthGrid.MarkSelection(StartDate, date); + _monthView.MarkSelection(StartDate, date); _state = DatePickerState.None; EndDate = date; } diff --git a/src/Ursa/Controls/DateTimePicker/CalendarDayButton.cs b/src/Ursa/Controls/DateTimePicker/CalendarDayButton.cs index e2bf172..d15e7b0 100644 --- a/src/Ursa/Controls/DateTimePicker/CalendarDayButton.cs +++ b/src/Ursa/Controls/DateTimePicker/CalendarDayButton.cs @@ -4,8 +4,10 @@ using Avalonia.Controls.Metadata; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; using Avalonia.Input; +using Avalonia.Interactivity; using Irihi.Avalonia.Shared.Common; using Irihi.Avalonia.Shared.Helpers; +using Ursa.EventArgs; namespace Ursa.Controls; @@ -128,6 +130,24 @@ public class CalendarDayButton: ContentControl PseudoClasses.Set(PC_NotCurrentMonth, value); } } + + public static readonly RoutedEvent DateSelectedEvent = RoutedEvent.Register( + nameof(DateSelected), RoutingStrategies.Bubble); + + public event EventHandler DateSelected + { + add => AddHandler(DateSelectedEvent, value); + remove => RemoveHandler(DateSelectedEvent, value); + } + + public static readonly RoutedEvent DatePreviewedEvent = RoutedEvent.Register( + nameof(DatePreviewed), RoutingStrategies.Bubble); + + public event EventHandler DatePreviewed + { + add => AddHandler(DateSelectedEvent, value); + remove => RemoveHandler(DateSelectedEvent, value); + } static CalendarDayButton() { @@ -145,4 +165,22 @@ public class CalendarDayButton: ContentControl PseudoClasses.Set(PC_InRange, IsInRange); 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 }); + } + } } \ No newline at end of file diff --git a/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs b/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs index 1e55785..2742cdd 100644 --- a/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs +++ b/src/Ursa/Controls/DateTimePicker/CalendarMonthView.cs @@ -88,14 +88,26 @@ public class CalendarMonthView : TemplatedControl var cell = new CalendarDayButton(); cell.SetValue(Grid.RowProperty, i); cell.SetValue(Grid.ColumnProperty, j); - cell.PointerPressed += OnDayButtonPressed; - cell.PointerEntered += OnDayButtonPointerEnter; + cell.AddHandler(CalendarDayButton.DateSelectedEvent, OnCellDateSelected); + cell.AddHandler(CalendarDayButton.DatePreviewedEvent, OnCellDatePreviewed); children.Add(cell); } _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) { if (_grid is null) return; @@ -117,18 +129,6 @@ public class CalendarMonthView : TemplatedControl 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) { var firstDay = date.GetFirstDayOfMonth(); @@ -151,8 +151,8 @@ public class CalendarMonthView : TemplatedControl } - public event EventHandler? OnCalendarDayButtonPressed; - public event EventHandler? OnCalendarDayButtonPointerEnter; + public event EventHandler? OnDateSelected; + public event EventHandler? OnDatePreviewed; public void MarkSelection(DateTime? start, DateTime? end) { @@ -164,12 +164,14 @@ public class CalendarMonthView : TemplatedControl if (d == start) { button.IsStartDate = true; + button.IsPreviewStartDate = false; button.IsEndDate = false; button.IsInRange = false; } else if (d == end) { button.IsEndDate = true; + button.IsPreviewEndDate = false; button.IsStartDate = false; button.IsInRange = false; } diff --git a/src/Ursa/Controls/DateTimePicker/CalendarYearButton.cs b/src/Ursa/Controls/DateTimePicker/CalendarYearButton.cs new file mode 100644 index 0000000..f46450a --- /dev/null +++ b/src/Ursa/Controls/DateTimePicker/CalendarYearButton.cs @@ -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(); + } + + 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 + }; + } +} \ No newline at end of file diff --git a/src/Ursa/Controls/DateTimePicker/CalendarYearButtonEventArgs.cs b/src/Ursa/Controls/DateTimePicker/CalendarYearButtonEventArgs.cs new file mode 100644 index 0000000..17294e6 --- /dev/null +++ b/src/Ursa/Controls/DateTimePicker/CalendarYearButtonEventArgs.cs @@ -0,0 +1,8 @@ +using Avalonia.Interactivity; + +namespace Ursa.Controls; + +public class CalendarYearButtonEventArgs: RoutedEventArgs +{ + +} \ No newline at end of file diff --git a/src/Ursa/Controls/DateTimePicker/CalendarYearView.cs b/src/Ursa/Controls/DateTimePicker/CalendarYearView.cs index 4cdc0cc..4be0249 100644 --- a/src/Ursa/Controls/DateTimePicker/CalendarYearView.cs +++ b/src/Ursa/Controls/DateTimePicker/CalendarYearView.cs @@ -1,3 +1,8 @@ +using Avalonia.Controls; +using Avalonia.Controls.Documents; +using Avalonia.Controls.Metadata; +using Avalonia.Controls.Primitives; + namespace Ursa.Controls; /// @@ -6,7 +11,82 @@ namespace Ursa.Controls; /// 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) /// -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(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; + } + } + } } \ No newline at end of file