use crossterm::event::{KeyCode, KeyEvent};
use ratatui::{
prelude::*,
widgets::{Block, Borders, List, ListItem, ListState},
};
#[derive(Default)]
pub struct OptionsList<'list_contents> {
contents: Vec<ListItem<'list_contents>>,
contents_texts: Vec<&'list_contents str>,
list_state: ListState,
focus: bool,
}
impl<'list_contents> OptionsList<'list_contents> {
pub fn focus(&mut self) {
self.focus = true;
}
pub fn defocus(&mut self) {
self.focus = false;
}
pub fn set_contents(&mut self, contents: Vec<String>) {
let contents = contents
.into_iter()
.map(|tittle| {
&*Box::leak(tittle.into_boxed_str())
})
.collect();
for tittle in self.contents_texts.iter() {
unsafe {
let boxed_str: Box<str> = Box::from_raw(*tittle as *const str as *mut str);
drop(boxed_str);
}
}
self.contents_texts = contents;
self.contents = self
.contents_texts
.iter()
.map(|tittle| ListItem::new(*tittle))
.collect();
if !self.contents.is_empty() {
self.list_state.select(Some(0));
}
}
pub fn handle_key_event(&mut self, key_event: KeyEvent) {
match key_event.code {
KeyCode::Up => self.up(),
KeyCode::Down => self.down(),
_ => (),
}
}
fn up(&mut self) {
self.list_state.select_previous();
}
fn down(&mut self) {
self.list_state.select_next();
}
pub fn current(&self) -> Option<usize> {
self.list_state.selected()
}
pub fn current_value(&self) -> Option<String> {
if let Some(idx) = self.list_state.selected() {
return Some(self.contents_texts[idx].to_owned());
}
None
}
}
impl Widget for &mut OptionsList<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let list = List::new(self.contents.clone())
.highlight_symbol("> ")
.highlight_style(Style::new().fg(Color::Yellow).add_modifier(Modifier::BOLD))
.block(
Block::new()
.borders(Borders::RIGHT)
.border_style(Style::new().fg(match self.focus {
true => Color::Yellow,
false => Color::White,
})),
);
StatefulWidget::render(list, area, buf, &mut self.list_state);
}
}