use crate::hooks::use_keyboard_press;
use super::state::{StateSetter, use_state};
use crossterm::event::KeyCode;
#[derive(Clone, Copy)]
pub struct ScrollHandle {
pub offset: usize,
pub content_height: usize,
pub viewport_height: usize,
set_offset: StateSetter<usize>,
}
impl ScrollHandle {
pub fn scroll_up(&self, lines: usize) {
let current = self.offset;
let new_offset = current.saturating_sub(lines);
self.set_offset.set(new_offset);
}
pub fn scroll_down(&self, lines: usize) {
let current = self.offset;
let max_offset = self.max_offset();
let new_offset = (current + lines).min(max_offset);
self.set_offset.set(new_offset);
}
pub fn scroll_to_top(&self) {
self.set_offset.set(0);
}
pub fn scroll_to_bottom(&self) {
self.set_offset.set(self.max_offset());
}
pub fn page_up(&self) {
self.scroll_up(self.viewport_height.saturating_sub(1));
}
pub fn page_down(&self) {
self.scroll_down(self.viewport_height.saturating_sub(1));
}
pub fn max_offset(&self) -> usize {
self.content_height.saturating_sub(self.viewport_height)
}
pub fn can_scroll_up(&self) -> bool {
self.offset > 0
}
pub fn can_scroll_down(&self) -> bool {
self.offset < self.max_offset()
}
pub fn scroll_progress(&self) -> f64 {
let max = self.max_offset();
if max == 0 {
0.0
} else {
self.offset as f64 / max as f64
}
}
pub fn has_overflow(&self) -> bool {
self.content_height > self.viewport_height
}
}
pub fn use_scroll(content_height: usize, viewport_height: usize) -> ScrollHandle {
let (offset, set_offset) = use_state(|| 0usize);
let max_offset = content_height.saturating_sub(viewport_height);
if offset > max_offset {
set_offset.set(max_offset);
}
ScrollHandle {
offset,
content_height,
viewport_height,
set_offset,
}
}
pub fn use_scroll_keyboard(content_height: usize, viewport_height: usize) -> ScrollHandle {
let scroll = use_scroll(content_height, viewport_height);
use_keyboard_press(move |key| match key.code {
KeyCode::Char('j') | KeyCode::Down => scroll.scroll_down(1),
KeyCode::Char('k') | KeyCode::Up => scroll.scroll_up(1),
KeyCode::Char('g') | KeyCode::Home => scroll.scroll_to_top(),
KeyCode::Char('G') | KeyCode::End => scroll.scroll_to_bottom(),
KeyCode::PageDown => scroll.page_down(),
KeyCode::PageUp => scroll.page_up(),
KeyCode::Char('d')
if key
.modifiers
.contains(crossterm::event::KeyModifiers::CONTROL) =>
{
scroll.page_down();
}
KeyCode::Char('u')
if key
.modifiers
.contains(crossterm::event::KeyModifiers::CONTROL) =>
{
scroll.page_up();
}
_ => {}
});
scroll
}