use crate::core::geometry::{Point, Rect};
use crate::core::event::Event;
use crate::terminal::Terminal;
use super::view::View;
use super::scrollbar::ScrollBar;
pub struct Scroller {
bounds: Rect,
delta: Point, limit: Point, h_scrollbar: Option<Box<ScrollBar>>,
v_scrollbar: Option<Box<ScrollBar>>,
}
impl Scroller {
pub fn new(bounds: Rect, h_scrollbar: Option<Box<ScrollBar>>, v_scrollbar: Option<Box<ScrollBar>>) -> Self {
let mut scroller = Self {
bounds,
delta: Point::zero(),
limit: Point::zero(),
h_scrollbar,
v_scrollbar,
};
scroller.update_scrollbars();
scroller
}
pub fn scroll_to(&mut self, x: i16, y: i16) {
self.delta.x = x.max(0).min(self.limit.x);
self.delta.y = y.max(0).min(self.limit.y);
self.update_scrollbars();
}
pub fn set_limit(&mut self, x: i16, y: i16) {
self.limit.x = x.max(0);
self.limit.y = y.max(0);
self.delta.x = self.delta.x.min(self.limit.x);
self.delta.y = self.delta.y.min(self.limit.y);
self.update_scrollbars();
}
pub fn get_delta(&self) -> Point {
self.delta
}
pub fn get_limit(&self) -> Point {
self.limit
}
fn update_scrollbars(&mut self) {
if let Some(ref mut h_bar) = self.h_scrollbar {
h_bar.set_params(
self.delta.x as i32,
0,
self.limit.x as i32,
self.bounds.width() as i32,
1,
);
}
if let Some(ref mut v_bar) = self.v_scrollbar {
v_bar.set_params(
self.delta.y as i32,
0,
self.limit.y as i32,
self.bounds.height() as i32,
1,
);
}
}
pub fn draw_scrollbars(&mut self, terminal: &mut Terminal) {
if let Some(ref mut h_bar) = self.h_scrollbar {
h_bar.draw(terminal);
}
if let Some(ref mut v_bar) = self.v_scrollbar {
v_bar.draw(terminal);
}
}
pub fn handle_scrollbar_events(&mut self, event: &mut Event) {
let old_delta = self.delta;
if let Some(ref mut h_bar) = self.h_scrollbar {
h_bar.handle_event(event);
self.delta.x = h_bar.get_value() as i16;
}
if let Some(ref mut v_bar) = self.v_scrollbar {
v_bar.handle_event(event);
self.delta.y = v_bar.get_value() as i16;
}
if old_delta != self.delta {
event.clear();
}
}
}
impl View for Scroller {
fn bounds(&self) -> Rect {
self.bounds
}
fn set_bounds(&mut self, bounds: Rect) {
self.bounds = bounds;
if let Some(ref mut h_bar) = self.h_scrollbar {
let h_bounds = Rect::new(
bounds.a.x,
bounds.b.y - 1,
bounds.b.x - 1,
bounds.b.y,
);
h_bar.set_bounds(h_bounds);
}
if let Some(ref mut v_bar) = self.v_scrollbar {
let v_bounds = Rect::new(
bounds.b.x - 1,
bounds.a.y,
bounds.b.x,
bounds.b.y - 1,
);
v_bar.set_bounds(v_bounds);
}
self.update_scrollbars();
}
fn draw(&mut self, terminal: &mut Terminal) {
self.draw_scrollbars(terminal);
}
fn handle_event(&mut self, event: &mut Event) {
self.handle_scrollbar_events(event);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scroller_scroll_to() {
let scroller = Scroller::new(Rect::new(0, 0, 80, 25), None, None);
let mut scroller = scroller;
scroller.set_limit(100, 100);
scroller.scroll_to(10, 20);
assert_eq!(scroller.get_delta(), Point::new(10, 20));
scroller.scroll_to(150, 150);
assert_eq!(scroller.get_delta(), Point::new(100, 100));
scroller.scroll_to(-10, -10);
assert_eq!(scroller.get_delta(), Point::new(0, 0));
}
#[test]
fn test_scroller_set_limit() {
let scroller = Scroller::new(Rect::new(0, 0, 80, 25), None, None);
let mut scroller = scroller;
scroller.set_limit(100, 100);
scroller.scroll_to(50, 50);
assert_eq!(scroller.get_delta(), Point::new(50, 50));
scroller.set_limit(30, 30);
assert_eq!(scroller.get_delta(), Point::new(30, 30));
assert_eq!(scroller.get_limit(), Point::new(30, 30));
}
}