From d4fe9e6d91be011b61d2edfd2b80ec858432ff0c Mon Sep 17 00:00:00 2001 From: polarboy Date: Fri, 13 Sep 2024 12:41:24 +0800 Subject: [PATCH] Add selected state event to DatePicker Add selected state event to DatePicker --- .../DatePicker/CalendarView/Calendar.cs | 11 +- .../DatePicker/CalendarView/CalendarItem.cs | 46 +++++- src/AtomUI.Controls/DatePicker/DatePicker.cs | 27 +++- .../DatePicker/DatePickerFlyoutPresenter.cs | 15 +- .../DatePicker/DatePickerPresenter.cs | 150 +++++++++++++++++- .../DatePicker/DatePickerPresenterTheme.cs | 16 +- .../InfoPickerInput/InfoPickerInput.cs | 9 +- 7 files changed, 257 insertions(+), 17 deletions(-) diff --git a/src/AtomUI.Controls/DatePicker/CalendarView/Calendar.cs b/src/AtomUI.Controls/DatePicker/CalendarView/Calendar.cs index aef8d14..d2b810e 100644 --- a/src/AtomUI.Controls/DatePicker/CalendarView/Calendar.cs +++ b/src/AtomUI.Controls/DatePicker/CalendarView/Calendar.cs @@ -358,6 +358,15 @@ public class Calendar : TemplatedControl internal DateTime DisplayDateRangeEnd => DisplayDateEnd.GetValueOrDefault(DateTime.MaxValue); internal bool HasFocusInternal { get; set; } + internal static readonly StyledProperty IsPointerInMonthViewProperty = + AvaloniaProperty.Register(nameof(IsPointerInMonthView), false); + + internal bool IsPointerInMonthView + { + get => GetValue(IsPointerInMonthViewProperty); + set => SetValue(IsPointerInMonthViewProperty, value); + } + #endregion private bool _displayDateIsChanging; @@ -375,7 +384,7 @@ public class Calendar : TemplatedControl HorizontalAlignmentProperty.OverrideDefaultValue(HorizontalAlignment.Left); VerticalAlignmentProperty.OverrideDefaultValue(VerticalAlignment.Top); } - + public Calendar() { SetCurrentValue(DisplayDateProperty, DateTime.Today); diff --git a/src/AtomUI.Controls/DatePicker/CalendarView/CalendarItem.cs b/src/AtomUI.Controls/DatePicker/CalendarView/CalendarItem.cs index 4f92c0f..4f79cbc 100644 --- a/src/AtomUI.Controls/DatePicker/CalendarView/CalendarItem.cs +++ b/src/AtomUI.Controls/DatePicker/CalendarView/CalendarItem.cs @@ -10,6 +10,7 @@ using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Input; +using Avalonia.Input.Raw; using Avalonia.Interactivity; using Avalonia.Media; @@ -204,6 +205,9 @@ internal class CalendarItem : TemplatedControl protected IconButton? _previousButton; protected IconButton? _previousMonthButton; + // 当鼠标移动到日历单元格外面的时候还原 hover 临时的高亮 + private IDisposable? _pointerPositionDisposable; + internal Calendar? Owner { get; set; } /// @@ -1076,6 +1080,41 @@ internal class CalendarItem : TemplatedControl PseudoClasses.Set(CalendarDisabledPC, !isEnabled); } + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTree(e); + TokenResourceBinder.CreateGlobalTokenBinding(this, BorderThicknessProperty, GlobalTokenResourceKey.BorderThickness, BindingPriority.Template, + new RenderScaleAwareThicknessConfigure(this, thickness => new Thickness(0, 0, 0, thickness.Bottom))); + var inputManager = AvaloniaLocator.Current.GetService()!; + _pointerPositionDisposable = inputManager.Process.Subscribe(DetectPointerPosition); + } + + private void DetectPointerPosition(RawInputEventArgs args) + { + if (Owner is null) + { + return; + } + + if (args is RawPointerEventArgs pointerEventArgs) + { + if (!IsPointerInMonthView(pointerEventArgs.Position)) + { + Owner.IsPointerInMonthView = false; + } + else + { + Owner.IsPointerInMonthView = true; + } + } + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + _pointerPositionDisposable?.Dispose(); + } + protected virtual bool IsPointerInMonthView(Point position) { if (Owner is null) @@ -1099,11 +1138,4 @@ internal class CalendarItem : TemplatedControl return new Rect(firstDayPos, new Size(monthView.Bounds.Width, monthViewPos.Y + monthView.Bounds.Height - firstDayPos.Y)); } - - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTree(e); - TokenResourceBinder.CreateGlobalTokenBinding(this, BorderThicknessProperty, GlobalTokenResourceKey.BorderThickness, BindingPriority.Template, - new RenderScaleAwareThicknessConfigure(this, thickness => new Thickness(0, 0, 0, thickness.Bottom))); - } } \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/DatePicker.cs b/src/AtomUI.Controls/DatePicker/DatePicker.cs index e835955..0528e38 100644 --- a/src/AtomUI.Controls/DatePicker/DatePicker.cs +++ b/src/AtomUI.Controls/DatePicker/DatePicker.cs @@ -5,12 +5,37 @@ namespace AtomUI.Controls; public class DatePicker : InfoPickerInput { + + private DatePickerPresenter? _pickerPresenter; + protected override Flyout CreatePickerFlyout() { return new DatePickerFlyout(); } - protected override void NotifyPresenterCreated(Control presenter) + protected override void NotifyFlyoutPresenterCreated(Control flyoutPresenter) { + if (flyoutPresenter is DatePickerFlyoutPresenter datePickerFlyoutPresenter) + { + datePickerFlyoutPresenter.AttachedToVisualTree += (sender, args) => + { + _pickerPresenter = datePickerFlyoutPresenter.DatePickerPresenter; + ConfigurePickerPresenter(_pickerPresenter); + }; + } + } + + private void ConfigurePickerPresenter(DatePickerPresenter? presenter) + { + if (presenter is null) + { + return; + } + + presenter.ChoosingStatueChanged += (sender, args) => + { + _isChoosing = args.IsChoosing; + UpdatePseudoClasses(); + }; } } \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/DatePickerFlyoutPresenter.cs b/src/AtomUI.Controls/DatePicker/DatePickerFlyoutPresenter.cs index 37f6fc1..078cfff 100644 --- a/src/AtomUI.Controls/DatePicker/DatePickerFlyoutPresenter.cs +++ b/src/AtomUI.Controls/DatePicker/DatePickerFlyoutPresenter.cs @@ -1,6 +1,17 @@ -namespace AtomUI.Controls; +using Avalonia.Controls; +using Avalonia.Controls.Primitives; -public class DatePickerFlyoutPresenter : FlyoutPresenter +namespace AtomUI.Controls; + +internal class DatePickerFlyoutPresenter : FlyoutPresenter { protected override Type StyleKeyOverride => typeof(DatePickerFlyoutPresenter); + + internal DatePickerPresenter? DatePickerPresenter; + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + DatePickerPresenter = e.NameScope.Get(DatePickerFlyoutPresenterTheme.DatePickerPresenterPart); + } } \ No newline at end of file diff --git a/src/AtomUI.Controls/DatePicker/DatePickerPresenter.cs b/src/AtomUI.Controls/DatePicker/DatePickerPresenter.cs index 5342658..55bcff2 100644 --- a/src/AtomUI.Controls/DatePicker/DatePickerPresenter.cs +++ b/src/AtomUI.Controls/DatePicker/DatePickerPresenter.cs @@ -1,31 +1,177 @@ -using AtomUI.Theme.Data; +using AtomUI.Controls.CalendarView; +using AtomUI.Theme.Data; using AtomUI.Theme.Styling; using AtomUI.Utils; using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.Primitives; using Avalonia.Data; +using Avalonia.Layout; +using PickerCalendar = AtomUI.Controls.CalendarView.Calendar; namespace AtomUI.Controls; +public class ChoosingStatusEventArgs : EventArgs +{ + public bool IsChoosing { get; } + public ChoosingStatusEventArgs(bool isChoosing) + { + IsChoosing = isChoosing; + } +} + internal class DatePickerPresenter : PickerPresenterBase { #region 公共属性定义 public static readonly StyledProperty IsNeedConfirmProperty = AvaloniaProperty.Register(nameof(IsNeedConfirm)); - + + public static readonly StyledProperty IsShowNowProperty = + AvaloniaProperty.Register(nameof(IsShowNow)); + + public static readonly StyledProperty IsShowTimeProperty = + AvaloniaProperty.Register(nameof(IsShowTime)); + + public static readonly StyledProperty SelectedDateTimeProperty = + AvaloniaProperty.Register(nameof(SelectedDateTime)); + public bool IsNeedConfirm { get => GetValue(IsNeedConfirmProperty); set => SetValue(IsNeedConfirmProperty, value); } + + public bool IsShowNow + { + get => GetValue(IsShowNowProperty); + set => SetValue(IsShowNowProperty, value); + } + + public bool IsShowTime + { + get => GetValue(IsShowTimeProperty); + set => SetValue(IsShowTimeProperty, value); + } + + public DateTime? SelectedDateTime + { + get => GetValue(SelectedDateTimeProperty); + set => SetValue(SelectedDateTimeProperty, value); + } #endregion + #region 公共事件定义 + + /// + /// 当前 Pointer 选中的日期和时间的变化事件 + /// + public event EventHandler? HoverDateTimeChanged; + + /// + /// 当前是否处于选择中状态 + /// + public event EventHandler? ChoosingStatueChanged; + + #endregion + + private Button? _nowButton; + private Button? _todayButton; + private Button? _confirmButton; + private PickerCalendar? _calendarView; + private IDisposable? _choosingStateDisposable; + protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); TokenResourceBinder.CreateGlobalTokenBinding(this, BorderThicknessProperty, GlobalTokenResourceKey.BorderThickness, BindingPriority.Template, new RenderScaleAwareThicknessConfigure(this, thickness => new Thickness(0, thickness.Top, 0, 0))); + if (_calendarView is not null) + { + _choosingStateDisposable = PickerCalendar.IsPointerInMonthViewProperty.Changed.Subscribe(args => + { + ChoosingStatueChanged?.Invoke(this, new ChoosingStatusEventArgs(args.GetNewValue())); + }); + } + } + + protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTree(e); + _choosingStateDisposable = null; + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (change.Property == IsNeedConfirmProperty || + change.Property == IsShowNowProperty || + change.Property == IsShowTimeProperty) + { + SetupButtonStatus(); + } + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + _nowButton = e.NameScope.Get