fix: completely fix timepicker racing issue.
This commit is contained in:
@@ -57,22 +57,6 @@ public class DatePicker: DatePickerBase, IClearControl
|
|||||||
SyncSelectedDateToText(args.NewValue.Value);
|
SyncSelectedDateToText(args.NewValue.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
|
||||||
{
|
|
||||||
base.OnPropertyChanged(change);
|
|
||||||
if (change.Property == IsDropdownOpenProperty)
|
|
||||||
{
|
|
||||||
if (change.GetNewValue<bool>() == false)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace Ursa.Controls;
|
|||||||
[TemplatePart(PART_Popup, typeof(Popup))]
|
[TemplatePart(PART_Popup, typeof(Popup))]
|
||||||
[TemplatePart(PART_TextBox, typeof(TextBox))]
|
[TemplatePart(PART_TextBox, typeof(TextBox))]
|
||||||
[TemplatePart(PART_Calendar, typeof(CalendarView))]
|
[TemplatePart(PART_Calendar, typeof(CalendarView))]
|
||||||
[TemplatePart(PART_TimePicker, typeof(TimePicker))]
|
[TemplatePart(PART_TimePicker, typeof(TimePickerPresenter))]
|
||||||
public class DateTimePicker : DatePickerBase
|
public class DateTimePicker : DatePickerBase
|
||||||
{
|
{
|
||||||
public const string PART_Button = "PART_Button";
|
public const string PART_Button = "PART_Button";
|
||||||
@@ -85,14 +85,14 @@ public class DateTimePicker : DatePickerBase
|
|||||||
{
|
{
|
||||||
_textBox?.SetValue(TextBox.TextProperty, null);
|
_textBox?.SetValue(TextBox.TextProperty, null);
|
||||||
_calendar?.ClearSelection();
|
_calendar?.ClearSelection();
|
||||||
_timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, null);
|
_timePickerPresenter?.SyncTime(null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_textBox?.SetValue(TextBox.TextProperty,
|
_textBox?.SetValue(TextBox.TextProperty,
|
||||||
date.Value.ToString(DisplayFormat ?? CultureInfo.InvariantCulture.DateTimeFormat.FullDateTimePattern));
|
date.Value.ToString(DisplayFormat ?? CultureInfo.InvariantCulture.DateTimeFormat.FullDateTimePattern));
|
||||||
_calendar?.MarkDates(date.Value.Date, date.Value.Date);
|
_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;
|
var date = SelectedDate ?? DateTime.Now;
|
||||||
_calendar.ContextDate = new CalendarContext(date.Year, date.Month);
|
_calendar.ContextDate = new CalendarContext(date.Year, date.Month);
|
||||||
_calendar.UpdateDayButtons();
|
_calendar.UpdateDayButtons();
|
||||||
_timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, SelectedDate?.TimeOfDay);
|
_timePickerPresenter?.SyncTime(SelectedDate?.TimeOfDay);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetCurrentValue(IsDropdownOpenProperty, true);
|
SetCurrentValue(IsDropdownOpenProperty, true);
|
||||||
@@ -193,7 +193,7 @@ public class DateTimePicker : DatePickerBase
|
|||||||
{
|
{
|
||||||
SetCurrentValue(SelectedDateProperty, null);
|
SetCurrentValue(SelectedDateProperty, null);
|
||||||
_calendar?.ClearSelection();
|
_calendar?.ClearSelection();
|
||||||
_timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, null);
|
_timePickerPresenter?.SyncTime(null);
|
||||||
}
|
}
|
||||||
else if (DisplayFormat is null || DisplayFormat.Length == 0)
|
else if (DisplayFormat is null || DisplayFormat.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -201,7 +201,7 @@ public class DateTimePicker : DatePickerBase
|
|||||||
{
|
{
|
||||||
SetCurrentValue(SelectedDateProperty, defaultTime);
|
SetCurrentValue(SelectedDateProperty, defaultTime);
|
||||||
_calendar?.MarkDates(defaultTime.Date, defaultTime.Date);
|
_calendar?.MarkDates(defaultTime.Date, defaultTime.Date);
|
||||||
_timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, defaultTime.TimeOfDay);
|
_timePickerPresenter?.SyncTime(defaultTime.TimeOfDay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -217,14 +217,14 @@ public class DateTimePicker : DatePickerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
_calendar?.MarkDates(date.Date, date.Date);
|
_calendar?.MarkDates(date.Date, date.Date);
|
||||||
_timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, date.TimeOfDay);
|
_timePickerPresenter?.SyncTime(date.TimeOfDay);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetCurrentValue(SelectedDateProperty, null);
|
SetCurrentValue(SelectedDateProperty, null);
|
||||||
if (!fromText) _textBox?.SetValue(TextBox.TextProperty, null);
|
if (!fromText) _textBox?.SetValue(TextBox.TextProperty, null);
|
||||||
_calendar?.ClearSelection();
|
_calendar?.ClearSelection();
|
||||||
_timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, null);
|
_timePickerPresenter?.SyncTime(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,7 +236,7 @@ public class DateTimePicker : DatePickerBase
|
|||||||
var date = SelectedDate ?? DateTime.Today;
|
var date = SelectedDate ?? DateTime.Today;
|
||||||
_calendar.ContextDate = _calendar.ContextDate.With(date.Year, date.Month);
|
_calendar.ContextDate = _calendar.ContextDate.With(date.Year, date.Month);
|
||||||
_calendar.UpdateDayButtons();
|
_calendar.UpdateDayButtons();
|
||||||
_timePickerPresenter?.SetValue(TimePickerPresenter.TimeProperty, date.TimeOfDay);
|
_timePickerPresenter?.SyncTime(date.TimeOfDay);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetCurrentValue(IsDropdownOpenProperty, true);
|
SetCurrentValue(IsDropdownOpenProperty, true);
|
||||||
|
|||||||
@@ -29,24 +29,22 @@ public class TimePicker : TimePickerBase, IClearControl
|
|||||||
public static readonly StyledProperty<string?> WatermarkProperty = AvaloniaProperty.Register<TimePicker, string?>(
|
public static readonly StyledProperty<string?> WatermarkProperty = AvaloniaProperty.Register<TimePicker, string?>(
|
||||||
nameof(Watermark));
|
nameof(Watermark));
|
||||||
|
|
||||||
private bool _suppressTextPresenterEvent;
|
|
||||||
|
|
||||||
private Button? _button;
|
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()
|
static TimePicker()
|
||||||
{
|
{
|
||||||
|
FocusableProperty.OverrideDefaultValue<TimePicker>(true);
|
||||||
SelectedTimeProperty.Changed.AddClassHandler<TimePicker, TimeSpan?>((picker, args) =>
|
SelectedTimeProperty.Changed.AddClassHandler<TimePicker, TimeSpan?>((picker, args) =>
|
||||||
picker.OnSelectionChanged(args));
|
picker.OnSelectionChanged(args));
|
||||||
DisplayFormatProperty.Changed.AddClassHandler<TimePicker, string?>((picker, args) => picker.OnDisplayFormatChanged(args));
|
DisplayFormatProperty.Changed.AddClassHandler<TimePicker, string?>((picker, args) =>
|
||||||
}
|
picker.OnDisplayFormatChanged(args));
|
||||||
|
|
||||||
private void OnDisplayFormatChanged(AvaloniaPropertyChangedEventArgs<string?> _)
|
|
||||||
{
|
|
||||||
if (_textBox is null) return;
|
|
||||||
SyncTimeToText(SelectedTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string? Watermark
|
public string? Watermark
|
||||||
@@ -64,7 +62,13 @@ public class TimePicker : TimePickerBase, IClearControl
|
|||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
Focus(NavigationMethod.Pointer);
|
Focus(NavigationMethod.Pointer);
|
||||||
_presenter?.SetValue(TimePickerPresenter.TimeProperty, null);
|
_presenter?.SyncTime(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisplayFormatChanged(AvaloniaPropertyChangedEventArgs<string?> _)
|
||||||
|
{
|
||||||
|
if (_textBox is null) return;
|
||||||
|
SyncTimeToText(SelectedTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
@@ -73,46 +77,43 @@ public class TimePicker : TimePickerBase, IClearControl
|
|||||||
|
|
||||||
GotFocusEvent.RemoveHandler(OnTextBoxGetFocus, _textBox);
|
GotFocusEvent.RemoveHandler(OnTextBoxGetFocus, _textBox);
|
||||||
TextBox.TextChangedEvent.RemoveHandler(OnTextChanged, _textBox);
|
TextBox.TextChangedEvent.RemoveHandler(OnTextChanged, _textBox);
|
||||||
PointerPressedEvent.RemoveHandler(OnTextBoxPointerPressed, _textBox);
|
|
||||||
Button.ClickEvent.RemoveHandler(OnButtonClick, _button);
|
Button.ClickEvent.RemoveHandler(OnButtonClick, _button);
|
||||||
TimePickerPresenter.SelectedTimeChangedEvent.RemoveHandler(OnPresenterTimeChanged, _presenter);
|
TimePickerPresenter.SelectedTimeChangedEvent.RemoveHandler(OnPresenterTimeChanged, _presenter);
|
||||||
|
|
||||||
_textBox = e.NameScope.Find<TextBox>(PART_TextBox);
|
_textBox = e.NameScope.Find<TextBox>(PART_TextBox);
|
||||||
e.NameScope.Find<Popup>(PartNames.PART_Popup);
|
_popup = e.NameScope.Find<Popup>(PartNames.PART_Popup);
|
||||||
_presenter = e.NameScope.Find<TimePickerPresenter>(PART_Presenter);
|
_presenter = e.NameScope.Find<TimePickerPresenter>(PART_Presenter);
|
||||||
_button = e.NameScope.Find<Button>(PART_Button);
|
_button = e.NameScope.Find<Button>(PART_Button);
|
||||||
|
|
||||||
GotFocusEvent.AddHandler(OnTextBoxGetFocus, _textBox);
|
GotFocusEvent.AddHandler(OnTextBoxGetFocus, _textBox);
|
||||||
TextBox.TextChangedEvent.AddHandler(OnTextChanged, _textBox);
|
TextBox.TextChangedEvent.AddHandler(OnTextChanged, _textBox);
|
||||||
PointerPressedEvent.AddHandler(OnTextBoxPointerPressed, RoutingStrategies.Tunnel, false, _textBox);
|
|
||||||
Button.ClickEvent.AddHandler(OnButtonClick, _button);
|
Button.ClickEvent.AddHandler(OnButtonClick, _button);
|
||||||
TimePickerPresenter.SelectedTimeChangedEvent.AddHandler(OnPresenterTimeChanged, _presenter);
|
TimePickerPresenter.SelectedTimeChangedEvent.AddHandler(OnPresenterTimeChanged, _presenter);
|
||||||
|
|
||||||
// SetCurrentValue(SelectedTimeProperty, DateTime.Now.TimeOfDay);
|
// SetCurrentValue(SelectedTimeProperty, DateTime.Now.TimeOfDay);
|
||||||
_presenter?.SetValue(TimePickerPresenter.TimeProperty, SelectedTime);
|
// _presenter?.SetValue(TimePickerPresenter.TimeProperty, SelectedTime);
|
||||||
|
_presenter?.SyncTime(SelectedTime);
|
||||||
SyncTimeToText(SelectedTime);
|
SyncTimeToText(SelectedTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPresenterTimeChanged(object? sender, TimeChangedEventArgs e)
|
private void OnPresenterTimeChanged(object? sender, TimeChangedEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (!IsInitialized) return;
|
||||||
if (_suppressTextPresenterEvent) return;
|
if (_suppressTextPresenterEvent) return;
|
||||||
SetCurrentValue(SelectedTimeProperty, e.NewTime);
|
SetCurrentValue(SelectedTimeProperty, e.NewTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnButtonClick(object? sender, RoutedEventArgs e)
|
private void OnButtonClick(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
Focus(NavigationMethod.Pointer);
|
if(IsFocused)
|
||||||
SetCurrentValue(IsDropdownOpenProperty, !IsDropdownOpen);
|
{
|
||||||
}
|
SetCurrentValue(IsDropdownOpenProperty, !IsDropdownOpen);
|
||||||
|
}
|
||||||
private void OnTextBoxPointerPressed(object? sender, PointerPressedEventArgs e)
|
|
||||||
{
|
|
||||||
SetCurrentValue(IsDropdownOpenProperty, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTextBoxGetFocus(object? sender, GotFocusEventArgs e)
|
private void OnTextBoxGetFocus(object? sender, GotFocusEventArgs e)
|
||||||
{
|
{
|
||||||
// SetCurrentValue(IsDropdownOpenProperty, true);
|
SetCurrentValue(IsDropdownOpenProperty, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnKeyDown(KeyEventArgs e)
|
protected override void OnKeyDown(KeyEventArgs e)
|
||||||
@@ -145,17 +146,22 @@ public class TimePicker : TimePickerBase, IClearControl
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(_textBox?.Text))
|
if (string.IsNullOrEmpty(_textBox?.Text))
|
||||||
{
|
{
|
||||||
TimePickerPresenter.TimeProperty.SetValue(null, _presenter);
|
_presenter?.SyncTime(null);
|
||||||
}
|
}
|
||||||
else if (DisplayFormat is null || DisplayFormat.Length == 0)
|
else if (DisplayFormat is null || DisplayFormat.Length == 0)
|
||||||
{
|
{
|
||||||
if (TimeSpan.TryParse(_textBox?.Text, out var defaultTime))
|
if (TimeSpan.TryParse(_textBox?.Text, out var defaultTime))
|
||||||
TimePickerPresenter.TimeProperty.SetValue(defaultTime, _presenter);
|
{
|
||||||
|
_presenter?.SyncTime(defaultTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (DateTime.TryParseExact(_textBox?.Text, DisplayFormat, CultureInfo.CurrentUICulture, DateTimeStyles.None,
|
if (DateTime.TryParseExact(_textBox?.Text, DisplayFormat, CultureInfo.CurrentUICulture, DateTimeStyles.None,
|
||||||
out var time)) TimePickerPresenter.TimeProperty.SetValue(time.TimeOfDay, _presenter);
|
out var time))
|
||||||
|
{
|
||||||
|
_presenter?.SyncTime(time.TimeOfDay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +169,7 @@ public class TimePicker : TimePickerBase, IClearControl
|
|||||||
{
|
{
|
||||||
if (_textBox is null) return;
|
if (_textBox is null) return;
|
||||||
_suppressTextPresenterEvent = true;
|
_suppressTextPresenterEvent = true;
|
||||||
_presenter?.SetValue(TimePickerPresenter.TimeProperty, args.NewValue.Value);
|
_presenter?.SyncTime(args.NewValue.Value);
|
||||||
SyncTimeToText(args.NewValue.Value);
|
SyncTimeToText(args.NewValue.Value);
|
||||||
_suppressTextPresenterEvent = false;
|
_suppressTextPresenterEvent = false;
|
||||||
}
|
}
|
||||||
@@ -176,6 +182,7 @@ public class TimePicker : TimePickerBase, IClearControl
|
|||||||
_textBox.Text = null;
|
_textBox.Text = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var date = new DateTime(1, 1, 1, time.Value.Hours, time.Value.Minutes, time.Value.Seconds);
|
var date = new DateTime(1, 1, 1, time.Value.Hours, time.Value.Minutes, time.Value.Seconds);
|
||||||
var text = date.ToString(DisplayFormat);
|
var text = date.ToString(DisplayFormat);
|
||||||
_textBox.Text = text;
|
_textBox.Text = text;
|
||||||
@@ -199,4 +206,33 @@ public class TimePicker : TimePickerBase, IClearControl
|
|||||||
base.UpdateDataValidation(property, state, error);
|
base.UpdateDataValidation(property, state, error);
|
||||||
if (property == SelectedTimeProperty) DataValidationErrors.SetError(this, error);
|
if (property == SelectedTimeProperty) DataValidationErrors.SetError(this, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnGotFocus(GotFocusEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnGotFocus(e);
|
||||||
|
FocusChanged(IsKeyboardFocusWithin);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLostFocus(RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnLostFocus(e);
|
||||||
|
FocusChanged(IsKeyboardFocusWithin);
|
||||||
|
var top = TopLevel.GetTopLevel(this);
|
||||||
|
var element = top?.FocusManager?.GetFocusedElement();
|
||||||
|
if (element is Visual v && _popup?.IsInsidePopup(v) == true) return;
|
||||||
|
SetCurrentValue(IsDropdownOpenProperty, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FocusChanged(bool hasFocus)
|
||||||
|
{
|
||||||
|
var wasFocused = _isFocused;
|
||||||
|
_isFocused = hasFocus;
|
||||||
|
|
||||||
|
if (hasFocus)
|
||||||
|
if (!wasFocused && _textBox != null)
|
||||||
|
{
|
||||||
|
_textBox.Focus();
|
||||||
|
_textBox.SelectAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.Metadata;
|
using Avalonia.Controls.Metadata;
|
||||||
using Avalonia.Controls.Primitives;
|
using Avalonia.Controls.Primitives;
|
||||||
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Irihi.Avalonia.Shared.Helpers;
|
using Irihi.Avalonia.Shared.Helpers;
|
||||||
|
|
||||||
@@ -49,14 +50,14 @@ public class TimePickerPresenter : TemplatedControl
|
|||||||
AvaloniaProperty.Register<TimePickerPresenter, int>(
|
AvaloniaProperty.Register<TimePickerPresenter, int>(
|
||||||
nameof(SecondIncrement), 1);
|
nameof(SecondIncrement), 1);
|
||||||
|
|
||||||
public static readonly StyledProperty<TimeSpan?> TimeProperty =
|
|
||||||
AvaloniaProperty.Register<TimePickerPresenter, TimeSpan?>(
|
|
||||||
nameof(Time));
|
|
||||||
|
|
||||||
public static readonly StyledProperty<string> PanelFormatProperty =
|
public static readonly StyledProperty<string> PanelFormatProperty =
|
||||||
AvaloniaProperty.Register<TimePickerPresenter, string>(
|
AvaloniaProperty.Register<TimePickerPresenter, string>(
|
||||||
nameof(PanelFormat), "HH mm ss t");
|
nameof(PanelFormat), "HH mm ss t");
|
||||||
|
|
||||||
|
public static readonly RoutedEvent<TimeChangedEventArgs> SelectedTimeChangedEvent =
|
||||||
|
RoutedEvent.Register<TimePickerPresenter, TimeChangedEventArgs>(
|
||||||
|
nameof(SelectedTimeChanged), RoutingStrategies.Bubble);
|
||||||
|
|
||||||
private Control? _ampmScrollPanel;
|
private Control? _ampmScrollPanel;
|
||||||
private DateTimePickerPanel? _ampmSelector;
|
private DateTimePickerPanel? _ampmSelector;
|
||||||
private Control? _firstSeparator;
|
private Control? _firstSeparator;
|
||||||
@@ -65,26 +66,21 @@ public class TimePickerPresenter : TemplatedControl
|
|||||||
private DateTimePickerPanel? _hourSelector;
|
private DateTimePickerPanel? _hourSelector;
|
||||||
private Control? _minuteScrollPanel;
|
private Control? _minuteScrollPanel;
|
||||||
private DateTimePickerPanel? _minuteSelector;
|
private DateTimePickerPanel? _minuteSelector;
|
||||||
|
|
||||||
private Grid? _pickerContainer;
|
private Grid? _pickerContainer;
|
||||||
private Control? _secondScrollPanel;
|
private Control? _secondScrollPanel;
|
||||||
private DateTimePickerPanel? _secondSelector;
|
private DateTimePickerPanel? _secondSelector;
|
||||||
private Control? _secondSeparator;
|
private Control? _secondSeparator;
|
||||||
|
|
||||||
|
private bool _surpressTimeEvent = true;
|
||||||
private Control? _thirdSeparator;
|
private Control? _thirdSeparator;
|
||||||
internal TimeSpan TimeHolder;
|
|
||||||
private bool _updateFromTimeChange;
|
|
||||||
private bool _use12Clock;
|
private bool _use12Clock;
|
||||||
|
internal TimeSpan? TimeHolder;
|
||||||
|
|
||||||
static TimePickerPresenter()
|
static TimePickerPresenter()
|
||||||
{
|
{
|
||||||
PanelFormatProperty.Changed.AddClassHandler<TimePickerPresenter, string>((presenter, args) =>
|
PanelFormatProperty.Changed.AddClassHandler<TimePickerPresenter, string>((presenter, args) =>
|
||||||
presenter.OnPanelFormatChanged(args));
|
presenter.OnPanelFormatChanged(args));
|
||||||
TimeProperty.Changed.AddClassHandler<TimePickerPresenter, TimeSpan?>((presenter, args) =>
|
|
||||||
presenter.OnTimeChanged(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
public TimePickerPresenter()
|
|
||||||
{
|
|
||||||
// SetCurrentValue(TimeProperty, DateTime.Now.TimeOfDay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool NeedsConfirmation
|
public bool NeedsConfirmation
|
||||||
@@ -105,40 +101,18 @@ public class TimePickerPresenter : TemplatedControl
|
|||||||
set => SetValue(SecondIncrementProperty, value);
|
set => SetValue(SecondIncrementProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimeSpan? Time
|
|
||||||
{
|
|
||||||
get => GetValue(TimeProperty);
|
|
||||||
set => SetValue(TimeProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string PanelFormat
|
public string PanelFormat
|
||||||
{
|
{
|
||||||
get => GetValue(PanelFormatProperty);
|
get => GetValue(PanelFormatProperty);
|
||||||
set => SetValue(PanelFormatProperty, value);
|
set => SetValue(PanelFormatProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly RoutedEvent<TimeChangedEventArgs> SelectedTimeChangedEvent =
|
|
||||||
RoutedEvent.Register<TimePickerPresenter, TimeChangedEventArgs>(
|
|
||||||
nameof(SelectedTimeChanged), RoutingStrategies.Bubble);
|
|
||||||
|
|
||||||
public event EventHandler<TimeChangedEventArgs> SelectedTimeChanged
|
public event EventHandler<TimeChangedEventArgs> SelectedTimeChanged
|
||||||
{
|
{
|
||||||
add => AddHandler(SelectedTimeChangedEvent, value);
|
add => AddHandler(SelectedTimeChangedEvent, value);
|
||||||
remove => RemoveHandler(SelectedTimeChangedEvent, value);
|
remove => RemoveHandler(SelectedTimeChangedEvent, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTimeChanged(AvaloniaPropertyChangedEventArgs<TimeSpan?> args)
|
|
||||||
{
|
|
||||||
_updateFromTimeChange = true;
|
|
||||||
UpdatePanelsFromSelectedTime(args.NewValue.Value);
|
|
||||||
_updateFromTimeChange = false;
|
|
||||||
if (args.OldValue.Value != args.NewValue.Value)
|
|
||||||
{
|
|
||||||
RaiseEvent(new TimeChangedEventArgs(args.OldValue.Value, args.NewValue.Value)
|
|
||||||
{ RoutedEvent = SelectedTimeChangedEvent, Source = this });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPanelFormatChanged(AvaloniaPropertyChangedEventArgs<string> args)
|
private void OnPanelFormatChanged(AvaloniaPropertyChangedEventArgs<string> args)
|
||||||
{
|
{
|
||||||
var format = args.NewValue.Value;
|
var format = args.NewValue.Value;
|
||||||
@@ -212,47 +186,63 @@ public class TimePickerPresenter : TemplatedControl
|
|||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
if (_hourSelector is not null) _hourSelector.SelectionChanged -= OnPanelSelectionChanged;
|
|
||||||
if (_minuteSelector is not null) _minuteSelector.SelectionChanged -= OnPanelSelectionChanged;
|
|
||||||
if (_secondSelector is not null) _secondSelector.SelectionChanged -= OnPanelSelectionChanged;
|
|
||||||
if (_ampmSelector is not null) _ampmSelector.SelectionChanged -= OnPanelSelectionChanged;
|
|
||||||
_hourSelector = e.NameScope.Find<DateTimePickerPanel>(PART_HourSelector);
|
_hourSelector = e.NameScope.Find<DateTimePickerPanel>(PART_HourSelector);
|
||||||
_minuteSelector = e.NameScope.Find<DateTimePickerPanel>(PART_MinuteSelector);
|
_minuteSelector = e.NameScope.Find<DateTimePickerPanel>(PART_MinuteSelector);
|
||||||
_secondSelector = e.NameScope.Find<DateTimePickerPanel>(PART_SecondSelector);
|
_secondSelector = e.NameScope.Find<DateTimePickerPanel>(PART_SecondSelector);
|
||||||
_ampmSelector = e.NameScope.Find<DateTimePickerPanel>(PART_AmPmSelector);
|
_ampmSelector = e.NameScope.Find<DateTimePickerPanel>(PART_AmPmSelector);
|
||||||
if (_hourSelector is not null) _hourSelector.SelectionChanged += OnPanelSelectionChanged;
|
|
||||||
if (_minuteSelector is not null) _minuteSelector.SelectionChanged += OnPanelSelectionChanged;
|
|
||||||
if (_secondSelector is not null) _secondSelector.SelectionChanged += OnPanelSelectionChanged;
|
|
||||||
if (_ampmSelector is not null) _ampmSelector.SelectionChanged += OnPanelSelectionChanged;
|
|
||||||
_pickerContainer = e.NameScope.Find<Grid>(PART_PickerContainer);
|
_pickerContainer = e.NameScope.Find<Grid>(PART_PickerContainer);
|
||||||
_hourScrollPanel = e.NameScope.Find<Control>(PART_HourScrollPanel);
|
_hourScrollPanel = e.NameScope.Find<Control>(PART_HourScrollPanel);
|
||||||
_minuteScrollPanel = e.NameScope.Find<Control>(PART_MinuteScrollPanel);
|
_minuteScrollPanel = e.NameScope.Find<Control>(PART_MinuteScrollPanel);
|
||||||
_secondScrollPanel = e.NameScope.Find<Control>(PART_SecondScrollPanel);
|
_secondScrollPanel = e.NameScope.Find<Control>(PART_SecondScrollPanel);
|
||||||
_ampmScrollPanel = e.NameScope.Find<Control>(PART_AmPmScrollPanel);
|
_ampmScrollPanel = e.NameScope.Find<Control>(PART_AmPmScrollPanel);
|
||||||
|
|
||||||
_firstSeparator = e.NameScope.Find<Control>(PART_FirstSeparator);
|
_firstSeparator = e.NameScope.Find<Control>(PART_FirstSeparator);
|
||||||
_secondSeparator = e.NameScope.Find<Control>(PART_SecondSeparator);
|
_secondSeparator = e.NameScope.Find<Control>(PART_SecondSeparator);
|
||||||
_thirdSeparator = e.NameScope.Find<Control>(PART_ThirdSeparator);
|
_thirdSeparator = e.NameScope.Find<Control>(PART_ThirdSeparator);
|
||||||
Initialize();
|
Initialize();
|
||||||
UpdatePanelLayout(PanelFormat);
|
UpdatePanelLayout(PanelFormat);
|
||||||
UpdatePanelsFromSelectedTime(Time);
|
_surpressTimeEvent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLoaded(RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnLoaded(e);
|
||||||
|
UpdatePanelsFromSelectedTime(TimeHolder);
|
||||||
|
if (_hourSelector is not null) _hourSelector.SelectionChanged += OnPanelSelectionChanged;
|
||||||
|
if (_minuteSelector is not null) _minuteSelector.SelectionChanged += OnPanelSelectionChanged;
|
||||||
|
if (_secondSelector is not null) _secondSelector.SelectionChanged += OnPanelSelectionChanged;
|
||||||
|
if (_ampmSelector is not null) _ampmSelector.SelectionChanged += OnPanelSelectionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnUnloaded(RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnUnloaded(e);
|
||||||
|
if (_hourSelector is not null) _hourSelector.SelectionChanged -= OnPanelSelectionChanged;
|
||||||
|
if (_minuteSelector is not null) _minuteSelector.SelectionChanged -= OnPanelSelectionChanged;
|
||||||
|
if (_secondSelector is not null) _secondSelector.SelectionChanged -= OnPanelSelectionChanged;
|
||||||
|
if (_ampmSelector is not null) _ampmSelector.SelectionChanged -= OnPanelSelectionChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPanelSelectionChanged(object? sender, System.EventArgs e)
|
private void OnPanelSelectionChanged(object? sender, System.EventArgs e)
|
||||||
{
|
{
|
||||||
if (_updateFromTimeChange) return;
|
if (_surpressTimeEvent) return;
|
||||||
if (!_use12Clock && Equals(sender, _ampmSelector)) return;
|
if (!_use12Clock && Equals(sender, _ampmSelector)) return;
|
||||||
var time = NeedsConfirmation ? TimeHolder : Time ?? DateTime.Now.TimeOfDay;
|
var time = TimeHolder ?? DateTime.Now.TimeOfDay;
|
||||||
var hour = _hourSelector?.SelectedValue ?? time.Hours;
|
var hour = _hourSelector?.SelectedValue ?? time.Hours;
|
||||||
var minute = _minuteSelector?.SelectedValue ?? time.Minutes;
|
var minute = _minuteSelector?.SelectedValue ?? time.Minutes;
|
||||||
var second = _secondSelector?.SelectedValue ?? time.Seconds;
|
var second = _secondSelector?.SelectedValue ?? time.Seconds;
|
||||||
var ampm = _ampmSelector?.SelectedValue ?? (time.Hours >= 12 ? 1 : 0);
|
var ampm = _ampmSelector?.SelectedValue ?? (time.Hours >= 12 ? 1 : 0);
|
||||||
if (_use12Clock)
|
if (_use12Clock)
|
||||||
|
{
|
||||||
hour = ampm switch
|
hour = ampm switch
|
||||||
{
|
{
|
||||||
0 when hour == 12 => 0,
|
0 when hour == 12 => 0,
|
||||||
1 when hour < 12 => hour + 12,
|
1 when hour < 12 => hour + 12,
|
||||||
_ => hour
|
_ => hour
|
||||||
};
|
};
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ampm = hour switch
|
ampm = hour switch
|
||||||
@@ -262,11 +252,23 @@ public class TimePickerPresenter : TemplatedControl
|
|||||||
};
|
};
|
||||||
SetIfChanged(_ampmSelector, ampm);
|
SetIfChanged(_ampmSelector, ampm);
|
||||||
}
|
}
|
||||||
|
|
||||||
var newTime = new TimeSpan(hour, minute, second);
|
var newTime = new TimeSpan(hour, minute, second);
|
||||||
if (NeedsConfirmation)
|
if (NeedsConfirmation)
|
||||||
|
{
|
||||||
TimeHolder = newTime;
|
TimeHolder = newTime;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
SetCurrentValue(TimeProperty, newTime);
|
{
|
||||||
|
if (_surpressTimeEvent) return;
|
||||||
|
RaiseEvent(new TimeChangedEventArgs(null, newTime) { RoutedEvent = SelectedTimeChangedEvent });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnPointerPressed(PointerPressedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnPointerPressed(e);
|
||||||
|
OnPanelSelectionChanged(null, System.EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdatePanelsFromSelectedTime(TimeSpan? time)
|
private void UpdatePanelsFromSelectedTime(TimeSpan? time)
|
||||||
@@ -276,21 +278,19 @@ public class TimePickerPresenter : TemplatedControl
|
|||||||
{
|
{
|
||||||
var index = _use12Clock ? time.Value.Hours % 12 : time.Value.Hours;
|
var index = _use12Clock ? time.Value.Hours % 12 : time.Value.Hours;
|
||||||
if (_use12Clock && index == 0) index = 12;
|
if (_use12Clock && index == 0) index = 12;
|
||||||
SetIfChanged(_hourSelector, index);
|
SetIfChanged(_hourSelector, index, true);
|
||||||
}
|
}
|
||||||
SetIfChanged(_minuteSelector, time.Value.Minutes);
|
|
||||||
SetIfChanged(_secondSelector, time.Value.Seconds);
|
SetIfChanged(_minuteSelector, time.Value.Minutes, true);
|
||||||
|
SetIfChanged(_secondSelector, time.Value.Seconds, true);
|
||||||
var ampm = time.Value.Hours switch
|
var ampm = time.Value.Hours switch
|
||||||
{
|
{
|
||||||
>= 12 => 1,
|
>= 12 => 1,
|
||||||
_ => 0
|
_ => 0
|
||||||
};
|
};
|
||||||
|
|
||||||
SetIfChanged(_ampmSelector, ampm);
|
SetIfChanged(_ampmSelector, ampm, true);
|
||||||
if (_ampmSelector is not null)
|
if (_ampmSelector is not null) _ampmSelector.IsEnabled = _use12Clock;
|
||||||
{
|
|
||||||
_ampmSelector.IsEnabled = _use12Clock;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Initialize()
|
private void Initialize()
|
||||||
@@ -327,12 +327,23 @@ public class TimePickerPresenter : TemplatedControl
|
|||||||
|
|
||||||
public void Confirm()
|
public void Confirm()
|
||||||
{
|
{
|
||||||
if (NeedsConfirmation) SetCurrentValue(TimeProperty, TimeHolder);
|
if (NeedsConfirmation)
|
||||||
|
RaiseEvent(new TimeChangedEventArgs(null, TimeHolder) { RoutedEvent = SelectedTimeChangedEvent });
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetIfChanged(DateTimePickerPanel? panel, int index)
|
private void SetIfChanged(DateTimePickerPanel? panel, int index, bool surpress = false)
|
||||||
{
|
{
|
||||||
if (panel is null) return;
|
if (panel is null) return;
|
||||||
|
panel.SelectionChanged -= OnPanelSelectionChanged;
|
||||||
if (panel.SelectedValue != index) panel.SelectedValue = index;
|
if (panel.SelectedValue != index) panel.SelectedValue = index;
|
||||||
|
panel.SelectionChanged += OnPanelSelectionChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SyncTime(TimeSpan? time)
|
||||||
|
{
|
||||||
|
_surpressTimeEvent = true;
|
||||||
|
TimeHolder = time;
|
||||||
|
UpdatePanelsFromSelectedTime(time);
|
||||||
|
_surpressTimeEvent = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,15 +100,16 @@ public class TimeRangePicker : TimePickerBase, IClearControl
|
|||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
Focus(NavigationMethod.Pointer);
|
Focus(NavigationMethod.Pointer);
|
||||||
_startPresenter?.SetValue(TimePickerPresenter.TimeProperty, null);
|
_startPresenter?.SyncTime(null);
|
||||||
_endPresenter?.SetValue(TimePickerPresenter.TimeProperty, null);
|
_endPresenter?.SyncTime(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSelectionChanged(AvaloniaPropertyChangedEventArgs<TimeSpan?> args, bool start = true)
|
private void OnSelectionChanged(AvaloniaPropertyChangedEventArgs<TimeSpan?> args, bool start = true)
|
||||||
{
|
{
|
||||||
SyncTimeToText(args.NewValue.Value, start);
|
SyncTimeToText(args.NewValue.Value, start);
|
||||||
_suppressTextPresenterEvent = true;
|
_suppressTextPresenterEvent = true;
|
||||||
TimePickerPresenter.TimeProperty.SetValue(args.NewValue.Value, start ? _startPresenter : _endPresenter);
|
var presenter = start ? _startPresenter : _endPresenter;
|
||||||
|
presenter?.SyncTime(args.NewValue.Value);
|
||||||
_suppressTextPresenterEvent = false;
|
_suppressTextPresenterEvent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,8 +151,8 @@ public class TimeRangePicker : TimePickerBase, IClearControl
|
|||||||
Button.ClickEvent.AddHandler(OnButtonClick, _button);
|
Button.ClickEvent.AddHandler(OnButtonClick, _button);
|
||||||
TimePickerPresenter.SelectedTimeChangedEvent.AddHandler(OnPresenterTimeChanged, _startPresenter, _endPresenter);
|
TimePickerPresenter.SelectedTimeChangedEvent.AddHandler(OnPresenterTimeChanged, _startPresenter, _endPresenter);
|
||||||
|
|
||||||
_startPresenter?.SetValue(TimePickerPresenter.TimeProperty, StartTime);
|
_startPresenter?.SyncTime(StartTime);
|
||||||
_endPresenter?.SetValue(TimePickerPresenter.TimeProperty, EndTime);
|
_endPresenter?.SyncTime(EndTime);
|
||||||
SyncTimeToText(StartTime);
|
SyncTimeToText(StartTime);
|
||||||
SyncTimeToText(EndTime, false);
|
SyncTimeToText(EndTime, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class TimePickerPresenterTests
|
|||||||
Assert.False(presenter.NeedsConfirmation);
|
Assert.False(presenter.NeedsConfirmation);
|
||||||
Assert.Equal(1, presenter.MinuteIncrement);
|
Assert.Equal(1, presenter.MinuteIncrement);
|
||||||
Assert.Equal(1, presenter.SecondIncrement);
|
Assert.Equal(1, presenter.SecondIncrement);
|
||||||
Assert.Null(presenter.Time);
|
Assert.Null(presenter.TimeHolder);
|
||||||
Assert.Equal("HH mm ss t", presenter.PanelFormat);
|
Assert.Equal("HH mm ss t", presenter.PanelFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,9 +27,9 @@ public class TimePickerPresenterTests
|
|||||||
var presenter = new TimePickerPresenter();
|
var presenter = new TimePickerPresenter();
|
||||||
var time = new TimeSpan(10, 30, 45);
|
var time = new TimeSpan(10, 30, 45);
|
||||||
|
|
||||||
presenter.Time = time;
|
presenter.TimeHolder = time;
|
||||||
|
|
||||||
Assert.Equal(time, presenter.Time);
|
Assert.Equal(time, presenter.TimeHolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AvaloniaTheory]
|
[AvaloniaTheory]
|
||||||
@@ -77,37 +77,44 @@ public class TimePickerPresenterTests
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[AvaloniaFact]
|
||||||
public void TimePickerPresenter_Confirm_ShouldSetTimeProperty()
|
public void TimePickerPresenter_Confirm_ShouldSetTimeProperty()
|
||||||
{
|
{
|
||||||
var presenter = new TimePickerPresenter { NeedsConfirmation = true };
|
var presenter = new TimePickerPresenter { NeedsConfirmation = true };
|
||||||
var time = new TimeSpan(10, 30, 45);
|
var time = new TimeSpan(10, 30, 45);
|
||||||
|
var eventRaised = 0;
|
||||||
|
TimeSpan? eventResult = null;
|
||||||
|
presenter.SelectedTimeChanged += (o, e) =>
|
||||||
|
{
|
||||||
|
eventRaised++;
|
||||||
|
eventResult = e.NewTime;
|
||||||
|
};
|
||||||
presenter.TimeHolder = time;
|
presenter.TimeHolder = time;
|
||||||
Assert.NotEqual(time, presenter.Time);
|
Assert.Null(eventResult);
|
||||||
|
Assert.Equal(0, eventRaised);
|
||||||
|
|
||||||
presenter.Confirm();
|
presenter.Confirm();
|
||||||
|
|
||||||
Assert.Equal(time, presenter.Time);
|
Assert.Equal(time, presenter.TimeHolder);
|
||||||
|
Assert.Equal(time, eventResult);
|
||||||
|
Assert.Equal(1, eventRaised);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AvaloniaFact]
|
[AvaloniaFact]
|
||||||
public void TimePickerPresenter_OnTimeChanged_ShouldRaiseEvent()
|
public void TimePickerPresenter_SyncTime_Should_Not_RaiseEvent()
|
||||||
{
|
{
|
||||||
var presenter = new TimePickerPresenter();
|
var presenter = new TimePickerPresenter();
|
||||||
var oldTime = new TimeSpan(10, 30, 45);
|
var oldTime = new TimeSpan(10, 30, 45);
|
||||||
var newTime = new TimeSpan(11, 45, 30);
|
var newTime = new TimeSpan(11, 45, 30);
|
||||||
presenter.Time = oldTime;
|
presenter.SyncTime(oldTime);
|
||||||
var eventRaised = false;
|
var eventRaised = false;
|
||||||
presenter.SelectedTimeChanged += (sender, args) =>
|
presenter.SelectedTimeChanged += (sender, args) =>
|
||||||
{
|
{
|
||||||
eventRaised = true;
|
eventRaised = true;
|
||||||
Assert.Equal(oldTime, args.OldTime);
|
|
||||||
Assert.Equal(newTime, args.NewTime);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
presenter.Time = newTime;
|
presenter.SyncTime(newTime);
|
||||||
|
Assert.False(eventRaised);
|
||||||
Assert.True(eventRaised);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[AvaloniaTheory]
|
[AvaloniaTheory]
|
||||||
@@ -120,6 +127,11 @@ public class TimePickerPresenterTests
|
|||||||
window.Show();
|
window.Show();
|
||||||
Dispatcher.UIThread.RunJobs();
|
Dispatcher.UIThread.RunJobs();
|
||||||
presenter.PanelFormat = format;
|
presenter.PanelFormat = format;
|
||||||
|
TimeSpan? eventResult = null;
|
||||||
|
presenter.SelectedTimeChanged += (o, e) =>
|
||||||
|
{
|
||||||
|
eventResult = e.NewTime;
|
||||||
|
};
|
||||||
Dispatcher.UIThread.RunJobs();
|
Dispatcher.UIThread.RunJobs();
|
||||||
|
|
||||||
var hourPanel = presenter.GetTemplateChildren().OfType<DateTimePickerPanel>().FirstOrDefault(a => a.Name == TimePickerPresenter.PART_HourSelector);
|
var hourPanel = presenter.GetTemplateChildren().OfType<DateTimePickerPanel>().FirstOrDefault(a => a.Name == TimePickerPresenter.PART_HourSelector);
|
||||||
@@ -137,7 +149,7 @@ public class TimePickerPresenterTests
|
|||||||
secondPanel.SelectedValue = secondSelection;
|
secondPanel.SelectedValue = secondSelection;
|
||||||
amPanel.SelectedValue = amSelection;
|
amPanel.SelectedValue = amSelection;
|
||||||
|
|
||||||
Assert.Equal(expectedTime, presenter.Time);
|
Assert.Equal(expectedTime, eventResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
[AvaloniaTheory]
|
[AvaloniaTheory]
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Headless;
|
||||||
|
using Avalonia.Headless.XUnit;
|
||||||
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Layout;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using HeadlessTest.Ursa.TestHelpers;
|
||||||
|
using TimePicker = Ursa.Controls.TimePicker;
|
||||||
|
|
||||||
|
namespace HeadlessTest.Ursa.Controls.DateTimePicker;
|
||||||
|
|
||||||
|
public class TimePickerTests
|
||||||
|
{
|
||||||
|
[AvaloniaFact]
|
||||||
|
public void Click_Opens_Popup()
|
||||||
|
{
|
||||||
|
var window = new Window();
|
||||||
|
var timePicker = new TimePicker()
|
||||||
|
{
|
||||||
|
Width = 300,
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = VerticalAlignment.Top,
|
||||||
|
};
|
||||||
|
window.Content = timePicker;
|
||||||
|
window.Show();
|
||||||
|
Dispatcher.UIThread.RunJobs();
|
||||||
|
Assert.False(timePicker.IsDropdownOpen);
|
||||||
|
window.MouseDown(new Point(10, 10), MouseButton.Left);
|
||||||
|
Assert.True(timePicker.IsDropdownOpen);
|
||||||
|
}
|
||||||
|
|
||||||
|
[AvaloniaFact]
|
||||||
|
public void Click_Button_Toggles_Popup()
|
||||||
|
{
|
||||||
|
var window = new Window();
|
||||||
|
var picker = new TimePicker()
|
||||||
|
{
|
||||||
|
Width = 300,
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
|
VerticalAlignment = VerticalAlignment.Top
|
||||||
|
};
|
||||||
|
window.Content = picker;
|
||||||
|
window.Show();
|
||||||
|
|
||||||
|
var button = picker.GetTemplateChildOfType<Button>(TimePicker.PART_Button);
|
||||||
|
var position = button?.TranslatePoint(new Point(5, 5), window);
|
||||||
|
Assert.NotNull(position);
|
||||||
|
|
||||||
|
Assert.False(picker.IsDropdownOpen);
|
||||||
|
Dispatcher.UIThread.RunJobs();
|
||||||
|
window.MouseDown(position.Value, MouseButton.Left);
|
||||||
|
window.MouseUp(position.Value, MouseButton.Left);
|
||||||
|
Dispatcher.UIThread.RunJobs();
|
||||||
|
Assert.True(picker.IsDropdownOpen);
|
||||||
|
|
||||||
|
window.MouseDown(position.Value, MouseButton.Left);
|
||||||
|
window.MouseUp(position.Value, MouseButton.Left);
|
||||||
|
Dispatcher.UIThread.RunJobs();
|
||||||
|
Assert.False(picker.IsDropdownOpen);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user