diff --git a/demo/Ursa.Demo/Pages/PaginationDemo.axaml b/demo/Ursa.Demo/Pages/PaginationDemo.axaml index 0037750..602c2da 100644 --- a/demo/Ursa.Demo/Pages/PaginationDemo.axaml +++ b/demo/Ursa.Demo/Pages/PaginationDemo.axaml @@ -13,11 +13,16 @@ - + + + + + diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs index aed7270..3c0765a 100644 --- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs +++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs @@ -34,7 +34,7 @@ public class MenuViewModel: ViewModelBase // new() { MenuHeader = "Number Displayer", Key = MenuKeys.MenuKeyNumberDisplayer, Status = "New" }, new() { MenuHeader = "Numeric UpDown", Key = MenuKeys.MenuKeyNumericUpDown }, new() { MenuHeader = "NumPad", Key = MenuKeys.MenuKeyNumPad, Status = "New" }, - new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination }, + new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination, Status = "Updated" }, new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider }, new() { MenuHeader = "Scroll To", Key = MenuKeys.MenuKeyScrollToButton, Status = "New" }, new() { MenuHeader = "Selection List", Key = MenuKeys.MenuKeySelectionList, Status = "New" }, diff --git a/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml b/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml index 31c1aa8..bb3cb95 100644 --- a/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml +++ b/src/Ursa.Themes.Semi/Controls/NumericUpDown.axaml @@ -35,6 +35,7 @@ MinWidth="0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" + ShowButtonSpinner="{TemplateBinding ShowButtonSpinner}" AllowSpin="{TemplateBinding AllowSpin}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" diff --git a/src/Ursa.Themes.Semi/Controls/Pagination.axaml b/src/Ursa.Themes.Semi/Controls/Pagination.axaml index 0eaa096..8b1446d 100644 --- a/src/Ursa.Themes.Semi/Controls/Pagination.axaml +++ b/src/Ursa.Themes.Semi/Controls/Pagination.axaml @@ -12,7 +12,7 @@ - + - + + + + + + - - - + + + diff --git a/src/Ursa.Themes.Semi/Locale/en-us.axaml b/src/Ursa.Themes.Semi/Locale/en-us.axaml index e6d2f69..9940ce5 100644 --- a/src/Ursa.Themes.Semi/Locale/en-us.axaml +++ b/src/Ursa.Themes.Semi/Locale/en-us.axaml @@ -10,4 +10,6 @@ Yes No Close + Jump to page + diff --git a/src/Ursa.Themes.Semi/Locale/zh-cn.axaml b/src/Ursa.Themes.Semi/Locale/zh-cn.axaml index c738313..d9a2c2b 100644 --- a/src/Ursa.Themes.Semi/Locale/zh-cn.axaml +++ b/src/Ursa.Themes.Semi/Locale/zh-cn.axaml @@ -10,4 +10,6 @@ 关闭 + 跳至 + diff --git a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs index a8dbd14..928fdfa 100644 --- a/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs +++ b/src/Ursa/Controls/NumericUpDown/NumericUpDownBase.cs @@ -110,6 +110,15 @@ public abstract class NumericUpDown : TemplatedControl, IClearControl set => SetValue(AllowSpinProperty, value); } + public static readonly StyledProperty ShowButtonSpinnerProperty = + ButtonSpinner.ShowButtonSpinnerProperty.AddOwner(); + + public bool ShowButtonSpinner + { + get => GetValue(ShowButtonSpinnerProperty); + set => SetValue(ShowButtonSpinnerProperty, value); + } + public event EventHandler? Spinned; static NumericUpDown() diff --git a/src/Ursa/Controls/Pagination/Pagination.cs b/src/Ursa/Controls/Pagination/Pagination.cs index f462496..a5bde53 100644 --- a/src/Ursa/Controls/Pagination/Pagination.cs +++ b/src/Ursa/Controls/Pagination/Pagination.cs @@ -3,8 +3,10 @@ using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; +using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Styling; +using Irihi.Avalonia.Shared.Helpers; namespace Ursa.Controls; @@ -16,39 +18,23 @@ namespace Ursa.Controls; [TemplatePart(PART_PreviousButton, typeof(PaginationButton))] [TemplatePart(PART_NextButton, typeof(PaginationButton))] [TemplatePart(PART_ButtonPanel, typeof(StackPanel))] -[TemplatePart(PART_SizeChangerComboBox, typeof(ComboBox))] +[TemplatePart(PART_QuickJumpInput, typeof(NumericIntUpDown))] public class Pagination: TemplatedControl { public const string PART_PreviousButton = "PART_PreviousButton"; public const string PART_NextButton = "PART_NextButton"; public const string PART_ButtonPanel = "PART_ButtonPanel"; - public const string PART_SizeChangerComboBox = "PART_SizeChangerComboBox"; + public const string PART_QuickJumpInput = "PART_QuickJumpInput"; private PaginationButton? _previousButton; private PaginationButton? _nextButton; private StackPanel? _buttonPanel; private readonly PaginationButton[] _buttons = new PaginationButton[7]; - private ComboBox? _sizeChangerComboBox; - - protected override void OnApplyTemplate(TemplateAppliedEventArgs e) - { - base.OnApplyTemplate(e); - if (_previousButton != null) _previousButton.Click -= OnButtonClick; - if (_nextButton != null) _nextButton.Click -= OnButtonClick; - _previousButton = e.NameScope.Find(PART_PreviousButton); - _nextButton = e.NameScope.Find(PART_NextButton); - _buttonPanel = e.NameScope.Find(PART_ButtonPanel); - _sizeChangerComboBox = e.NameScope.Find(PART_SizeChangerComboBox); - if (_previousButton != null) _previousButton.Click += OnButtonClick; - if (_nextButton != null) _nextButton.Click += OnButtonClick; - InitializePanelButtons(); - UpdateButtons(0); - - } + private NumericIntUpDown? _quickJumpInput; - public static readonly StyledProperty CurrentPageProperty = AvaloniaProperty.Register( + public static readonly StyledProperty CurrentPageProperty = AvaloniaProperty.Register( nameof(CurrentPage)); - public int CurrentPage + public int? CurrentPage { get => GetValue(CurrentPageProperty); set => SetValue(CurrentPageProperty, value); @@ -121,40 +107,85 @@ public class Pagination: TemplatedControl public static readonly StyledProperty ShowQuickJumpProperty = AvaloniaProperty.Register( nameof(ShowQuickJump)); - + public bool ShowQuickJump { get => GetValue(ShowQuickJumpProperty); set => SetValue(ShowQuickJumpProperty, value); } - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + static Pagination() { - base.OnPropertyChanged(change); - // When page size is updated, the selected current page must be updated. - if (change.Property == PageSizeProperty) + PageSizeProperty.Changed.AddClassHandler((pagination, args)=>pagination.OnPageSizeChanged(args)); + CurrentPageProperty.Changed.AddClassHandler((pagination, args) => + pagination.UpdateButtonsByCurrentPage(args.NewValue.Value)); + TotalCountProperty.Changed.AddClassHandler((pagination, args) => + pagination.UpdateButtonsByCurrentPage(pagination.CurrentPage)); + } + + private void OnPageSizeChanged(AvaloniaPropertyChangedEventArgs args) + { + int pageCount = TotalCount / args.NewValue.Value; + int residue = TotalCount % args.NewValue.Value; + if (residue > 0) { - int oldPageSize = change.GetOldValue(); - int index = oldPageSize * CurrentPage; - UpdateButtons(index); + pageCount++; } - else if (change.Property == TotalCountProperty || change.Property == CurrentPageProperty) + PageCount = pageCount; + if (CurrentPage > PageCount) { - int index = PageSize * CurrentPage; - UpdateButtons(index); + CurrentPage = null; } + UpdateButtonsByCurrentPage(CurrentPage); + } + + protected override void OnApplyTemplate(TemplateAppliedEventArgs e) + { + base.OnApplyTemplate(e); + + Button.ClickEvent.AddHandler(OnButtonClick, _previousButton, _nextButton); + _previousButton = e.NameScope.Find(PART_PreviousButton); + _nextButton = e.NameScope.Find(PART_NextButton); + _buttonPanel = e.NameScope.Find(PART_ButtonPanel); + Button.ClickEvent.AddHandler(OnButtonClick, _previousButton, _nextButton); + + KeyDownEvent.RemoveHandler(OnQuickJumpInputKeyDown, _quickJumpInput); + LostFocusEvent.RemoveHandler(OnQuickJumpInputLostFocus, _quickJumpInput); + _quickJumpInput = e.NameScope.Find(PART_QuickJumpInput); + KeyDownEvent.AddHandler(OnQuickJumpInputKeyDown, _quickJumpInput); + LostFocusEvent.AddHandler(OnQuickJumpInputLostFocus, _quickJumpInput); + + InitializePanelButtons(); + UpdateButtonsByCurrentPage(0); + } + + private void OnQuickJumpInputKeyDown(object sender, KeyEventArgs e) + { + if (e.Key is Key.Enter or Key.Return) + { + SyncQuickJumperValue(); + } + } + + private void OnQuickJumpInputLostFocus(object sender, RoutedEventArgs e) + { + SyncQuickJumperValue(); + } + + private void SyncQuickJumperValue() + { + if (_quickJumpInput is null) return; + var value = _quickJumpInput?.Value; + if (value is null) return; + value = Clamp(value.Value, 1, PageCount); + SetCurrentValue(CurrentPageProperty, value); + _quickJumpInput?.SetCurrentValue(NumericIntUpDown.ValueProperty, null); } private void OnButtonClick(object? sender, RoutedEventArgs e) { - if (Equals(sender, _previousButton)) - { - AddCurrentPage(-1); - } - else - { - AddCurrentPage(1); - } + var diff = Equals(sender, _previousButton) ? -1 : 1; + AddCurrentPage(diff); } private void InitializePanelButtons() @@ -166,7 +197,7 @@ public class Pagination: TemplatedControl var button = new PaginationButton() { Page = i, IsVisible = true }; _buttonPanel.Children.Add(button); _buttons![i - 1] = button; - button.Click+= OnPageButtonClick; + Button.ClickEvent.AddHandler(OnPageButtonClick, button); } } @@ -191,30 +222,32 @@ public class Pagination: TemplatedControl private void AddCurrentPage(int pageChange) { - int newValue = CurrentPage + pageChange; - if (newValue <= 0) newValue = 1; - else if(newValue>=PageCount) newValue = PageCount; - CurrentPage = newValue; + int newValue = (CurrentPage ?? 0) + pageChange; + newValue = Clamp(newValue, 1, PageCount); + ; SetCurrentValue(CurrentPageProperty, newValue); } - - private void UpdateButtons(int index) + + private int Clamp(int value, int min, int max) + { + return value < min ? min : value > max ? max : value; + } + + /// + /// Update Button Content and Visibility by current page. + /// + /// + private void UpdateButtonsByCurrentPage(int? page) { if (_buttonPanel is null) return; if (PageSize == 0) return; - int currentIndex = index; - + int? currentPage = CurrentPage; int pageCount = TotalCount / PageSize; int residue = TotalCount % PageSize; if (residue > 0) { pageCount++; } - - PageCount = pageCount; - - int currentPage = currentIndex/ PageSize; - if (currentPage == 0) currentPage++; if (pageCount <= 7) { @@ -237,9 +270,8 @@ public class Pagination: TemplatedControl { _buttons[i].IsVisible = true; } - int mid = currentPage; - if (mid < 4) mid = 4; - else if (mid > pageCount - 3) mid = pageCount - 3; + int mid = currentPage ?? 0; + mid = Clamp(mid, 4, pageCount - 3); _buttons[3].Page = mid; _buttons[2].Page = mid - 1; _buttons[4].Page = mid + 1; @@ -247,7 +279,7 @@ public class Pagination: TemplatedControl _buttons[6].Page = pageCount; if(mid>4) { - _buttons[1].SetStatus(0, false, true, false); + _buttons[1].SetStatus(-1, false, true, false); } else { @@ -255,7 +287,7 @@ public class Pagination: TemplatedControl } if(mid 1; - if( _nextButton!=null) _nextButton.IsEnabled = CurrentPage < PageCount; + PageCount = pageCount; + SetCurrentValue(CurrentPageProperty, currentPage); + if (_previousButton != null) _previousButton.IsEnabled = (CurrentPage??int.MaxValue) > 1; + if (_nextButton != null) _nextButton.IsEnabled = (CurrentPage ?? 0) < PageCount; } } \ No newline at end of file