You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
3.5 KiB
139 lines
3.5 KiB
use crate::ui::{
|
|
component::{Component, Event, EventCtx, Never},
|
|
display,
|
|
geometry::{LinearPlacement, Offset, Rect},
|
|
};
|
|
|
|
use super::theme;
|
|
|
|
pub struct ScrollBar {
|
|
area: Rect,
|
|
layout: LinearPlacement,
|
|
arrows: bool,
|
|
pub page_count: usize,
|
|
pub active_page: usize,
|
|
}
|
|
|
|
impl ScrollBar {
|
|
pub const DOT_SIZE: i32 = 6;
|
|
/// Edge to edge.
|
|
const DOT_INTERVAL: i32 = 6;
|
|
/// Edge of last dot to center of arrow icon.
|
|
const ARROW_SPACE: i32 = 26;
|
|
|
|
const ICON_UP: &'static [u8] = include_res!("model_tt/res/scroll-up.toif");
|
|
const ICON_DOWN: &'static [u8] = include_res!("model_tt/res/scroll-down.toif");
|
|
|
|
fn new(layout: LinearPlacement) -> Self {
|
|
Self {
|
|
area: Rect::zero(),
|
|
layout: layout.align_at_center().with_spacing(Self::DOT_INTERVAL),
|
|
arrows: false,
|
|
page_count: 0,
|
|
active_page: 0,
|
|
}
|
|
}
|
|
|
|
pub fn vertical() -> Self {
|
|
Self::new(LinearPlacement::vertical())
|
|
}
|
|
|
|
pub fn horizontal() -> Self {
|
|
Self::new(LinearPlacement::horizontal())
|
|
}
|
|
|
|
pub fn with_arrows(mut self) -> Self {
|
|
self.arrows = true;
|
|
self
|
|
}
|
|
|
|
pub fn set_count_and_active_page(&mut self, page_count: usize, active_page: usize) {
|
|
self.page_count = page_count;
|
|
self.active_page = active_page;
|
|
}
|
|
|
|
pub fn has_pages(&self) -> bool {
|
|
self.page_count > 1
|
|
}
|
|
|
|
pub fn has_next_page(&self) -> bool {
|
|
self.active_page < self.page_count - 1
|
|
}
|
|
|
|
pub fn has_previous_page(&self) -> bool {
|
|
self.active_page > 0
|
|
}
|
|
|
|
pub fn go_to_next_page(&mut self) {
|
|
self.go_to(self.active_page.saturating_add(1).min(self.page_count - 1));
|
|
}
|
|
|
|
pub fn go_to_previous_page(&mut self) {
|
|
self.go_to(self.active_page.saturating_sub(1));
|
|
}
|
|
|
|
pub fn go_to(&mut self, active_page: usize) {
|
|
self.active_page = active_page;
|
|
}
|
|
}
|
|
|
|
impl Component for ScrollBar {
|
|
type Msg = Never;
|
|
|
|
fn event(&mut self, _ctx: &mut EventCtx, _event: Event) -> Option<Self::Msg> {
|
|
None
|
|
}
|
|
|
|
fn paint(&mut self) {
|
|
let mut i = 0;
|
|
let mut top = None;
|
|
let mut display_icon = |top_left| {
|
|
let icon = if i == self.active_page {
|
|
theme::DOT_ACTIVE
|
|
} else {
|
|
theme::DOT_INACTIVE
|
|
};
|
|
display::icon_top_left(top_left, icon, theme::FG, theme::BG);
|
|
i += 1;
|
|
top.get_or_insert(top_left.x);
|
|
};
|
|
|
|
self.layout.arrange_uniform(
|
|
self.area,
|
|
self.page_count,
|
|
Offset::new(Self::DOT_SIZE, Self::DOT_SIZE),
|
|
&mut display_icon,
|
|
);
|
|
|
|
if self.arrows {
|
|
let arrow_distance = self.area.center().x - top.unwrap_or(0) + Self::ARROW_SPACE;
|
|
let offset = Offset::on_axis(self.layout.axis, arrow_distance);
|
|
if self.has_previous_page() {
|
|
display::icon(
|
|
self.area.center() - offset,
|
|
Self::ICON_UP,
|
|
theme::FG,
|
|
theme::BG,
|
|
);
|
|
}
|
|
if self.has_next_page() {
|
|
display::icon(
|
|
self.area.center() + offset,
|
|
Self::ICON_DOWN,
|
|
theme::FG,
|
|
theme::BG,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn place(&mut self, bounds: Rect) -> Rect {
|
|
self.area = bounds;
|
|
bounds
|
|
}
|
|
|
|
fn bounds(&self, sink: &mut dyn FnMut(Rect)) {
|
|
sink(self.area);
|
|
}
|
|
}
|