1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 21:38:38 +00:00
2023-07-21 23:10:50 +02:00

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;
}
}