#[derive(Debug, Default, Clone, Copy)]
pub struct ScrollState {
pub selected_idx: Option<usize>,
pub scroll_top: usize,
}
impl ScrollState {
pub fn new() -> Self {
Self {
selected_idx: None,
scroll_top: 0,
}
}
pub fn reset(&mut self) {
self.selected_idx = None;
self.scroll_top = 0;
}
pub fn clamp_selection(&mut self, len: usize) {
if self.clear_if_empty(len) {
return;
}
self.selected_idx = Some(self.selected_idx.unwrap_or(0).min(len - 1));
}
pub fn move_up_wrap(&mut self, len: usize) {
if self.clear_if_empty(len) {
return;
}
self.selected_idx = Some(match self.selected_idx {
Some(idx) if idx > 0 => idx - 1,
Some(_) => len - 1,
None => 0,
});
}
pub fn move_down_wrap(&mut self, len: usize) {
if self.clear_if_empty(len) {
return;
}
self.selected_idx = Some(match self.selected_idx {
Some(idx) if idx + 1 < len => idx + 1,
_ => 0,
});
}
pub fn page_up_clamped(&mut self, len: usize, visible_rows: usize) {
if self.clear_if_empty(len) {
return;
}
let step = visible_rows.max(1);
let current = self.selected_idx.unwrap_or(0).min(len - 1);
self.selected_idx = Some(current.saturating_sub(step));
self.ensure_visible(len, visible_rows);
}
pub fn page_down_clamped(&mut self, len: usize, visible_rows: usize) {
if self.clear_if_empty(len) {
return;
}
let step = visible_rows.max(1);
let current = self.selected_idx.unwrap_or(0).min(len - 1);
self.selected_idx = Some(current.saturating_add(step).min(len - 1));
self.ensure_visible(len, visible_rows);
}
pub fn jump_top(&mut self, len: usize, visible_rows: usize) {
if self.clear_if_empty(len) {
return;
}
self.selected_idx = Some(0);
self.ensure_visible(len, visible_rows);
}
pub fn jump_bottom(&mut self, len: usize, visible_rows: usize) {
if self.clear_if_empty(len) {
return;
}
self.selected_idx = Some(len - 1);
self.ensure_visible(len, visible_rows);
}
fn clear_if_empty(&mut self, len: usize) -> bool {
if len != 0 {
return false;
}
self.selected_idx = None;
self.scroll_top = 0;
true
}
pub fn ensure_visible(&mut self, len: usize, visible_rows: usize) {
if len == 0 || visible_rows == 0 {
self.scroll_top = 0;
return;
}
if let Some(sel) = self.selected_idx {
if sel < self.scroll_top {
self.scroll_top = sel;
} else {
let bottom = self.scroll_top + visible_rows - 1;
if sel > bottom {
self.scroll_top = sel + 1 - visible_rows;
}
}
} else {
self.scroll_top = 0;
}
}
}