use ratatui::{
Frame,
layout::Rect,
widgets::{Scrollbar, ScrollbarOrientation, ScrollbarState},
};
use super::nav::ScrollView;
use super::style;
use crate::theme::Skin;
pub fn render_scrollbar(
frame: &mut Frame,
area: Rect,
skin: &Skin,
view: ScrollView,
) {
render(frame, area, skin, ScrollbarOrientation::VerticalRight, view);
}
pub fn render_hscrollbar(
frame: &mut Frame,
area: Rect,
skin: &Skin,
view: ScrollView,
) {
render(
frame,
area,
skin,
ScrollbarOrientation::HorizontalBottom,
view,
);
}
fn scrollable_length(view: ScrollView) -> Option<usize> {
if view.total <= view.viewport {
return None;
}
Some(view.total.saturating_sub(view.viewport).saturating_add(1))
}
fn render(
frame: &mut Frame,
area: Rect,
skin: &Skin,
orientation: ScrollbarOrientation,
view: ScrollView,
) {
let Some(content_length) = scrollable_length(view) else {
return;
};
if area.width == 0 || area.height == 0 {
return;
}
let mut state = ScrollbarState::new(content_length).position(view.offset);
let scrollbar = Scrollbar::new(orientation)
.begin_symbol(None)
.end_symbol(None)
.thumb_style(style::fg(skin.palette.foreground_dim))
.track_style(style::fg(skin.palette.border));
frame.render_stateful_widget(scrollbar, area, &mut state);
}
#[cfg(test)]
mod tests {
use super::*;
fn view(total: usize, offset: usize, viewport: usize) -> ScrollView {
ScrollView {
total,
offset,
viewport,
}
}
#[test]
fn no_scrollbar_when_content_fits() {
assert_eq!(scrollable_length(view(3, 0, 3)), None);
assert_eq!(scrollable_length(view(2, 0, 5)), None);
}
#[test]
fn scrollable_length_is_total_minus_viewport_plus_one() {
assert_eq!(scrollable_length(view(10, 0, 3)), Some(8));
assert_eq!(scrollable_length(view(4, 0, 3)), Some(2));
}
}