From 27375f722eebb7fd0a0f7dd5e438ab009cb49eba Mon Sep 17 00:00:00 2001 From: Dong Bin Date: Tue, 18 Feb 2025 18:02:37 +0800 Subject: [PATCH] fix: completely fix timepicker racing issue. --- .../Controls/DateTimePicker/DatePicker.cs | 16 --- .../Controls/DateTimePicker/DateTimePicker.cs | 18 +-- .../Controls/DateTimePicker/TimePicker.cs | 92 +++++++++---- .../DateTimePicker/TimePickerPresenter.cs | 129 ++++++++++-------- .../DateTimePicker/TimeRangePicker.cs | 11 +- .../TimePickerPresenterTests.cs | 40 ++++-- .../DateTimePicker/TimePickerTests.cs | 62 +++++++++ 7 files changed, 237 insertions(+), 131 deletions(-) create mode 100644 tests/HeadlessTest.Ursa/Controls/DateTimePicker/TimePickerTests.cs diff --git a/src/Ursa/Controls/DateTimePicker/DatePicker.cs b/src/Ursa/Controls/DateTimePicker/DatePicker.cs index 45318f1..03f673f 100644 --- a/src/Ursa/Controls/DateTimePicker/DatePicker.cs +++ b/src/Ursa/Controls/DateTimePicker/DatePicker.cs @@ -57,22 +57,6 @@ public class DatePicker: DatePickerBase, IClearControl SyncSelectedDateToText(args.NewValue.Value); } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - if (change.Property == IsDropdownOpenProperty) - { - if (change.GetNewValue() == false) - { - - } - else - { - - } - } - } - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); diff --git a/src/Ursa/Controls/DateTimePicker/DateTimePicker.cs b/src/Ursa/Controls/DateTimePicker/DateTimePicker.cs index 43fafbb..6bb9236 100644 --- a/src/Ursa/Controls/DateTimePicker/DateTimePicker.cs +++ b/src/Ursa/Controls/DateTimePicker/DateTimePicker.cs @@ -15,7 +15,7 @@ namespace Ursa.Controls; [TemplatePart(PART_Popup, typeof(Popup))] [TemplatePart(PART_TextBox, typeof(TextBox))] [TemplatePart(PART_Calendar, typeof(CalendarView))] -[TemplatePart(PART_TimePicker, typeof(TimePicker))] +[TemplatePart(PART_TimePicker, typeof(TimePickerPresenter))] public class DateTimePicker : DatePickerBase { public const string PART_Button = "PART_Button"; @@ -85,14 +85,14 @@ public class DateTimePicker : DatePickerBase { _textBox?.SetValue(TextBox.TextProperty, null); _calendar?.ClearSelection(); - _timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, null); + _timePickerPresenter?.SyncTime(null); } else { _textBox?.SetValue(TextBox.TextProperty, date.Value.ToString(DisplayFormat ?? CultureInfo.InvariantCulture.DateTimeFormat.FullDateTimePattern)); _calendar?.MarkDates(date.Value.Date, date.Value.Date); - _timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, date.Value.TimeOfDay); + _timePickerPresenter?.SyncTime(date.Value.TimeOfDay); } } @@ -175,7 +175,7 @@ public class DateTimePicker : DatePickerBase var date = SelectedDate ?? DateTime.Now; _calendar.ContextDate = new CalendarContext(date.Year, date.Month); _calendar.UpdateDayButtons(); - _timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, SelectedDate?.TimeOfDay); + _timePickerPresenter?.SyncTime(SelectedDate?.TimeOfDay); } SetCurrentValue(IsDropdownOpenProperty, true); @@ -193,7 +193,7 @@ public class DateTimePicker : DatePickerBase { SetCurrentValue(SelectedDateProperty, null); _calendar?.ClearSelection(); - _timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, null); + _timePickerPresenter?.SyncTime(null); } else if (DisplayFormat is null || DisplayFormat.Length == 0) { @@ -201,7 +201,7 @@ public class DateTimePicker : DatePickerBase { SetCurrentValue(SelectedDateProperty, defaultTime); _calendar?.MarkDates(defaultTime.Date, defaultTime.Date); - _timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, defaultTime.TimeOfDay); + _timePickerPresenter?.SyncTime(defaultTime.TimeOfDay); } } else @@ -217,14 +217,14 @@ public class DateTimePicker : DatePickerBase } _calendar?.MarkDates(date.Date, date.Date); - _timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, date.TimeOfDay); + _timePickerPresenter?.SyncTime(date.TimeOfDay); } else { SetCurrentValue(SelectedDateProperty, null); if (!fromText) _textBox?.SetValue(TextBox.TextProperty, null); _calendar?.ClearSelection(); - _timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, null); + _timePickerPresenter?.SyncTime(null); } } } @@ -236,7 +236,7 @@ public class DateTimePicker : DatePickerBase var date = SelectedDate ?? DateTime.Today; _calendar.ContextDate = _calendar.ContextDate.With(date.Year, date.Month); _calendar.UpdateDayButtons(); - _timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, date.TimeOfDay); + _timePickerPresenter?.SyncTime(date.TimeOfDay); } SetCurrentValue(IsDropdownOpenProperty, true); diff --git a/src/Ursa/Controls/DateTimePicker/TimePicker.cs b/src/Ursa/Controls/DateTimePicker/TimePicker.cs index 3eee8f7..f361533 100644 --- a/src/Ursa/Controls/DateTimePicker/TimePicker.cs +++ b/src/Ursa/Controls/DateTimePicker/TimePicker.cs @@ -29,24 +29,22 @@ public class TimePicker : TimePickerBase, IClearControl public static readonly StyledProperty WatermarkProperty = AvaloniaProperty.Register( nameof(Watermark)); - private bool _suppressTextPresenterEvent; - private Button? _button; - private TimePickerPresenter? _presenter; - private TextBox? _textBox; + private bool _isFocused; + private Popup? _popup; + private TimePickerPresenter? _presenter; + + private bool _suppressTextPresenterEvent; + private TextBox? _textBox; static TimePicker() { + FocusableProperty.OverrideDefaultValue(true); SelectedTimeProperty.Changed.AddClassHandler((picker, args) => picker.OnSelectionChanged(args)); - DisplayFormatProperty.Changed.AddClassHandler((picker, args) => picker.OnDisplayFormatChanged(args)); - } - - private void OnDisplayFormatChanged(AvaloniaPropertyChangedEventArgs _) - { - if (_textBox is null) return; - SyncTimeToText(SelectedTime); + DisplayFormatProperty.Changed.AddClassHandler((picker, args) => + picker.OnDisplayFormatChanged(args)); } public string? Watermark @@ -64,7 +62,13 @@ public class TimePicker : TimePickerBase, IClearControl public void Clear() { Focus(NavigationMethod.Pointer); - _presenter?.SetValue(TimePickerPresenter.TimeProperty, null); + _presenter?.SyncTime(null); + } + + private void OnDisplayFormatChanged(AvaloniaPropertyChangedEventArgs _) + { + if (_textBox is null) return; + SyncTimeToText(SelectedTime); } protected override void OnApplyTemplate(TemplateAppliedEventArgs e) @@ -73,46 +77,43 @@ public class TimePicker : TimePickerBase, IClearControl GotFocusEvent.RemoveHandler(OnTextBoxGetFocus, _textBox); TextBox.TextChangedEvent.RemoveHandler(OnTextChanged, _textBox); - PointerPressedEvent.RemoveHandler(OnTextBoxPointerPressed, _textBox); Button.ClickEvent.RemoveHandler(OnButtonClick, _button); TimePickerPresenter.SelectedTimeChangedEvent.RemoveHandler(OnPresenterTimeChanged, _presenter); _textBox = e.NameScope.Find(PART_TextBox); - e.NameScope.Find(PartNames.PART_Popup); + _popup = e.NameScope.Find(PartNames.PART_Popup); _presenter = e.NameScope.Find(PART_Presenter); _button = e.NameScope.Find