feat: update pagination functionality, start building themes.

This commit is contained in:
rabbitism
2023-06-21 19:29:42 +08:00
parent e0dfd64681
commit 105c996af4
11 changed files with 239 additions and 148 deletions

View File

@@ -8,6 +8,11 @@ using Avalonia.Styling;
namespace Ursa.Controls;
/// <summary>
/// Pagination is a control that displays a series of buttons that can be used to navigate to pages.
/// CurrentPage starts from 1.
/// Pagination only stores an approximate index internally.
/// </summary>
[TemplatePart(PART_PreviousButton, typeof(Button))]
[TemplatePart(PART_NextButton, typeof(Button))]
[TemplatePart(PART_ButtonPanel, typeof(StackPanel))]
@@ -21,14 +26,9 @@ public class Pagination: TemplatedControl
private Button? _previousButton;
private Button? _nextButton;
private StackPanel? _buttonPanel;
private readonly PaginationButton[] _buttons = new PaginationButton[7];
private ComboBox? _sizeChangerComboBox;
/// <summary>
/// To reduce allocation, there are maximum of 7 buttons and 2 selection controls. it will be reused.
/// </summary>
private PaginationExpandButton? _leftSelection;
private PaginationExpandButton? _rightSelection;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
@@ -40,9 +40,8 @@ public class Pagination: TemplatedControl
_sizeChangerComboBox = e.NameScope.Find<ComboBox>(PART_SizeChangerComboBox);
if (_previousButton != null) _previousButton.Click += OnButtonClick;
if (_nextButton != null) _nextButton.Click += OnButtonClick;
_leftSelection = new PaginationExpandButton();
_rightSelection = new PaginationExpandButton();
UpdateButtons();
InitializePanelButtons();
UpdateButtons(0);
}
@@ -111,21 +110,20 @@ public class Pagination: TemplatedControl
set => SetValue(PageButtonThemeProperty, value);
}
public static readonly StyledProperty<ControlTheme> ExpandButtonThemeProperty = AvaloniaProperty.Register<Pagination, ControlTheme>(
nameof(ExpandButtonTheme));
public ControlTheme ExpandButtonTheme
{
get => GetValue(ExpandButtonThemeProperty);
set => SetValue(ExpandButtonThemeProperty, value);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == PageSizeProperty || change.Property == TotalCountProperty)
// When page size is updated, the selected current page must be updated.
if (change.Property == PageSizeProperty)
{
UpdateButtons();
int oldPageSize = change.GetOldValue<int>();
int index = oldPageSize * CurrentPage;
UpdateButtons(index);
}
else if (change.Property == TotalCountProperty || change.Property == CurrentPageProperty)
{
int index = PageSize * CurrentPage;
UpdateButtons(index);
}
}
@@ -133,18 +131,60 @@ public class Pagination: TemplatedControl
{
if (Equals(sender, _previousButton))
{
CurrentPage--;
AddCurrentPage(-1);
}
else
{
CurrentPage++;
AddCurrentPage(1);
}
}
private void UpdateButtons()
private void InitializePanelButtons()
{
if (_buttonPanel is null) return;
_buttonPanel.Children.Clear();
for (int i = 1; i <= 7; i++)
{
var button = new PaginationButton() { Page = i, IsVisible = true };
_buttonPanel.Children.Add(button);
_buttons![i - 1] = button;
button.Click+= OnPageButtonClick;
}
}
private void OnPageButtonClick(object sender, RoutedEventArgs args)
{
if (sender is PaginationButton pageButton)
{
if (pageButton.IsLeftForward)
{
AddCurrentPage(-5);
}
else if (pageButton.IsRightForward)
{
AddCurrentPage(5);
}
else
{
CurrentPage = pageButton.Page;
}
}
}
private void AddCurrentPage(int pageChange)
{
int newValue = CurrentPage + pageChange;
if (newValue <= 0) newValue = 1;
else if(newValue>=PageCount) newValue = PageCount;
CurrentPage = newValue;
}
private void UpdateButtons(int index)
{
if (_buttonPanel is null) return;
if (PageSize == 0) return;
int currentIndex = CurrentPage * PageSize;
int currentIndex = index;
int pageCount = TotalCount / PageSize;
int residue = TotalCount % PageSize;
@@ -152,18 +192,71 @@ public class Pagination: TemplatedControl
{
pageCount++;
}
_buttonPanel?.Children.Clear();
for (int i = 0; i < pageCount; i++)
PageCount = pageCount;
int currentPage = currentIndex/ PageSize;
if (currentPage == 0) currentPage++;
if (pageCount <= 7)
{
if (i == 1 && _leftSelection is not null)
for (int i = 0; i < 7; i++)
{
_leftSelection.Pages = new AvaloniaList<int>() { PageSize + 1, PageSize + 2, PageSize + 3 };
_buttonPanel?.Children.Add(_leftSelection);
if (i < pageCount)
{
_buttons[i].IsVisible = true;
_buttons[i].SetStatus(i + 1, i+1 == CurrentPage, false, false);
}
else
{
_buttons[i].IsVisible = false;
}
}
}
else
{
for (int i = 0; i < 7; i++)
{
_buttons[i].IsVisible = true;
}
int mid = currentPage;
if (mid < 4) mid = 4;
else if (mid > pageCount - 3) mid = pageCount - 3;
_buttons[3].Page = mid;
_buttons[2].Page = mid - 1;
_buttons[4].Page = mid + 1;
_buttons[0].Page = 1;
_buttons[6].Page = pageCount;
if(mid>4)
{
_buttons[1].SetStatus(0, false, true, false);
}
else
{
_buttonPanel?.Children.Add(new PaginationButton { Content = i + 1, Page = i + 1 });
_buttons[1].SetStatus(mid-2, false, false, false);
}
if(mid<pageCount-3)
{
_buttons[5].SetStatus(0, false, false, true);
}
else
{
_buttons[5].SetStatus(mid+2, false, false, false);
}
foreach (var button in _buttons)
{
if (button.Page == currentPage)
{
button.SetSelected(true);
}
else
{
button.SetSelected(false);
}
}
}
CurrentPage = currentPage;
}
}

View File

@@ -1,14 +1,41 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Styling;
namespace Ursa.Controls;
[PseudoClasses(PC_Left, PC_Right, PC_Selected)]
public class PaginationButton: Button, IStyleable
{
Type IStyleable.StyleKey => typeof(Button);
public int Page { get; set; }
public const string PC_Left = ":left";
public const string PC_Right = ":right";
public const string PC_Selected = ":selected";
public static readonly StyledProperty<int> PageProperty = AvaloniaProperty.Register<PaginationButton, int>(
nameof(Page));
public int Page
{
get => GetValue(PageProperty);
set => SetValue(PageProperty, value);
}
public bool IsLeftForward { get; private set; }
public bool IsRightForward { get; private set; }
internal void SetStatus(int page, bool isSelected, bool isLeft, bool isRight)
{
PseudoClasses.Set(PC_Selected, isSelected);
PseudoClasses.Set(PC_Left, isLeft);
PseudoClasses.Set(PC_Right, isRight);
IsLeftForward = isLeft;
IsRightForward = isRight;
Page = page;
}
internal void SetSelected(bool isSelected)
{
PseudoClasses.Set(PC_Selected, isSelected);
}
}

View File

@@ -1,79 +0,0 @@
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
namespace Ursa.Controls;
[TemplatePart(PART_Button, typeof(Button))]
[TemplatePart(PART_Popup, typeof(Popup))]
public class PaginationExpandButton: TemplatedControl
{
public const string PART_Button = "PART_Button";
public const string PART_Popup = "PART_Popup";
private Popup? _popup;
private Button? _button;
public static readonly StyledProperty<AvaloniaList<int>> PagesProperty = AvaloniaProperty.Register<PaginationExpandButton, AvaloniaList<int>>(
nameof(Pages));
public AvaloniaList<int> Pages
{
get => GetValue(PagesProperty);
set => SetValue(PagesProperty, value);
}
public static readonly StyledProperty<bool> IsDropdownOpenProperty = AvaloniaProperty.Register<PaginationExpandButton, bool>(
nameof(IsDropdownOpen));
public bool IsDropdownOpen
{
get => GetValue(IsDropdownOpenProperty);
set => SetValue(IsDropdownOpenProperty, value);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_popup = e.NameScope.Find<Popup>(PART_Popup);
_button = e.NameScope.Find<Button>(PART_Button);
}
protected override void OnPointerEntered(PointerEventArgs e)
{
if (!e.Handled && e.Source is Visual source )
{
SetCurrentValue(IsDropdownOpenProperty, true);
e.Handled = true;
}
base.OnPointerEntered(e);
}
protected override void OnPointerExited(PointerEventArgs e)
{
base.OnPointerExited(e);
// IsDropdownOpen = false;
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
if (!e.Handled && e.Source is Visual source)
{
if (_popup?.IsInsidePopup(source) == true)
{
SetCurrentValue(IsDropdownOpenProperty, false);
e.Handled = true;
}
else
{
//SetCurrentValue(IsDropdownOpenProperty, !IsDropdownOpen);
SetCurrentValue(IsDropdownOpenProperty, false);
e.Handled = true;
}
}
base.OnPointerReleased(e);
}
}