diff --git a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml index 28ae7c8..ffbd131 100644 --- a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml +++ b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml @@ -1,24 +1,28 @@ - + - - - - - - - - - - - - + + + + + + + - + \ No newline at end of file diff --git a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml.cs b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml.cs index 74214c7..4bb0d49 100644 --- a/demo/Ursa.Demo/Pages/DatePickerDemo.axaml.cs +++ b/demo/Ursa.Demo/Pages/DatePickerDemo.axaml.cs @@ -10,14 +10,4 @@ public partial class DatePickerDemo : UserControl { InitializeComponent(); } - - private void CalendarView_OnOnDateSelected(object? _, CalendarDayButtonEventArgs e) - { - Debug.WriteLine("Pressed: "+ e.Date?.ToLongDateString()); - } - - private void CalendarView_OnOnDatePreviewed(object? _, CalendarDayButtonEventArgs e) - { - Debug.WriteLine("Hovered: "+e.Date?.ToLongDateString()); - } } \ No newline at end of file diff --git a/demo/Ursa.Demo/Pages/DateRangePickerDemo.axaml b/demo/Ursa.Demo/Pages/DateRangePickerDemo.axaml new file mode 100644 index 0000000..f4f2071 --- /dev/null +++ b/demo/Ursa.Demo/Pages/DateRangePickerDemo.axaml @@ -0,0 +1,37 @@ + + + + + + + + + + + + diff --git a/demo/Ursa.Demo/Pages/DateRangePickerDemo.axaml.cs b/demo/Ursa.Demo/Pages/DateRangePickerDemo.axaml.cs new file mode 100644 index 0000000..56acc29 --- /dev/null +++ b/demo/Ursa.Demo/Pages/DateRangePickerDemo.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Ursa.Demo.Pages; + +public partial class DateRangePickerDemo : UserControl +{ + public DateRangePickerDemo() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/Pages/TimePickerDemo.axaml b/demo/Ursa.Demo/Pages/TimePickerDemo.axaml index 4b849fd..9ffc7fe 100644 --- a/demo/Ursa.Demo/Pages/TimePickerDemo.axaml +++ b/demo/Ursa.Demo/Pages/TimePickerDemo.axaml @@ -43,10 +43,6 @@ InnerRightContent="截止" NeedConfirmation="True" PanelFormat="{Binding #panelFormat.Text}" /> - - diff --git a/demo/Ursa.Demo/Pages/TimeRangePickerDemo.axaml b/demo/Ursa.Demo/Pages/TimeRangePickerDemo.axaml new file mode 100644 index 0000000..7d98aec --- /dev/null +++ b/demo/Ursa.Demo/Pages/TimeRangePickerDemo.axaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + diff --git a/demo/Ursa.Demo/Pages/TimeRangePickerDemo.axaml.cs b/demo/Ursa.Demo/Pages/TimeRangePickerDemo.axaml.cs new file mode 100644 index 0000000..564a507 --- /dev/null +++ b/demo/Ursa.Demo/Pages/TimeRangePickerDemo.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Ursa.Demo.Pages; + +public partial class TimeRangePickerDemo : UserControl +{ + public TimeRangePickerDemo() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/DatePickerDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/DatePickerDemoViewModel.cs index 693ab0f..a1bf3c4 100644 --- a/demo/Ursa.Demo/ViewModels/DatePickerDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/DatePickerDemoViewModel.cs @@ -7,22 +7,9 @@ namespace Ursa.Demo.ViewModels; public partial class DatePickerDemoViewModel: ObservableObject { [ObservableProperty] private DateTime? _selectedDate; - [ObservableProperty] private DateTime? _startDate; - [ObservableProperty] private DateTime? _endDate; public DatePickerDemoViewModel() { SelectedDate = DateTime.Today; - StartDate = DateTime.Today; - EndDate = DateTime.Today.AddDays(7); - } - - protected override void OnPropertyChanged(PropertyChangedEventArgs e) - { - base.OnPropertyChanged(e); - if (e.PropertyName == nameof(SelectedDate)) - { - - } } } \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/DateRangePickerDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/DateRangePickerDemoViewModel.cs new file mode 100644 index 0000000..1bcaa2c --- /dev/null +++ b/demo/Ursa.Demo/ViewModels/DateRangePickerDemoViewModel.cs @@ -0,0 +1,16 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Ursa.Demo.ViewModels; + +public partial class DateRangePickerDemoViewModel: ObservableObject +{ + [ObservableProperty] private DateTime? _startDate; + [ObservableProperty] private DateTime? _endDate; + + public DateRangePickerDemoViewModel() + { + StartDate = DateTime.Today; + EndDate = DateTime.Today.AddDays(7); + } +} \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs index 2abaaf4..161df53 100644 --- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs @@ -41,6 +41,7 @@ public partial class MainViewViewModel : ViewModelBase MenuKeys.MenuKeyClassInput => new ClassInputDemoViewModel(), MenuKeys.MenuKeyClock => new ClockDemoViewModel(), MenuKeys.MenuKeyDatePicker => new DatePickerDemoViewModel(), + MenuKeys.MenuKeyDateRangePicker => new DateRangePickerDemoViewModel(), MenuKeys.MenuKeyDateTimePicker => new DateTimePickerDemoViewModel(), MenuKeys.MenuKeyDialog => new DialogDemoViewModel(), MenuKeys.MenuKeyDisableContainer => new DisableContainerDemoViewModel(), @@ -75,6 +76,7 @@ public partial class MainViewViewModel : ViewModelBase MenuKeys.MenuKeyTimeBox => new TimeBoxDemoViewModel(), MenuKeys.MenuKeyTimeline => new TimelineDemoViewModel(), MenuKeys.MenuKeyTimePicker => new TimePickerDemoViewModel(), + MenuKeys.MenuKeyTimeRangePicker => new TimeRangePickerDemoViewModel(), MenuKeys.MenuKeyToast => new ToastDemoViewModel(), MenuKeys.MenuKeyToolBar => new ToolBarDemoViewModel(), MenuKeys.MenuKeyTreeComboBox => new TreeComboBoxDemoViewModel(), diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs index b044516..fde221b 100644 --- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs @@ -52,10 +52,12 @@ public class MenuViewModel : ViewModelBase { MenuHeader = "Date & Time", Children = new ObservableCollection { - new() { MenuHeader = "Date Picker", Key = MenuKeys.MenuKeyDatePicker }, - new() { MenuHeader = "Date Time Picker", Key = MenuKeys.MenuKeyDateTimePicker }, + new() { MenuHeader = "Date Picker", Key = MenuKeys.MenuKeyDatePicker, Status = "Updated" }, + new() { MenuHeader = "Date Range Picker", Key = MenuKeys.MenuKeyDateRangePicker, Status = "Updated" }, + new() { MenuHeader = "Date Time Picker", Key = MenuKeys.MenuKeyDateTimePicker, Status = "Updated" }, new() { MenuHeader = "Time Box", Key = MenuKeys.MenuKeyTimeBox }, - new() { MenuHeader = "TimePicker", Key = MenuKeys.MenuKeyTimePicker }, + new() { MenuHeader = "Time Picker", Key = MenuKeys.MenuKeyTimePicker, Status = "Updated" }, + new() { MenuHeader = "Time Range Picker", Key = MenuKeys.MenuKeyTimeRangePicker, Status = "Updated" }, new() { MenuHeader = "Clock", Key = MenuKeys.MenuKeyClock } } }, @@ -108,6 +110,7 @@ public static class MenuKeys public const string MenuKeyClassInput = "Class Input"; public const string MenuKeyClock = "Clock"; public const string MenuKeyDatePicker = "DatePicker"; + public const string MenuKeyDateRangePicker = "DateRangePicker"; public const string MenuKeyDateTimePicker = "DateTimePicker"; public const string MenuKeyDialog = "Dialog"; public const string MenuKeyDisableContainer = "DisableContainer"; @@ -142,6 +145,7 @@ public static class MenuKeys public const string MenuKeyTimeBox = "TimeBox"; public const string MenuKeyTimeline = "Timeline"; public const string MenuKeyTimePicker = "TimePicker"; + public const string MenuKeyTimeRangePicker = "TimeRangePicker"; public const string MenuKeyToast = "Toast"; public const string MenuKeyToolBar = "ToolBar"; public const string MenuKeyTreeComboBox = "TreeComboBox"; diff --git a/demo/Ursa.Demo/ViewModels/TimePickerDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/TimePickerDemoViewModel.cs index 7b93e1a..fed5690 100644 --- a/demo/Ursa.Demo/ViewModels/TimePickerDemoViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/TimePickerDemoViewModel.cs @@ -6,13 +6,9 @@ namespace Ursa.Demo.ViewModels; public partial class TimePickerDemoViewModel: ObservableObject { [ObservableProperty] private TimeSpan? _time; - [ObservableProperty] private TimeSpan? _startTime; - [ObservableProperty] private TimeSpan? _endTime; - + public TimePickerDemoViewModel() { Time = new TimeSpan(12, 20, 0); - StartTime = new TimeSpan(8, 21, 0); - EndTime = new TimeSpan(18, 22, 0); } } \ No newline at end of file diff --git a/demo/Ursa.Demo/ViewModels/TimeRangePickerDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/TimeRangePickerDemoViewModel.cs new file mode 100644 index 0000000..5369eaf --- /dev/null +++ b/demo/Ursa.Demo/ViewModels/TimeRangePickerDemoViewModel.cs @@ -0,0 +1,16 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Ursa.Demo.ViewModels; + +public partial class TimeRangePickerDemoViewModel: ObservableObject +{ + [ObservableProperty] private TimeSpan? _startTime; + [ObservableProperty] private TimeSpan? _endTime; + + public TimeRangePickerDemoViewModel() + { + StartTime = new TimeSpan(8, 21, 0); + EndTime = new TimeSpan(18, 22, 0); + } +} \ No newline at end of file diff --git a/src/Ursa.Themes.Semi/Controls/DateRangePicker.axaml b/src/Ursa.Themes.Semi/Controls/DateRangePicker.axaml index c9ba2ed..db9c77b 100644 --- a/src/Ursa.Themes.Semi/Controls/DateRangePicker.axaml +++ b/src/Ursa.Themes.Semi/Controls/DateRangePicker.axaml @@ -26,7 +26,7 @@ BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}" /> - + - diff --git a/src/Ursa/Controls/DateTimePicker/DatePicker.cs b/src/Ursa/Controls/DateTimePicker/DatePicker.cs index 03f673f..256cb7a 100644 --- a/src/Ursa/Controls/DateTimePicker/DatePicker.cs +++ b/src/Ursa/Controls/DateTimePicker/DatePicker.cs @@ -129,26 +129,7 @@ public class DatePicker: DatePickerBase, IClearControl } else { - if (DateTime.TryParseExact(_textBox?.Text, DisplayFormat, CultureInfo.CurrentUICulture, DateTimeStyles.None, - out var date)) - { - SetCurrentValue(SelectedDateProperty, date); - if (_calendar is not null) - { - _calendar.ContextDate = _calendar.ContextDate.With(year: date.Year, month: date.Month); - _calendar.UpdateDayButtons(); - } - _calendar?.MarkDates(startDate: date, endDate: date); - } - else - { - SetCurrentValue(SelectedDateProperty, null); - if (!fromText) - { - _textBox?.SetValue(TextBox.TextProperty, null); - } - _calendar?.ClearSelection(); - } + CommitInput(!fromText); } } @@ -192,6 +173,13 @@ public class DatePicker: DatePickerBase, IClearControl case Key.Tab: SetCurrentValue(IsDropdownOpenProperty, false); return; + case Key.Enter: + { + SetCurrentValue(IsDropdownOpenProperty, false); + CommitInput(true); + e.Handled = true; + return; + } default: base.OnKeyDown(e); break; @@ -219,6 +207,7 @@ public class DatePicker: DatePickerBase, IClearControl { return; } + CommitInput(true); SetCurrentValue(IsDropdownOpenProperty, false); } @@ -230,12 +219,33 @@ public class DatePicker: DatePickerBase, IClearControl if (hasFocus) { - if (!wasFocused && _textBox != null) { _textBox.Focus(); - _textBox.SelectAll(); } } } + + private void CommitInput(bool clearWhenInvalid) + { + if (DateTime.TryParseExact(_textBox?.Text, DisplayFormat, CultureInfo.CurrentUICulture, DateTimeStyles.None, + out var date)) + { + SetCurrentValue(SelectedDateProperty, date); + if (_calendar is not null) + { + _calendar.ContextDate = _calendar.ContextDate.With(year: date.Year, month: date.Month); + _calendar.UpdateDayButtons(); + } + _calendar?.MarkDates(startDate: date, endDate: date); + } + else + { + if (clearWhenInvalid) + { + SetCurrentValue(SelectedDateProperty, null); + } + _calendar?.ClearSelection(); + } + } } \ No newline at end of file diff --git a/src/Ursa/Controls/DateTimePicker/DateRangePicker.cs b/src/Ursa/Controls/DateTimePicker/DateRangePicker.cs index 56968f7..2bef08b 100644 --- a/src/Ursa/Controls/DateTimePicker/DateRangePicker.cs +++ b/src/Ursa/Controls/DateTimePicker/DateRangePicker.cs @@ -9,6 +9,7 @@ using Avalonia.Interactivity; using Irihi.Avalonia.Shared.Common; using Irihi.Avalonia.Shared.Contracts; using Irihi.Avalonia.Shared.Helpers; +using Calendar = Avalonia.Controls.Calendar; namespace Ursa.Controls; @@ -53,6 +54,7 @@ public class DateRangePicker : DatePickerBase, IClearControl static DateRangePicker() { + FocusableProperty.OverrideDefaultValue(true); SelectedStartDateProperty.Changed.AddClassHandler((picker, args) => picker.OnSelectionChanged(args)); SelectedEndDateProperty.Changed.AddClassHandler((picker, args) => @@ -279,7 +281,6 @@ public class DateRangePicker : DatePickerBase, IClearControl private void OnButtonClick(object? sender, RoutedEventArgs e) { - Focus(NavigationMethod.Pointer); SetCurrentValue(IsDropdownOpenProperty, !IsDropdownOpen); _startTextBox?.Focus(); // _start = true; @@ -456,9 +457,111 @@ public class DateRangePicker : DatePickerBase, IClearControl } return; } + case Key.Enter: + { + SetCurrentValue(IsDropdownOpenProperty, false); + CommitInput(true); + e.Handled = true; + return; + } default: base.OnKeyDown(e); break; } } + + 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; + } + + if (element == _startTextBox || element == _endTextBox) + { + return; + } + CommitInput(true); + SetCurrentValue(IsDropdownOpenProperty, false); + } + + private bool _isFocused; + private void FocusChanged(bool hasFocus) + { + bool wasFocused = _isFocused; + _isFocused = hasFocus; + + if (hasFocus) + { + if (!wasFocused && _startTextBox != null) + { + _startTextBox.Focus(); + _start = true; + } + } + } + + private void CommitInput(bool clearWhenInvalid) + { + DateTime? startDate = null; + DateTime? endDate = null; + if (DateTime.TryParseExact(_startTextBox?.Text, DisplayFormat, CultureInfo.CurrentUICulture, DateTimeStyles.None, + out var localStartDate)) + { + startDate = localStartDate; + SetCurrentValue(SelectedStartDateProperty, localStartDate); + if (_startCalendar is not null) + { + _startCalendar.ContextDate = _startCalendar.ContextDate.With(year: localStartDate.Year, month: localStartDate.Month); + _startCalendar.UpdateDayButtons(); + } + } + else + { + if (clearWhenInvalid) + { + SetCurrentValue(SelectedStartDateProperty, null); + } + + } + if (DateTime.TryParseExact(_endTextBox?.Text, DisplayFormat, CultureInfo.CurrentUICulture, DateTimeStyles.None, + out var localEndDate)) + { + endDate = localEndDate; + SetCurrentValue(SelectedEndDateProperty, localEndDate); + if (_endCalendar is not null) + { + _endCalendar.ContextDate = _endCalendar.ContextDate.With(year: localEndDate.Year, month: localEndDate.Month); + _endCalendar.UpdateDayButtons(); + } + } + else + { + if (clearWhenInvalid) + { + SetCurrentValue(SelectedEndDateProperty, null); + } + } + if (startDate is null || endDate is null) + { + _startCalendar?.ClearSelection(); + _endCalendar?.ClearSelection(); + } + else + { + _startCalendar?.MarkDates(startDate: startDate, endDate: endDate); + _endCalendar?.MarkDates(startDate: startDate, endDate: endDate); + } + } + } \ No newline at end of file diff --git a/src/Ursa/Controls/DateTimePicker/DateTimePicker.cs b/src/Ursa/Controls/DateTimePicker/DateTimePicker.cs index 22793ac..dca00aa 100644 --- a/src/Ursa/Controls/DateTimePicker/DateTimePicker.cs +++ b/src/Ursa/Controls/DateTimePicker/DateTimePicker.cs @@ -41,10 +41,12 @@ public class DateTimePicker : DatePickerBase private Button? _button; private CalendarView? _calendar; private TextBox? _textBox; + private Popup? _popup; private TimePickerPresenter? _timePickerPresenter; static DateTimePicker() { + FocusableProperty.OverrideDefaultValue(true); DisplayFormatProperty.OverrideDefaultValue(CultureInfo.InvariantCulture.DateTimeFormat.FullDateTimePattern); SelectedDateProperty.Changed.AddClassHandler((picker, args) => picker.OnSelectionChanged(args)); @@ -102,19 +104,17 @@ public class DateTimePicker : DatePickerBase base.OnApplyTemplate(e); GotFocusEvent.RemoveHandler(OnTextBoxGetFocus, _textBox); TextBox.TextChangedEvent.RemoveHandler(OnTextChanged, _textBox); - PointerPressedEvent.RemoveHandler(OnTextBoxPointerPressed, _textBox); Button.ClickEvent.RemoveHandler(OnButtonClick, _button); CalendarView.DateSelectedEvent.RemoveHandler(OnDateSelected, _calendar); TimePickerPresenter.SelectedTimeChangedEvent.RemoveHandler(OnTimeSelectedChanged, _timePickerPresenter); _button = e.NameScope.Find