diff --git a/src/AtomUI.Controls/Flyouts/FlyoutStateHelper.cs b/src/AtomUI.Controls/Flyouts/FlyoutStateHelper.cs index 9163e51..36d9b86 100644 --- a/src/AtomUI.Controls/Flyouts/FlyoutStateHelper.cs +++ b/src/AtomUI.Controls/Flyouts/FlyoutStateHelper.cs @@ -62,6 +62,7 @@ internal class FlyoutStateHelper : AvaloniaObject public event EventHandler? FlyoutAboutToShow; public Func? OpenFlyoutPredicate; + public Func? ClickHideFlyoutPredicate; private DispatcherTimer? _mouseEnterDelayTimer; private DispatcherTimer? _mouseLeaveDelayTimer; @@ -258,9 +259,16 @@ internal class FlyoutStateHelper : AvaloniaObject } } } else { + if (Flyout is IPopupHostProvider popupHostProvider) { - if (popupHostProvider.PopupHost != pointerEventArgs.Root) { - HideFlyout(); + if (ClickHideFlyoutPredicate is not null) { + if (ClickHideFlyoutPredicate(popupHostProvider, pointerEventArgs)) { + HideFlyout(); + } + } else { + if (popupHostProvider.PopupHost != pointerEventArgs.Root) { + HideFlyout(); + } } } } diff --git a/src/AtomUI.Controls/TimePicker/PickerIndicator.cs b/src/AtomUI.Controls/TimePicker/PickerIndicator.cs index 0ca5538..956b6d8 100644 --- a/src/AtomUI.Controls/TimePicker/PickerIndicator.cs +++ b/src/AtomUI.Controls/TimePicker/PickerIndicator.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.Primitives; namespace AtomUI.Controls; @@ -15,4 +16,18 @@ internal class PickerIndicator : TemplatedControl get => GetValue(IsInClearModeProperty); set => SetValue(IsInClearModeProperty, value); } + + private IconButton? _clearButton; + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + _clearButton = e.NameScope.Get(PickerIndicatorTheme.ClearButtonPart); + if (_clearButton is not null) { + _clearButton.Click += (sender, args) => + { + ClearRequest?.Invoke(this, EventArgs.Empty); + }; + } + } } \ No newline at end of file diff --git a/src/AtomUI.Controls/TimePicker/TimePicker.cs b/src/AtomUI.Controls/TimePicker/TimePicker.cs index 79ea945..289e088 100644 --- a/src/AtomUI.Controls/TimePicker/TimePicker.cs +++ b/src/AtomUI.Controls/TimePicker/TimePicker.cs @@ -1,8 +1,10 @@ -using AtomUI.Data; +using AtomUI.Controls.Utils; +using AtomUI.Data; using AtomUI.Theme.Styling; using AtomUI.Utils; using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Diagnostics; using Avalonia.Controls.Primitives; using Avalonia.Data; using Avalonia.Input; @@ -162,9 +164,15 @@ public class TimePicker : LineEdit _flyoutStateHelper.FlyoutAboutToShow += HandleFlyoutAboutToShow; _flyoutStateHelper.FlyoutAboutToClose += HandleFlyoutAboutToClose; _flyoutStateHelper.OpenFlyoutPredicate = FlyoutOpenPredicate; + _flyoutStateHelper.ClickHideFlyoutPredicate = ClickHideFlyoutPredicate; } private bool FlyoutOpenPredicate(Point position) + { + return PositionInEditKernel(position); + } + + private bool PositionInEditKernel(Point position) { if (_textBoxInnerBox is not null) { var pos = _textBoxInnerBox.TranslatePoint(new Point(0, 0), TopLevel.GetTopLevel(this)!); @@ -200,6 +208,17 @@ public class TimePicker : LineEdit return false; } + private bool ClickHideFlyoutPredicate(IPopupHostProvider hostProvider, RawPointerEventArgs args) + { + if (hostProvider.PopupHost != args.Root) { + if (!PositionInEditKernel(args.Position)) { + return true; + } + } + + return false; + } + private void HandleFlyoutAboutToShow(object? sender, EventArgs args) { _currentValidSelected = false; @@ -208,7 +227,11 @@ public class TimePicker : LineEdit private void HandleFlyoutAboutToClose(object? sender, EventArgs args) { if (!_currentValidSelected) { - Text = SelectedTime.ToString(); + if (SelectedTime.HasValue) { + Text = DateTimeUtils.FormatTimeSpan(SelectedTime.Value, ClockIdentifier == "12HourClock"); + } else { + Clear(); + } } } @@ -222,6 +245,11 @@ public class TimePicker : LineEdit base.OnApplyTemplate(e); if (InnerRightContent is null) { _pickerIndicator = new PickerIndicator(); + _pickerIndicator.ClearRequest += (sender, args) => + { + Clear(); + SelectedTime = null; + }; InnerRightContent = _pickerIndicator; } if (_pickerFlyout is null) { @@ -260,7 +288,21 @@ public class TimePicker : LineEdit _indicatorDetectDisposable = inputManager.Process.Subscribe(DetectIndicatorState); } } - + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + if (VisualRoot is not null) { + if (change.Property == SelectedTimeProperty) { + if (SelectedTime.HasValue) { + Text = DateTimeUtils.FormatTimeSpan(SelectedTime.Value, ClockIdentifier == "12HourClock"); + } else { + Clear(); + } + } + } + } + private void DetectIndicatorState(RawInputEventArgs args) { if (args is RawPointerEventArgs pointerEventArgs) { @@ -318,7 +360,7 @@ public class TimePicker : LineEdit internal void NotifyTemporaryTimeSelected(TimeSpan selected) { - Text = selected.ToString(); + Text = DateTimeUtils.FormatTimeSpan(selected, ClockIdentifier == "12HourClock"); } internal void NotifyConfirmed(TimeSpan value) diff --git a/src/AtomUI.Controls/TimePicker/TimePickerFlyoutPresenter.cs b/src/AtomUI.Controls/TimePicker/TimePickerFlyoutPresenter.cs index 8aea8e2..7a9d186 100644 --- a/src/AtomUI.Controls/TimePicker/TimePickerFlyoutPresenter.cs +++ b/src/AtomUI.Controls/TimePicker/TimePickerFlyoutPresenter.cs @@ -15,7 +15,8 @@ public class TimePickerFlyoutPresenter : FlyoutPresenter private TimePickerPresenter? _timePickerPresenter; private IDisposable? _disposable; private Button? _confirmButton; - + private Button? _nowButton; + public TimePickerFlyoutPresenter(TimePicker timePicker) { TimePickerRef = timePicker; @@ -27,6 +28,7 @@ public class TimePickerFlyoutPresenter : FlyoutPresenter base.OnApplyTemplate(e); _timePickerPresenter = e.NameScope.Get(TimePickerFlyoutPresenterTheme.ContentPresenterPart); _confirmButton = e.NameScope.Get