feat: add popup content. format code.

This commit is contained in:
rabbitism
2024-04-27 01:11:17 +08:00
parent 9cce1cc180
commit 31847513d9
3 changed files with 125 additions and 126 deletions

View File

@@ -108,6 +108,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource TextBoxDefaultBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource TextBoxBorderThickness}" />
<Setter Property="CornerRadius" Value="{DynamicResource TextBoxDefaultCornerRadius}" />
<Setter Property="MinHeight" Value="32"/>
<Setter Property="Template">
<ControlTemplate TargetType="u:TimePicker">
<DataValidationErrors>
@@ -117,12 +118,12 @@
VerticalAlignment="Stretch">
<Border
x:Name="Background"
HorizontalAlignment="Stretch"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}" />
<Grid ColumnDefinitions="*, Auto, Auto">
<!-- InnerLeftContent, Text and Watermark, ClearButton, InnerRightContent, Icon -->
<TextBox
Name="{x:Static u:TimePicker.PART_TextBox}"
Grid.Column="0"
@@ -196,10 +197,25 @@
<DockPanel>
<StackPanel DockPanel.Dock="Bottom" IsVisible="{TemplateBinding NeedConfirmation}">
<Button
Margin="8"
HorizontalAlignment="Right"
Command="{Binding $parent[u:TimePicker].Confirm}"
Content="Confirm" />
</StackPanel>
<ContentPresenter
Name="PART_PopupHeader"
Margin="8,8,8,0"
Content="{TemplateBinding PopupInnerTopContent}"
DockPanel.Dock="Top"
IsVisible="{TemplateBinding PopupInnerTopContent,
Converter={x:Static ObjectConverters.IsNotNull}}" />
<ContentPresenter
Name="PART_PopupFooter"
Margin="8,0,8,8"
Content="{TemplateBinding PopupInnerBottomContent}"
DockPanel.Dock="Bottom"
IsVisible="{TemplateBinding PopupInnerBottomContent,
Converter={x:Static ObjectConverters.IsNotNull}}" />
<u:TimePickerPresenter
Name="{x:Static u:TimePicker.PART_Presenter}"
NeedsConfirmation="{TemplateBinding NeedConfirmation}"

View File

@@ -62,18 +62,13 @@ public class TimePicker : TemplatedControl, IClearControl, IInnerContentControl,
public static readonly StyledProperty<bool> IsReadonlyProperty = AvaloniaProperty.Register<TimePicker, bool>(
nameof(IsReadonly));
public bool IsReadonly
{
get => GetValue(IsReadonlyProperty);
set => SetValue(IsReadonlyProperty, value);
}
private Button? _button;
private Popup? _popup;
private TimePickerPresenter? _presenter;
private TextBox? _textBox;
private Button? _button;
static TimePicker()
{
@@ -81,6 +76,12 @@ public class TimePicker : TemplatedControl, IClearControl, IInnerContentControl,
picker.OnSelectionChanged(args));
}
public bool IsReadonly
{
get => GetValue(IsReadonlyProperty);
set => SetValue(IsReadonlyProperty, value);
}
public bool IsDropdownOpen
{
get => GetValue(IsDropdownOpenProperty);
@@ -150,7 +151,7 @@ public class TimePicker : TemplatedControl, IClearControl, IInnerContentControl,
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
GotFocusEvent.RemoveHandler(OnTextBoxGetFocus, _textBox);
TextBox.TextChangedEvent.RemoveHandler(OnTextChanged, _textBox);
PointerPressedEvent.RemoveHandler(OnTextBoxPointerPressed, _textBox);
@@ -160,12 +161,12 @@ public class TimePicker : TemplatedControl, IClearControl, IInnerContentControl,
_popup = e.NameScope.Find<Popup>(PART_Popup);
_presenter = e.NameScope.Find<TimePickerPresenter>(PART_Presenter);
_button = e.NameScope.Find<Button>(PART_Button);
GotFocusEvent.AddHandler(OnTextBoxGetFocus, _textBox);
TextBox.TextChangedEvent.AddHandler(OnTextChanged, _textBox);
PointerPressedEvent.AddHandler(OnTextBoxPointerPressed, RoutingStrategies.Tunnel, false, _textBox);
Button.ClickEvent.AddHandler(OnButtonClick, _button);
SetCurrentValue(SelectedTimeProperty, DateTime.Now.TimeOfDay);
}
@@ -187,7 +188,11 @@ public class TimePicker : TemplatedControl, IClearControl, IInnerContentControl,
private void OnTextChanged(object? sender, TextChangedEventArgs e)
{
if (DisplayFormat is null || DisplayFormat.Length == 0)
if (string.IsNullOrEmpty(_textBox?.Text))
{
TimePickerPresenter.TimeProperty.SetValue(null, _presenter);
}
else if (DisplayFormat is null || DisplayFormat.Length == 0)
{
if (TimeSpan.TryParse(_textBox?.Text, out var defaultTime))
TimePickerPresenter.TimeProperty.SetValue(defaultTime, _presenter);
@@ -203,7 +208,13 @@ public class TimePicker : TemplatedControl, IClearControl, IInnerContentControl,
{
if (_textBox is null) return;
var time = args.NewValue.Value;
DateTime date = new DateTime(1, 1, 1, time?.Hours ?? 0, time?.Minutes ?? 0, time?.Seconds ?? 0);
if (time is null)
{
_textBox.Text = null;
return;
}
var date = new DateTime(1, 1, 1, time.Value.Hours, time.Value.Minutes, time.Value.Seconds);
var text = date.ToString(DisplayFormat);
_textBox.Text = text;
}
@@ -212,11 +223,13 @@ public class TimePicker : TemplatedControl, IClearControl, IInnerContentControl,
{
_presenter?.Confirm();
SetCurrentValue(IsDropdownOpenProperty, false);
Focus();
}
public void Dismiss()
{
SetCurrentValue(IsDropdownOpenProperty, false);
Focus();
}
protected override void UpdateDataValidation(AvaloniaProperty property, BindingValueType state, Exception? error)

View File

@@ -18,42 +18,73 @@ namespace Ursa.Controls;
[TemplatePart(PART_FirstSeparator, typeof(Control))]
[TemplatePart(PART_SecondSeparator, typeof(Control))]
[TemplatePart(PART_ThirdSeparator, typeof(Control))]
public class TimePickerPresenter: TemplatedControl
public class TimePickerPresenter : TemplatedControl
{
public const string PART_HourSelector = "PART_HourSelector";
public const string PART_MinuteSelector = "PART_MinuteSelector";
public const string PART_SecondSelector = "PART_SecondSelector";
public const string PART_AmPmSelector = "PART_AmPmSelector";
public const string PART_PickerContainer = "PART_PickerContainer";
public const string PART_HourScrollPanel = "PART_HourScrollPanel";
public const string PART_MinuteScrollPanel = "PART_MinuteScrollPanel";
public const string PART_SecondScrollPanel = "PART_SecondScrollPanel";
public const string PART_AmPmScrollPanel = "PART_AmPmScrollPanel";
public const string PART_FirstSeparator = "PART_FirstSeparator";
public const string PART_SecondSeparator = "PART_SecondSeparator";
public const string PART_ThirdSeparator = "PART_ThirdSeparator";
private DateTimePickerPanel? _hourSelector;
private DateTimePickerPanel? _minuteSelector;
private DateTimePickerPanel? _secondSelector;
private DateTimePickerPanel? _ampmSelector;
private Grid? _pickerContainer;
private Control? _hourScrollPanel;
private Control? _minuteScrollPanel;
private Control? _secondScrollPanel;
public static readonly StyledProperty<bool> NeedsConfirmationProperty =
AvaloniaProperty.Register<TimePickerPresenter, bool>(
nameof(NeedsConfirmation));
public static readonly StyledProperty<int> MinuteIncrementProperty =
AvaloniaProperty.Register<TimePickerPresenter, int>(
nameof(MinuteIncrement));
public static readonly StyledProperty<int> SecondIncrementProperty =
AvaloniaProperty.Register<TimePickerPresenter, int>(
nameof(SecondIncrement));
public static readonly StyledProperty<TimeSpan?> TimeProperty =
AvaloniaProperty.Register<TimePickerPresenter, TimeSpan?>(
nameof(Time));
public static readonly StyledProperty<string> PanelFormatProperty =
AvaloniaProperty.Register<TimePickerPresenter, string>(
nameof(PanelFormat), "HH mm ss t");
private Control? _ampmScrollPanel;
private DateTimePickerPanel? _ampmSelector;
private Control? _firstSeparator;
private Control? _hourScrollPanel;
private DateTimePickerPanel? _hourSelector;
private Control? _minuteScrollPanel;
private DateTimePickerPanel? _minuteSelector;
private Grid? _pickerContainer;
private Control? _secondScrollPanel;
private DateTimePickerPanel? _secondSelector;
private Control? _secondSeparator;
private Control? _thirdSeparator;
private bool _use12Clock;
private bool _updateFromTimeChange;
internal TimeSpan _timeHolder;
public static readonly StyledProperty<bool> NeedsConfirmationProperty = AvaloniaProperty.Register<TimePickerPresenter, bool>(
nameof(NeedsConfirmation));
private bool _updateFromTimeChange;
private bool _use12Clock;
static TimePickerPresenter()
{
PanelFormatProperty.Changed.AddClassHandler<TimePickerPresenter, string>((presenter, args) =>
presenter.OnPanelFormatChanged(args));
TimeProperty.Changed.AddClassHandler<TimePickerPresenter, TimeSpan?>((presenter, args) =>
presenter.OnTimeChanged(args));
}
public TimePickerPresenter()
{
SetCurrentValue(TimeProperty, DateTime.Now.TimeOfDay);
}
public bool NeedsConfirmation
{
@@ -61,54 +92,36 @@ public class TimePickerPresenter: TemplatedControl
set => SetValue(NeedsConfirmationProperty, value);
}
public static readonly StyledProperty<int> MinuteIncrementProperty = AvaloniaProperty.Register<TimePickerPresenter, int>(
nameof(MinuteIncrement));
public int MinuteIncrement
{
get => GetValue(MinuteIncrementProperty);
set => SetValue(MinuteIncrementProperty, value);
}
public static readonly StyledProperty<int> SecondIncrementProperty = AvaloniaProperty.Register<TimePickerPresenter, int>(
nameof(SecondIncrement));
public int SecondIncrement
{
get => GetValue(SecondIncrementProperty);
set => SetValue(SecondIncrementProperty, value);
}
public static readonly StyledProperty<TimeSpan?> TimeProperty = AvaloniaProperty.Register<TimePickerPresenter, TimeSpan?>(
nameof(Time));
public TimeSpan? Time
{
get => GetValue(TimeProperty);
set => SetValue(TimeProperty, value);
}
public static readonly StyledProperty<string> PanelFormatProperty = AvaloniaProperty.Register<TimePickerPresenter, string>(
nameof(PanelFormat), defaultValue: "HH mm ss t");
public string PanelFormat
{
get => GetValue(PanelFormatProperty);
set => SetValue(PanelFormatProperty, value);
}
public event EventHandler<TimePickerSelectedValueChangedEventArgs>? SelectedTimeChanged;
static TimePickerPresenter()
{
PanelFormatProperty.Changed.AddClassHandler<TimePickerPresenter, string>((presenter, args) => presenter.OnPanelFormatChanged(args));
TimeProperty.Changed.AddClassHandler<TimePickerPresenter, TimeSpan?>((presenter, args) => presenter.OnTimeChanged(args));
}
public event EventHandler<TimePickerSelectedValueChangedEventArgs>? SelectedTimeChanged;
private void OnTimeChanged(AvaloniaPropertyChangedEventArgs<TimeSpan?> args)
{
_updateFromTimeChange = true;
UpdatePanelsFromSelectedTime();
UpdatePanelsFromSelectedTime(args.NewValue.Value);
_updateFromTimeChange = false;
SelectedTimeChanged?.Invoke(this,
new TimePickerSelectedValueChangedEventArgs(args.OldValue.Value, args.NewValue.Value));
@@ -117,7 +130,7 @@ public class TimePickerPresenter: TemplatedControl
private void OnPanelFormatChanged(AvaloniaPropertyChangedEventArgs<string> args)
{
var format = args.NewValue.Value;
UpdatePanelLayout(format);
}
@@ -155,10 +168,11 @@ public class TimePickerPresenter: TemplatedControl
_ampmSelector?.SetValue(DateTimePickerPanel.ItemFormatProperty, part);
}
}
if (panels.Count < 1) return;
IsVisibleProperty.SetValue(false, _hourScrollPanel, _minuteScrollPanel, _secondScrollPanel, _ampmScrollPanel,
_firstSeparator, _secondSeparator, _thirdSeparator);
for(var i = 0; i< panels.Count; i++)
for (var i = 0; i < panels.Count; i++)
{
var panel = panels[i];
if (panel is null) continue;
@@ -169,56 +183,27 @@ public class TimePickerPresenter: TemplatedControl
0 => _firstSeparator,
1 => _secondSeparator,
2 => _thirdSeparator,
_ => null,
_ => null
};
if (i != panels.Count - 1) IsVisibleProperty.SetValue(true, separator);
}
}
public TimePickerPresenter()
{
SetCurrentValue(TimeProperty, DateTime.Now.TimeOfDay);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs 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;
}
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);
_minuteSelector = e.NameScope.Find<DateTimePickerPanel>(PART_MinuteSelector);
_secondSelector = e.NameScope.Find<DateTimePickerPanel>(PART_SecondSelector);
_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;
}
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);
_hourScrollPanel = e.NameScope.Find<Control>(PART_HourScrollPanel);
_minuteScrollPanel = e.NameScope.Find<Control>(PART_MinuteScrollPanel);
@@ -229,58 +214,46 @@ public class TimePickerPresenter: TemplatedControl
_thirdSeparator = e.NameScope.Find<Control>(PART_ThirdSeparator);
Initialize();
UpdatePanelLayout(PanelFormat);
UpdatePanelsFromSelectedTime();
UpdatePanelsFromSelectedTime(Time);
}
private void OnPanelSelectionChanged(object sender, System.EventArgs e)
{
if (_updateFromTimeChange) return;
TimeSpan time = NeedsConfirmation ? _timeHolder : Time ?? DateTime.Now.TimeOfDay;
int hour = _hourSelector?.SelectedValue ?? time.Hours;
int minute = _minuteSelector?.SelectedValue ?? time.Minutes;
int second = _secondSelector?.SelectedValue ?? time.Seconds;
int ampm = _ampmSelector?.SelectedValue ?? (time.Hours >= 12 ? 1 : 0);
var time = NeedsConfirmation ? _timeHolder : Time ?? DateTime.Now.TimeOfDay;
var hour = _hourSelector?.SelectedValue ?? time.Hours;
var minute = _minuteSelector?.SelectedValue ?? time.Minutes;
var second = _secondSelector?.SelectedValue ?? time.Seconds;
var ampm = _ampmSelector?.SelectedValue ?? (time.Hours >= 12 ? 1 : 0);
if (_use12Clock)
{
hour = ampm switch
{
0 when hour == 12 => 0,
1 when hour < 12 => hour + 12,
_ => hour
};
}
var newTime = new TimeSpan(hour, minute, second);
if (NeedsConfirmation)
{
_timeHolder = newTime;
}
else
{
SetCurrentValue(TimeProperty, newTime);
}
}
private void UpdatePanelsFromSelectedTime()
private void UpdatePanelsFromSelectedTime(TimeSpan? time)
{
if (Time is null) return;
var time = Time ?? DateTime.Now.TimeOfDay;
if (time is null) return;
if (_hourSelector is not null)
{
var index = _use12Clock ? time.Hours % 12 : time.Hours;
var index = _use12Clock ? time.Value.Hours % 12 : time.Value.Hours;
if (index == 0) index = 12;
_hourSelector.SelectedValue = index;
}
if (_minuteSelector is not null)
{
_minuteSelector.SelectedValue = time.Minutes;
}
if (_secondSelector is not null)
{
_secondSelector.SelectedValue = time.Seconds;
}
if (_minuteSelector is not null) _minuteSelector.SelectedValue = time.Value.Minutes;
if (_secondSelector is not null) _secondSelector.SelectedValue = time.Value.Seconds;
if (_ampmSelector is not null)
{
_ampmSelector.SelectedValue = time.Hours switch
_ampmSelector.SelectedValue = time.Value.Hours switch
{
>= 12 => 1,
_ => 0
@@ -297,23 +270,23 @@ public class TimePickerPresenter: TemplatedControl
_hourSelector.ItemFormat = "hh";
_hourSelector.MaximumValue = _use12Clock ? 12 : 23;
_hourSelector.MinimumValue = _use12Clock ? 1 : 0;
}
if(_minuteSelector is not null)
if (_minuteSelector is not null)
{
_minuteSelector.ItemFormat = "mm";
_minuteSelector.MaximumValue = 59;
_minuteSelector.MinimumValue = 0;
}
if(_secondSelector is not null)
if (_secondSelector is not null)
{
_secondSelector.ItemFormat = "mm";
_secondSelector.MaximumValue = 59;
_secondSelector.MinimumValue = 0;
}
if(_ampmSelector is not null)
if (_ampmSelector is not null)
{
_ampmSelector.ItemFormat = "t";
_ampmSelector.MaximumValue = 1;
@@ -323,9 +296,6 @@ public class TimePickerPresenter: TemplatedControl
public void Confirm()
{
if (NeedsConfirmation)
{
SetCurrentValue(TimeProperty, _timeHolder);
}
if (NeedsConfirmation) SetCurrentValue(TimeProperty, _timeHolder);
}
}