OneLauncher-Cracked/packages/oneclient_app/src/components/pagination.rs
2026-09-18 23:05:50 +02:00

180 lines
5.2 KiB
Rust

use freya::prelude::*;
use crate::components::{Button, Icon, IconType, TooltipPlacement};
use crate::theme::colors;
use crate::ui::border_all_color;
fn page_window(current: usize, total: usize, window: usize) -> (usize, usize) {
if total <= window {
return (0, total);
}
let end = (current + window / 2 + 1).min(total);
let start = end.saturating_sub(window);
(start, end)
}
#[derive(PartialEq)]
pub struct Pagination {
page: State<usize>,
total_pages: usize,
window: usize,
enabled: bool,
}
impl Pagination {
pub fn new(page: State<usize>, total_pages: usize) -> Self {
Self {
page,
total_pages,
window: 5,
enabled: true,
}
}
#[allow(dead_code)]
pub fn window(mut self, window: usize) -> Self {
self.window = window.max(1);
self
}
/// Greys out controls without unmounting the pager
pub fn enabled(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
}
impl Component for Pagination {
fn render(&self) -> impl IntoElement {
let page = self.page;
let total_pages = self.total_pages;
let interactive = self.enabled;
let current = *page.read();
let last = total_pages.saturating_sub(1);
let icon_btn =
move |target: usize, available: bool, icon: IconType, tooltip: &'static str| {
let mut page = page;
let enabled = available && interactive;
Button::new()
.secondary()
.icon()
.enabled(enabled)
.tooltip(tooltip)
.tooltip_placement(TooltipPlacement::Top)
.on_press(move |_| page.set(target))
.child(Icon::new(icon).size(14.).color(if enabled {
colors::fg_primary()
} else {
colors::fg_secondary().with_a(90)
}))
.into_element()
};
let num_btn = move |target: usize| {
let mut page = page;
let is_current = target == current;
if is_current {
return rect()
.center()
.width(Size::px(32.))
.height(Size::px(32.))
.corner_radius(CornerRadius::new_all(8.))
.background(colors::component_bg_pressed())
.border(border_all_color(1., colors::brand()))
.child(
label()
.text(format!("{}", target + 1))
.font_size(12.)
.font_weight(FontWeight::SEMI_BOLD)
.color(colors::fg_primary()),
)
.into_element();
}
Button::new()
.secondary()
.width(Size::px(32.))
.height(Size::px(32.))
.padding(Gaps::new_symmetric(6., 0.))
.enabled(interactive)
.on_press(move |_| page.set(target))
.child(
label()
.text(format!("{}", target + 1))
.font_size(12.)
.font_weight(FontWeight::NORMAL)
.max_lines(1)
.color(colors::fg_secondary()),
)
.into_element()
};
let ellipsis = || {
rect()
.center()
.width(Size::px(20.))
.height(Size::px(32.))
.child(
label()
.text("...")
.font_size(12.)
.color(colors::fg_secondary()),
)
.into_element()
};
let (start, end) = page_window(current, total_pages, self.window);
let mut row = rect()
.horizontal()
.width(Size::fill())
.main_align(Alignment::Center)
.cross_align(Alignment::Center)
.spacing(6.)
.child(icon_btn(
0,
current > 0,
IconType::ChevronsLeft,
"First page",
))
.child(icon_btn(
current.saturating_sub(1),
current > 0,
IconType::ArrowLeft,
"Previous page",
));
if start > 0 {
row = row.child(num_btn(0));
if start > 1 {
row = row.child(ellipsis());
}
}
for p in start..end {
row = row.child(num_btn(p));
}
if end < total_pages {
if end < last {
row = row.child(ellipsis());
}
row = row.child(num_btn(last));
}
row.child(icon_btn(
current + 1,
current < last,
IconType::ArrowRight,
"Next page",
))
.child(icon_btn(
last,
current < last,
IconType::ChevronsRight,
"Last page",
))
}
}