diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/TimePickerShowCase.axaml b/samples/AtomUI.Demo.Desktop/ShowCase/TimePickerShowCase.axaml
index 89a15ff95..1d8eea718 100644
--- a/samples/AtomUI.Demo.Desktop/ShowCase/TimePickerShowCase.axaml
+++ b/samples/AtomUI.Demo.Desktop/ShowCase/TimePickerShowCase.axaml
@@ -66,18 +66,31 @@
Title="Variants"
Description="Bordered-less style component.">
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -85,16 +98,36 @@
Title="Status"
Description="Add status to TimePicker with status, which could be error or warning.">
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AtomUI.Controls/TimePicker/DateTimePickerPanel.cs b/src/AtomUI.Controls/TimePicker/DateTimePickerPanel.cs
index 08594b371..c3d06d2af 100644
--- a/src/AtomUI.Controls/TimePicker/DateTimePickerPanel.cs
+++ b/src/AtomUI.Controls/TimePicker/DateTimePickerPanel.cs
@@ -11,6 +11,7 @@ using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
+using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace AtomUI.Controls;
@@ -581,7 +582,7 @@ public class DateTimePickerPanel : Panel, ILogicalScrollable
{
var snapY = Math.Round(Offset.Y / ItemHeight) * ItemHeight;
- if (snapY != Offset.Y) {
+ if (!MathUtilities.AreClose(snapY, Offset.Y)) {
Offset = Offset.WithY(snapY);
}
}
diff --git a/src/AtomUI.Controls/TimePicker/RangeTimePicker.cs b/src/AtomUI.Controls/TimePicker/RangeTimePicker.cs
index b2a6c792d..42940e2ea 100644
--- a/src/AtomUI.Controls/TimePicker/RangeTimePicker.cs
+++ b/src/AtomUI.Controls/TimePicker/RangeTimePicker.cs
@@ -1,4 +1,5 @@
-using AtomUI.Data;
+using AtomUI.Controls.Utils;
+using AtomUI.Data;
using AtomUI.Theme.Utils;
using Avalonia;
using Avalonia.Animation;
@@ -292,16 +293,15 @@ public class RangeTimePicker : TemplatedControl
if (!IsEnabled) {
return false;
}
-
- return PositionInEditKernel(position);
- }
- private bool PositionInEditKernel(Point position)
- {
- if (_rangeStartTextBox is not null && _rangeEndTextBox is not null) {
- return PositionInRangeStartTextBox(position) ||
- PositionInRangeEndTextBox(position);
+ if (PositionInRangeStartTextBox(position)) {
+ RangeActivatedPart = RangeActivatedPart.Start;
+ return true;
+ } else if (PositionInRangeEndTextBox(position)) {
+ RangeActivatedPart = RangeActivatedPart.End;
+ return true;
}
+
return false;
}
@@ -329,7 +329,6 @@ public class RangeTimePicker : TemplatedControl
targetWidth = endOffsetX - startOffsetX;
var bounds = new Rect(new Point(startOffsetX, offsetY), new Size(targetWidth, targetHeight));
if (bounds.Contains(position)) {
- RangeActivatedPart = RangeActivatedPart.Start;
return true;
}
@@ -361,7 +360,6 @@ public class RangeTimePicker : TemplatedControl
targetWidth = endOffsetX - startOffsetX;
var bounds = new Rect(new Point(startOffsetX, offsetY), new Size(targetWidth, targetHeight));
if (bounds.Contains(position)) {
- RangeActivatedPart = RangeActivatedPart.End;
return true;
}
@@ -371,7 +369,17 @@ public class RangeTimePicker : TemplatedControl
private bool ClickHideFlyoutPredicate(IPopupHostProvider hostProvider, RawPointerEventArgs args)
{
if (hostProvider.PopupHost != args.Root) {
- if (!PositionInEditKernel(args.Position)) {
+ var inRangeStart = PositionInRangeStartTextBox(args.Position);
+ var inRangeEnd = PositionInRangeEndTextBox(args.Position);
+ if (inRangeStart) {
+ RangeActivatedPart = RangeActivatedPart.Start;
+ }
+
+ if (inRangeEnd) {
+ RangeActivatedPart = RangeActivatedPart.End;
+ }
+ if (!inRangeStart &&
+ !inRangeEnd) {
return true;
}
}
@@ -381,20 +389,27 @@ public class RangeTimePicker : TemplatedControl
private void HandleFlyoutAboutToShow(object? sender, EventArgs args)
{
- _rangeStartTextBox?.Focus();
_currentValidSelected = false;
}
private void HandleFlyoutAboutToClose(object? sender, EventArgs args)
{
+ if (!_currentValidSelected) {
+ if (RangeActivatedPart == RangeActivatedPart.Start) {
+ if (RangeStartSelectedTime.HasValue) {
+ _rangeStartTextBox!.Text = DateTimeUtils.FormatTimeSpan(RangeStartSelectedTime.Value, ClockIdentifier == ClockIdentifierType.HourClock12);
+ } else {
+ ResetRangeStartTimeValue();
+ }
+ } else if (RangeActivatedPart == RangeActivatedPart.End) {
+ if (RangeEndSelectedTime.HasValue) {
+ _rangeEndTextBox!.Text = DateTimeUtils.FormatTimeSpan(RangeEndSelectedTime.Value, ClockIdentifier == ClockIdentifierType.HourClock12);
+ } else {
+ ResetRangeEndTimeValue();
+ }
+ }
+ }
RangeActivatedPart = RangeActivatedPart.None;
- // if (!_currentValidSelected) {
- // if (SelectedTime.HasValue) {
- // Text = DateTimeUtils.FormatTimeSpan(SelectedTime.Value, ClockIdentifier == ClockIdentifierType.HourClock12);
- // } else {
- // ResetTimeValue();
- // }
- // }
}
protected override Size ArrangeOverride(Size finalSize)
@@ -422,8 +437,10 @@ public class RangeTimePicker : TemplatedControl
_pickerClearUpButton = new PickerClearUpButton();
_pickerClearUpButton.ClearRequest += (sender, args) =>
{
- // ResetTimeValue();
- // SelectedTime = null;
+ ResetRangeStartTimeValue();
+ ResetRangeEndTimeValue();
+ RangeStartSelectedTime = null;
+ RangeEndSelectedTime = null;
};
InnerRightContent = _pickerClearUpButton;
}
@@ -491,9 +508,12 @@ public class RangeTimePicker : TemplatedControl
base.OnAttachedToLogicalTree(e);
BindUtils.RelayBind(this, MouseEnterDelayProperty, _flyoutStateHelper, FlyoutStateHelper.MouseEnterDelayProperty);
BindUtils.RelayBind(this, MouseLeaveDelayProperty, _flyoutStateHelper, FlyoutStateHelper.MouseLeaveDelayProperty);
- // if (DefaultTime is not null) {
- // SelectedTime = DefaultTime;
- // }
+ if (RangeStartDefaultTime is not null) {
+ RangeStartSelectedTime = RangeStartDefaultTime;
+ }
+ if (RangeEndDefaultTime is not null) {
+ RangeEndDefaultTime = RangeEndDefaultTime;
+ }
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
@@ -506,25 +526,33 @@ public class RangeTimePicker : TemplatedControl
}
}
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+ _flyoutStateHelper.NotifyDetachedFromVisualTree();
+ _clearUpButtonDetectDisposable?.Dispose();
+ _clearUpButtonDetectDisposable = null;
+ }
+
private void DetectClearUpButtonState(RawInputEventArgs args)
{
if (IsEnabled) {
if (args is RawPointerEventArgs pointerEventArgs) {
- // if (_textBoxInnerBox is not null) {
- // var pos = _textBoxInnerBox.TranslatePoint(new Point(0, 0), TopLevel.GetTopLevel(this)!);
- // if (!pos.HasValue) {
- // return;
- // }
- //
- // var bounds = new Rect(pos.Value, _textBoxInnerBox.Bounds.Size);
- // if (bounds.Contains(pointerEventArgs.Position)) {
- // if (SelectedTime is not null) {
- // _pickerClearUpButton!.IsInClearMode = true;
- // }
- // } else {
- // _pickerClearUpButton!.IsInClearMode = false;
- // }
- // }
+ if (_rangePickerInner is not null) {
+ var pos = _rangePickerInner.TranslatePoint(new Point(0, 0), TopLevel.GetTopLevel(this)!);
+ if (!pos.HasValue) {
+ return;
+ }
+
+ var bounds = new Rect(pos.Value, _rangePickerInner.Bounds.Size);
+ if (bounds.Contains(pointerEventArgs.Position)) {
+ if (RangeStartSelectedTime is not null || RangeEndSelectedTime is not null) {
+ _pickerClearUpButton!.IsInClearMode = true;
+ }
+ } else {
+ _pickerClearUpButton!.IsInClearMode = false;
+ }
+ }
}
}
}
@@ -535,6 +563,41 @@ public class RangeTimePicker : TemplatedControl
if (change.Property == RangeActivatedPartProperty) {
HandleRangeActivatedPartChanged();
}
+ if (VisualRoot is not null) {
+ if (change.Property == RangeStartSelectedTimeProperty) {
+ if (RangeStartSelectedTime.HasValue) {
+ _rangeStartTextBox!.Text = DateTimeUtils.FormatTimeSpan(RangeStartSelectedTime.Value, ClockIdentifier == ClockIdentifierType.HourClock12);
+ } else {
+ ResetRangeStartTimeValue();
+ }
+ } else if (change.Property == RangeEndSelectedTimeProperty) {
+ if (RangeEndSelectedTime.HasValue) {
+ _rangeEndTextBox!.Text = DateTimeUtils.FormatTimeSpan(RangeEndSelectedTime.Value, ClockIdentifier == ClockIdentifierType.HourClock12);
+ } else {
+ ResetRangeEndTimeValue();
+ }
+ }
+ }
+ }
+
+ protected void ResetRangeStartTimeValue()
+ {
+ var targetTextBox = _rangeStartTextBox!;
+ if (RangeStartDefaultTime is not null) {
+ targetTextBox.Text = DateTimeUtils.FormatTimeSpan(RangeStartDefaultTime.Value, ClockIdentifier == ClockIdentifierType.HourClock12);
+ } else {
+ targetTextBox.Clear();
+ }
+ }
+
+ protected void ResetRangeEndTimeValue()
+ {
+ var targetTextBox = _rangeEndTextBox!;
+ if (RangeEndDefaultTime is not null) {
+ targetTextBox.Text = DateTimeUtils.FormatTimeSpan(RangeEndDefaultTime.Value, ClockIdentifier == ClockIdentifierType.HourClock12);
+ } else {
+ targetTextBox.Clear();
+ }
}
protected override Size MeasureOverride(Size availableSize)
@@ -557,19 +620,45 @@ public class RangeTimePicker : TemplatedControl
{
if (_rangeActivatedPart == RangeActivatedPart.Start) {
PickerPlacement = PlacementMode.BottomEdgeAlignedLeft;
+ if (RangeEndSelectedTime is null) {
+ ResetRangeEndTimeValue();
+ }
+
+ _rangeStartTextBox!.Focus();
} else if (_rangeActivatedPart == RangeActivatedPart.End) {
PickerPlacement = PlacementMode.BottomEdgeAlignedRight;
+ if (RangeStartSelectedTime is null) {
+ ResetRangeStartTimeValue();
+ }
+ _rangeEndTextBox!.Focus();
+ } else {
+ if (RangeStartSelectedTime is null) {
+ ResetRangeStartTimeValue();
+ }
+ if (RangeEndSelectedTime is null) {
+ ResetRangeEndTimeValue();
+ }
}
-
SetupPickerIndicatorPosition();
}
internal void NotifyConfirmed(TimeSpan value)
{
+ _currentValidSelected = true;
+ if (RangeActivatedPart == RangeActivatedPart.Start) {
+ RangeStartSelectedTime = value;
+ } else if (RangeActivatedPart == RangeActivatedPart.End) {
+ RangeEndSelectedTime = value;
+ }
}
- internal void NotifyTemporaryTimeSelected(TimeSpan selected)
+ internal void NotifyTemporaryTimeSelected(TimeSpan value)
{
+ if (RangeActivatedPart == RangeActivatedPart.Start) {
+ _rangeStartTextBox!.Text = DateTimeUtils.FormatTimeSpan(value, ClockIdentifier == ClockIdentifierType.HourClock12);
+ } else if (RangeActivatedPart == RangeActivatedPart.End) {
+ _rangeEndTextBox!.Text = DateTimeUtils.FormatTimeSpan(value, ClockIdentifier == ClockIdentifierType.HourClock12);
+ }
}
internal void ClosePickerFlyout()
diff --git a/src/AtomUI.Controls/TimePicker/RangeTimePickerFlyoutPresenter.cs b/src/AtomUI.Controls/TimePicker/RangeTimePickerFlyoutPresenter.cs
index c75d99ffc..9fc613bae 100644
--- a/src/AtomUI.Controls/TimePicker/RangeTimePickerFlyoutPresenter.cs
+++ b/src/AtomUI.Controls/TimePicker/RangeTimePickerFlyoutPresenter.cs
@@ -1,4 +1,5 @@
-using Avalonia;
+using System.Reactive.Disposables;
+using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
@@ -49,7 +50,7 @@ internal class RangeTimePickerFlyoutPresenter : FlyoutPresenter
internal RangeTimePicker TimePickerRef { get; set; }
private TimePickerPresenter? _timePickerPresenter;
- private IDisposable? _disposable;
+ private CompositeDisposable? _compositeDisposable;
private Button? _confirmButton;
private Button? _nowButton;
@@ -71,9 +72,8 @@ internal class RangeTimePickerFlyoutPresenter : FlyoutPresenter
{
TimePickerRef.NotifyConfirmed(_timePickerPresenter.Time);
};
- // if (TimePickerRef.DefaultTime is not null) {
- // _timePickerPresenter.Time = TimePickerRef.DefaultTime.Value;
- // }
+
+ SetupTime();
}
if (_confirmButton is not null) {
@@ -84,6 +84,20 @@ internal class RangeTimePickerFlyoutPresenter : FlyoutPresenter
_nowButton.Click += HandleNowButtonClicked;
}
}
+
+ private void SetupTime()
+ {
+ if (TimePickerRef.RangeActivatedPart == RangeActivatedPart.Start) {
+ if (TimePickerRef.RangeStartSelectedTime is not null) {
+ _timePickerPresenter!.Time = TimePickerRef.RangeStartSelectedTime.Value;
+
+ }
+ } else if (TimePickerRef.RangeActivatedPart == RangeActivatedPart.End) {
+ if (TimePickerRef.RangeEndSelectedTime is not null) {
+ _timePickerPresenter!.Time = TimePickerRef.RangeEndSelectedTime.Value;
+ }
+ }
+ }
private void HandleNowButtonClicked(object? sender, RoutedEventArgs args)
{
@@ -94,24 +108,43 @@ internal class RangeTimePickerFlyoutPresenter : FlyoutPresenter
private void HandleConfirmButtonClicked(object? sender, RoutedEventArgs args)
{
_timePickerPresenter?.Confirm();
- TimePickerRef.PickerPlacement = PlacementMode.BottomEdgeAlignedRight;
- // TimePickerRef.ClosePickerFlyout();
+ if (TimePickerRef.RangeActivatedPart == RangeActivatedPart.Start) {
+ if (TimePickerRef.RangeEndSelectedTime is null) {
+ TimePickerRef.RangeActivatedPart = RangeActivatedPart.End;
+ return;
+ }
+ } else if (TimePickerRef.RangeActivatedPart == RangeActivatedPart.End) {
+ if (TimePickerRef.RangeStartSelectedTime is null) {
+ TimePickerRef.RangeActivatedPart = RangeActivatedPart.Start;
+ return;
+ }
+ }
+ TimePickerRef.ClosePickerFlyout();
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
+ _compositeDisposable = new CompositeDisposable();
if (_timePickerPresenter is not null) {
- _disposable = TimePickerPresenter.TemporaryTimeProperty.Changed.Subscribe(args =>
+ _compositeDisposable.Add(TimePickerPresenter.TemporaryTimeProperty.Changed.Subscribe(args =>
{
TimePickerRef.NotifyTemporaryTimeSelected(args.GetNewValue());
- });
+ }));
}
+ _compositeDisposable.Add(RangeTimePicker.RangeActivatedPartProperty.Changed.Subscribe(HandleRangeActivatedPartChanged));
+ SetupTime();
+ }
+
+ private void HandleRangeActivatedPartChanged(AvaloniaPropertyChangedEventArgs args)
+ {
+ SetupTime();
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
- _disposable?.Dispose();
+ _compositeDisposable?.Dispose();
+ _compositeDisposable = null;
}
}
\ No newline at end of file
diff --git a/src/AtomUI.Controls/TimePicker/TimePicker.cs b/src/AtomUI.Controls/TimePicker/TimePicker.cs
index c2c44baab..95e9b41ee 100644
--- a/src/AtomUI.Controls/TimePicker/TimePicker.cs
+++ b/src/AtomUI.Controls/TimePicker/TimePicker.cs
@@ -28,36 +28,21 @@ public class TimePicker : LineEdit
public static readonly StyledProperty PickerPlacementProperty =
AvaloniaProperty.Register(nameof(PickerPlacement), defaultValue: PlacementMode.BottomEdgeAlignedLeft);
- ///
- /// 是否显示指示箭头
- ///
public static readonly StyledProperty IsShowArrowProperty =
ArrowDecoratedBox.IsShowArrowProperty.AddOwner();
-
- ///
- /// 箭头是否始终指向中心
- ///
+
public static readonly StyledProperty IsPointAtCenterProperty =
Flyout.IsPointAtCenterProperty.AddOwner();
- ///
- /// Defines the property
- ///
public static readonly StyledProperty MinuteIncrementProperty =
AvaloniaProperty.Register(nameof(MinuteIncrement), 1, coerce: CoerceMinuteIncrement);
public static readonly StyledProperty SecondIncrementProperty =
AvaloniaProperty.Register(nameof(SecondIncrement), 1, coerce: CoerceSecondIncrement);
-
- ///
- /// Defines the property
- ///
+
public static readonly StyledProperty ClockIdentifierProperty =
AvaloniaProperty.Register(nameof(ClockIdentifier), ClockIdentifierType.HourClock12);
- ///
- /// Defines the property
- ///
public static readonly StyledProperty SelectedTimeProperty =
AvaloniaProperty.Register(nameof(SelectedTime),
defaultBindingMode: BindingMode.TwoWay,
@@ -76,9 +61,6 @@ public class TimePicker : LineEdit
public static readonly StyledProperty MouseLeaveDelayProperty =
FlyoutStateHelper.MouseLeaveDelayProperty.AddOwner();
- ///
- /// Gets or sets the desired placement of the popup in relation to the .
- ///
public PlacementMode PickerPlacement
{
get => GetValue(PickerPlacementProperty);
@@ -97,9 +79,6 @@ public class TimePicker : LineEdit
set => SetValue(IsPointAtCenterProperty, value);
}
- ///
- /// Gets or sets the minute increment in the picker
- ///
public int MinuteIncrement
{
get => GetValue(MinuteIncrementProperty);
@@ -111,19 +90,13 @@ public class TimePicker : LineEdit
get => GetValue(SecondIncrementProperty);
set => SetValue(SecondIncrementProperty, value);
}
-
- ///
- /// Gets or sets the clock identifier, either 12HourClock or 24HourClock
- ///
+
public ClockIdentifierType ClockIdentifier
{
get => GetValue(ClockIdentifierProperty);
set => SetValue(ClockIdentifierProperty, value);
}
-
- ///
- /// Gets or sets the selected time. Can be null.
- ///
+
public TimeSpan? SelectedTime
{
get => GetValue(SelectedTimeProperty);
@@ -310,6 +283,14 @@ public class TimePicker : LineEdit
_clearUpButtonDetectDisposable = inputManager.Process.Subscribe(DetectClearUpButtonState);
}
}
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+ _flyoutStateHelper.NotifyDetachedFromVisualTree();
+ _clearUpButtonDetectDisposable?.Dispose();
+ _clearUpButtonDetectDisposable = null;
+ }
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
@@ -356,14 +337,6 @@ public class TimePicker : LineEdit
}
}
}
-
- protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
- {
- base.OnDetachedFromVisualTree(e);
- _flyoutStateHelper.NotifyDetachedFromVisualTree();
- _clearUpButtonDetectDisposable?.Dispose();
- _clearUpButtonDetectDisposable = null;
- }
private static int CoerceMinuteIncrement(AvaloniaObject sender, int value)
{
@@ -382,9 +355,9 @@ public class TimePicker : LineEdit
return value;
}
- internal void NotifyTemporaryTimeSelected(TimeSpan selected)
+ internal void NotifyTemporaryTimeSelected(TimeSpan value)
{
- Text = DateTimeUtils.FormatTimeSpan(selected, ClockIdentifier == ClockIdentifierType.HourClock12);
+ Text = DateTimeUtils.FormatTimeSpan(value, ClockIdentifier == ClockIdentifierType.HourClock12);
}
internal void NotifyConfirmed(TimeSpan value)
diff --git a/src/AtomUI.Controls/TimePicker/TimePickerPresenter.cs b/src/AtomUI.Controls/TimePicker/TimePickerPresenter.cs
index c54dc127c..36bec5ddd 100644
--- a/src/AtomUI.Controls/TimePicker/TimePickerPresenter.cs
+++ b/src/AtomUI.Controls/TimePicker/TimePickerPresenter.cs
@@ -144,6 +144,7 @@ public class TimePickerPresenter : PickerPresenterBase
TokenResourceBinder.CreateGlobalTokenBinding(this, SpacerThicknessProperty, GlobalTokenResourceKey.LineWidth,
BindingPriority.Template,
new RenderScaleAwareDoubleConfigure(this));
+ InitPicker();
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
@@ -176,8 +177,6 @@ public class TimePickerPresenter : PickerPresenterBase
}
_spacer3 = e.NameScope.Get(TimePickerPresenterTheme.ThirdSpacerPart);
-
- InitPicker();
}
private void HandleSelectionChanged(object? sender, EventArgs args)
@@ -295,7 +294,5 @@ public class TimePickerPresenter : PickerPresenterBase
_spacer3!.IsVisible = !use24HourClock;
_periodHost!.IsVisible = !use24HourClock;
-
- _hourSelector?.Focus(NavigationMethod.Pointer);
}
}
\ No newline at end of file