feat: update pagination functionality, start building themes.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user