use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::widgets::ScrollbarState;
use crate::layout::style;
#[must_use]
pub fn area_above_bottom_pad(area: Rect, bottom_pad: u16) -> Rect {
if area.height > bottom_pad {
let chunks =
style::split_vertical(area, &[Constraint::Min(0), Constraint::Length(bottom_pad)]);
chunks[0]
} else {
area
}
}
#[must_use]
pub fn viewport_text_width(padded: Rect, total_lines: usize) -> u16 {
let viewport = padded.height as usize;
let need_scrollbar = total_lines > viewport;
if need_scrollbar && padded.width > 1 {
padded.width - 1
} else {
padded.width
}
}
#[derive(Debug)]
pub struct ScrollableLayout {
pub content_rect: Rect,
pub scrollbar_rect: Rect,
pub scroll_y: u16,
pub show_scrollbar: bool,
}
pub fn layout_scrollable_content(
area: Rect,
total_lines: usize,
scroll: &mut u16,
bottom_pad: u16,
) -> ScrollableLayout {
let content_with_pad = if area.height > bottom_pad {
let chunks =
style::split_vertical(area, &[Constraint::Min(0), Constraint::Length(bottom_pad)]);
chunks[0]
} else {
area
};
let viewport = content_with_pad.height as usize;
let need_scrollbar = total_lines > viewport;
let (content_rect, scrollbar_rect) = if need_scrollbar && content_with_pad.width > 1 {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Min(0), Constraint::Length(1)])
.split(content_with_pad);
(chunks[0], chunks[1])
} else {
(content_with_pad, Rect::default())
};
let viewport_final = content_rect.height as usize;
let max_scroll = total_lines.saturating_sub(viewport_final) as u16;
let scroll_y = (*scroll).min(max_scroll);
*scroll = scroll_y;
ScrollableLayout {
content_rect,
scrollbar_rect,
scroll_y,
show_scrollbar: need_scrollbar,
}
}
pub fn draw_scrollbar(f: &mut ratatui::Frame, layout: &ScrollableLayout, total_lines: usize) {
if !layout.show_scrollbar
|| layout.scrollbar_rect.width == 0
|| layout.scrollbar_rect.height == 0
{
return;
}
let viewport = layout.content_rect.height as usize;
let max_scroll = total_lines.saturating_sub(viewport);
if max_scroll == 0 {
return;
}
let content_len = max_scroll + 1;
let mut state = ScrollbarState::new(content_len)
.position(layout.scroll_y as usize)
.viewport_content_length(1);
f.render_stateful_widget(style::viewer_scrollbar(), layout.scrollbar_rect, &mut state);
}