use std::cell::Cell;
use ratatui::{Frame, layout::Rect, text::Line, widgets::Paragraph};
use super::{chrome, nav, scroll, style};
use crate::theme::Skin;
pub struct ListView<'a> {
pub rows: Vec<Line<'static>>,
pub selected: usize,
pub offset: &'a Cell<usize>,
}
pub fn render(
frame: &mut Frame,
area: Rect,
skin: &Skin,
view: ListView,
) -> usize {
render_core(frame, area, skin, view)
}
pub fn render_counted(
frame: &mut Frame,
area: Rect,
skin: &Skin,
view: ListView,
) -> usize {
if area.height <= 1 {
return render_core(frame, area, skin, view);
}
let badge = chrome::position_badge(view.selected, view.rows.len());
let rows = Rect {
height: area.height - 1,
..area
};
let viewport = render_core(frame, rows, skin, view);
chrome::render_corner_badge(frame, area, skin, &badge);
viewport
}
pub fn render_boxed(
frame: &mut Frame,
area: Rect,
skin: &Skin,
view: ListView,
decor: &chrome::BoxDecor,
force: bool,
) -> usize {
if force {
let badge = chrome::position_badge(view.selected, view.rows.len());
let inner = chrome::framed_decor(frame, area, skin, decor, &badge);
render_core(frame, inner, skin, view)
} else {
render(frame, area, skin, view)
}
}
fn render_core(
frame: &mut Frame,
area: Rect,
skin: &Skin,
view: ListView,
) -> usize {
let viewport = area.height as usize;
let total = view.rows.len();
let selected = view.selected;
let scroll = nav::ScrollView {
total,
offset: view.offset.get(),
viewport,
};
let visible_offset = nav::keep_visible(scroll, selected);
view.offset.set(visible_offset);
let highlight = style::bg(skin.palette.selection);
let visible: Vec<Line> = view
.rows
.into_iter()
.enumerate()
.skip(visible_offset)
.take(viewport)
.map(|(index, line)| {
if index == selected {
line.style(highlight)
} else {
line
}
})
.collect();
frame.render_widget(Paragraph::new(visible), area);
scroll::render_scrollbar(
frame,
area,
skin,
nav::ScrollView {
total,
offset: visible_offset,
viewport,
},
);
viewport
}