use crate::widgets::markdown_widget::state::scroll_state::ScrollState;
pub fn thumb_bounds(scroll: &ScrollState, track_height: u16, min_thumb_height: u16) -> (u16, u16) {
let total = scroll.total_lines.max(1);
let viewport = scroll.viewport_height.max(1);
if total <= viewport {
return (0, track_height);
}
let thumb_height = ((viewport as f64 / total as f64) * track_height as f64)
.max(min_thumb_height as f64)
.min(track_height as f64) as u16;
let max_scroll = total.saturating_sub(viewport);
let scrollable_track = track_height.saturating_sub(thumb_height);
let thumb_y = if max_scroll > 0 && scrollable_track > 0 {
((scroll.scroll_offset as f64 / max_scroll as f64) * scrollable_track as f64) as u16
} else {
0
};
(thumb_y, thumb_height)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_content_fits_viewport() {
let scroll = ScrollState {
scroll_offset: 0,
viewport_height: 20,
total_lines: 10,
current_line: 1,
filter: None,
filter_mode: false,
};
let (y, height) = thumb_bounds(&scroll, 20, 1);
assert_eq!(y, 0);
assert_eq!(height, 20); }
#[test]
fn test_at_top() {
let scroll = ScrollState {
scroll_offset: 0,
viewport_height: 10,
total_lines: 100,
current_line: 1,
filter: None,
filter_mode: false,
};
let (y, _height) = thumb_bounds(&scroll, 20, 1);
assert_eq!(y, 0);
}
#[test]
fn test_at_bottom() {
let scroll = ScrollState {
scroll_offset: 90, viewport_height: 10,
total_lines: 100,
current_line: 1,
filter: None,
filter_mode: false,
};
let (y, height) = thumb_bounds(&scroll, 20, 1);
assert_eq!(y + height, 20);
}
#[test]
fn test_min_thumb_height() {
let scroll = ScrollState {
scroll_offset: 0,
viewport_height: 1,
total_lines: 1000,
current_line: 1,
filter: None,
filter_mode: false,
};
let (_y, height) = thumb_bounds(&scroll, 20, 3);
assert!(height >= 3); }
}