use ratatui::{
Frame,
layout::Rect,
style::Style,
widgets::{Block, List, ListItem, ListState},
};
use crate::tui::widgets::common::render_scrollbar;
pub(crate) struct ListPane {
cursor: usize,
state: ListState,
}
impl ListPane {
pub fn new(cursor: usize) -> Self {
ListPane {
cursor,
state: ListState::default(),
}
}
pub fn cursor(&self) -> usize {
self.cursor
}
pub fn set_cursor(&mut self, i: usize) {
self.cursor = i;
}
#[cfg(test)]
pub fn offset(&self) -> usize {
self.state.offset()
}
pub fn move_cursor(
&mut self,
dir: isize,
len: usize,
focusable: impl Fn(usize) -> bool,
) -> bool {
let mut pos = self.cursor as isize;
loop {
pos += dir;
if pos < 0 || pos as usize >= len {
return false;
}
if focusable(pos as usize) {
self.cursor = pos as usize;
return true;
}
}
}
pub fn hit_test(&self, area: Rect, screen_row: u16) -> Option<usize> {
let inner_top = area.y + 1;
let inner_height = area.height.saturating_sub(2);
if screen_row < inner_top || screen_row >= inner_top + inner_height {
return None;
}
Some((screen_row - inner_top) as usize + self.state.offset())
}
pub fn render(
&mut self,
frame: &mut Frame,
area: Rect,
items: Vec<ListItem>,
block: Block<'_>,
highlight: Style,
) {
let total = items.len();
let list = List::new(items).block(block).highlight_style(highlight);
self.state.select(Some(self.cursor));
frame.render_stateful_widget(list, area, &mut self.state);
let inner_height = area.height.saturating_sub(2) as usize;
render_scrollbar(frame, area, total, inner_height, self.state.offset());
}
}