use crate::{components::ScrollType, ui::scrollbar::draw_scrollbar}; use std::cell::Cell; use tui::{backend::Backend, layout::Rect, Frame}; pub struct VerticalScroll { top: Cell, max_top: Cell, } impl VerticalScroll { pub const fn new() -> Self { Self { top: Cell::new(0), max_top: Cell::new(0), } } pub fn get_top(&self) -> usize { self.top.get() } pub fn reset(&self) { self.top.set(0); } pub fn _move_top(&self, move_type: ScrollType) -> bool { let old = self.top.get(); let max = self.max_top.get(); let new_scroll_top = match move_type { ScrollType::Down => old.saturating_add(1), ScrollType::Up => old.saturating_sub(1), ScrollType::Home => 0, ScrollType::End => max, _ => old, }; let new_scroll_top = new_scroll_top.clamp(0, max); if new_scroll_top == old { return false; } self.top.set(new_scroll_top); true } pub fn update(&self, selection: usize, selection_max: usize, visual_height: usize) -> usize { let new_top = calc_scroll_top(self.get_top(), visual_height, selection, selection_max); self.top.set(new_top); if visual_height == 0 { self.max_top.set(0); } else { let new_max = selection_max.saturating_sub(visual_height); self.max_top.set(new_max); } new_top } pub fn _update_no_selection(&self, line_count: usize, visual_height: usize) -> usize { self.update(self.get_top(), line_count, visual_height) } pub fn draw(&self, f: &mut Frame, r: Rect) { draw_scrollbar(f, r, self.max_top.get(), self.top.get()); } } const fn calc_scroll_top( current_top: usize, height_in_lines: usize, selection: usize, selection_max: usize, ) -> usize { if height_in_lines == 0 { return 0; } if selection_max <= height_in_lines { return 0; } if current_top + height_in_lines <= selection { selection.saturating_sub(height_in_lines) + 1 } else if current_top > selection { selection } else { current_top } }