mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 21:38:38 +00:00
174 lines
5.5 KiB
C#
174 lines
5.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Metadata;
|
|
using Avalonia.Controls.Primitives;
|
|
using Avalonia.Input;
|
|
using Avalonia.Interactivity;
|
|
using ReactiveUI;
|
|
|
|
namespace Artemis.UI.Shared.Pagination;
|
|
|
|
/// <summary>
|
|
/// Represents a pagination control that can be used to switch between pages.
|
|
/// </summary>
|
|
[TemplatePart("PART_PreviousButton", typeof(Button))]
|
|
[TemplatePart("PART_NextButton", typeof(Button))]
|
|
[TemplatePart("PART_PagesView", typeof(StackPanel))]
|
|
public partial class Pagination : TemplatedControl
|
|
{
|
|
/// <inheritdoc />
|
|
public Pagination()
|
|
{
|
|
PropertyChanged += OnPropertyChanged;
|
|
}
|
|
|
|
public Button? PreviousButton { get; set; }
|
|
public Button? NextButton { get; set; }
|
|
public StackPanel? PagesView { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
|
{
|
|
if (PreviousButton != null)
|
|
PreviousButton.Click -= PreviousButtonOnClick;
|
|
if (NextButton != null)
|
|
NextButton.Click -= NextButtonOnClick;
|
|
|
|
PreviousButton = e.NameScope.Find<Button>("PART_PreviousButton");
|
|
NextButton = e.NameScope.Find<Button>("PART_NextButton");
|
|
PagesView = e.NameScope.Find<StackPanel>("PART_PagesView");
|
|
|
|
if (PreviousButton != null)
|
|
PreviousButton.Click += PreviousButtonOnClick;
|
|
if (NextButton != null)
|
|
NextButton.Click += NextButtonOnClick;
|
|
|
|
Update();
|
|
}
|
|
|
|
private void OnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
|
{
|
|
if (e.Property == ValueProperty || e.Property == MaximumProperty)
|
|
Update();
|
|
}
|
|
|
|
private void NextButtonOnClick(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (Value < Maximum)
|
|
Value++;
|
|
}
|
|
|
|
private void PreviousButtonOnClick(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (Value > 1)
|
|
Value--;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (PagesView == null)
|
|
return;
|
|
|
|
List<int> pages = GetPages(Value, Maximum);
|
|
|
|
// Remove extra children
|
|
while (PagesView.Children.Count > pages.Count)
|
|
{
|
|
PagesView.Children.RemoveAt(PagesView.Children.Count - 1);
|
|
}
|
|
|
|
if (PagesView.Children.Count > pages.Count)
|
|
PagesView.Children.RemoveRange(0, PagesView.Children.Count - pages.Count);
|
|
|
|
// Add/modify children
|
|
for (int i = 0; i < pages.Count; i++)
|
|
{
|
|
int page = pages[i];
|
|
|
|
// -1 indicates an ellipsis (...)
|
|
if (page == -1)
|
|
{
|
|
if (PagesView.Children.ElementAtOrDefault(i) is not PaginationEllipsis)
|
|
{
|
|
if (PagesView.Children.Count - 1 >= i)
|
|
PagesView.Children[i] = new PaginationEllipsis();
|
|
else
|
|
PagesView.Children.Add(new PaginationEllipsis());
|
|
}
|
|
}
|
|
// Anything else indicates a regular page
|
|
else
|
|
{
|
|
if (PagesView.Children.ElementAtOrDefault(i) is PaginationPage paginationPage)
|
|
{
|
|
paginationPage.Page = page;
|
|
paginationPage.Command = ReactiveCommand.Create(() => Value = page);
|
|
continue;
|
|
}
|
|
|
|
paginationPage = new PaginationPage {Page = page, Command = ReactiveCommand.Create(() => Value = page)};
|
|
if (PagesView.Children.Count - 1 >= i)
|
|
PagesView.Children[i] = paginationPage;
|
|
else
|
|
PagesView.Children.Add(paginationPage);
|
|
}
|
|
}
|
|
|
|
foreach (Control child in PagesView.Children)
|
|
{
|
|
if (child is PaginationPage paginationPage)
|
|
((IPseudoClasses) paginationPage.Classes).Set(":selected", paginationPage.Page == Value);
|
|
}
|
|
}
|
|
|
|
private void PaginationPageOnPointerReleased(object? sender, PointerReleasedEventArgs e)
|
|
{
|
|
if (sender is PaginationPage paginationPage)
|
|
Value = paginationPage.Page;
|
|
}
|
|
|
|
private static List<int> GetPages(int currentPage, int pageCount)
|
|
{
|
|
// Determine the delta based on how close to the edge the current page is
|
|
int delta;
|
|
if (pageCount <= 7)
|
|
delta = 7;
|
|
else
|
|
delta = currentPage > 4 && currentPage < pageCount - 3 ? 2 : 4;
|
|
|
|
int start = currentPage - delta / 2;
|
|
int end = currentPage + delta / 2;
|
|
|
|
if (start - 1 == 1 || end + 1 == pageCount)
|
|
{
|
|
start += 1;
|
|
end += 1;
|
|
}
|
|
|
|
// Determine start and end numbers based on how close to the edge the current page is
|
|
start = currentPage > delta ? Math.Min(start, pageCount - delta) : 1;
|
|
end = currentPage > delta ? Math.Min(end, pageCount) : Math.Min(pageCount, delta + 1);
|
|
|
|
// Start with the pages neighbouring the current page
|
|
List<int> paginationItems = Enumerable.Range(start, end - start + 1).ToList();
|
|
|
|
// If not starting at the first page, add the first page and an ellipsis (-1)
|
|
if (paginationItems.First() != 1)
|
|
{
|
|
paginationItems.Insert(0, 1);
|
|
paginationItems.Insert(1, -1);
|
|
}
|
|
|
|
// If not ending at the last page, add an ellipsis (-1) and the last page
|
|
if (paginationItems.Last() < pageCount)
|
|
{
|
|
paginationItems.Add(-1);
|
|
paginationItems.Add(pageCount);
|
|
}
|
|
|
|
return paginationItems;
|
|
}
|
|
} |