Files
Cagire/crates/ratatui/src/list_select.rs
Raphaël Forment dd853b8e1b
Some checks failed
Deploy Website / deploy (push) Failing after 4m46s
Feat: begin slight refactoring
2026-02-01 12:38:48 +01:00

106 lines
3.0 KiB
Rust

use crate::theme;
use ratatui::layout::Rect;
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::Paragraph;
use ratatui::Frame;
pub struct ListSelect<'a> {
items: &'a [String],
selected: usize,
cursor: usize,
focused: bool,
visible_count: usize,
scroll_offset: usize,
}
impl<'a> ListSelect<'a> {
pub fn new(items: &'a [String], selected: usize, cursor: usize) -> Self {
Self {
items,
selected,
cursor,
focused: false,
visible_count: 5,
scroll_offset: 0,
}
}
pub fn focused(mut self, focused: bool) -> Self {
self.focused = focused;
self
}
pub fn scroll_offset(mut self, offset: usize) -> Self {
self.scroll_offset = offset;
self
}
pub fn visible_count(mut self, n: usize) -> Self {
self.visible_count = n;
self
}
pub fn height(&self) -> u16 {
let item_lines = self.items.len().min(self.visible_count) as u16;
if self.items.len() > self.visible_count {
item_lines + 1
} else {
item_lines
}
}
pub fn render(self, frame: &mut Frame, area: Rect) {
let colors = theme::get();
let cursor_style = Style::new().fg(colors.hint.key).add_modifier(Modifier::BOLD);
let selected_style = Style::new().fg(colors.ui.accent);
let normal_style = Style::default();
let indicator_style = Style::new().fg(colors.ui.text_dim);
let visible_end = (self.scroll_offset + self.visible_count).min(self.items.len());
let has_above = self.scroll_offset > 0;
let has_below = visible_end < self.items.len();
let mut lines: Vec<Line> = Vec::new();
for i in self.scroll_offset..visible_end {
let name = &self.items[i];
let is_cursor = self.focused && i == self.cursor;
let is_selected = i == self.selected;
let style = if is_cursor {
cursor_style
} else if is_selected {
selected_style
} else {
normal_style
};
let prefix = if is_selected { "x " } else { " " };
let mut spans = vec![
Span::styled(prefix.to_string(), style),
Span::styled(name.clone(), style),
];
if has_above && i == self.scroll_offset {
spans.push(Span::styled("", indicator_style));
} else if has_below && i == visible_end - 1 {
spans.push(Span::styled("", indicator_style));
}
lines.push(Line::from(spans));
}
if self.items.len() > self.visible_count {
let position = self.cursor + 1;
let total = self.items.len();
lines.push(Line::from(Span::styled(
format!(" ({position}/{total})"),
indicator_style,
)));
}
frame.render_widget(Paragraph::new(lines), area);
}
}