use crossterm::event::{
KeyCode,
KeyModifiers,
};
use crate::{
Component,
Event,
Focusable,
InputResult,
RenderError,
Rendered,
theme::{
Palette,
Style,
Theme,
stylize,
},
};
pub struct SettingsList {
items: Vec<(String, bool)>,
selected: usize,
focused: bool,
on_change: Option<fn(usize, bool)>,
}
impl SettingsList {
pub fn new(items: Vec<(String, bool)>) -> Self {
Self {
items: items,
selected: 0,
focused: false,
on_change: None,
}
}
pub fn on_change(mut self, cb: fn(usize, bool)) -> Self {
self.on_change = Some(cb);
self
}
pub fn values(&self) -> Vec<bool> {
self.items.iter().map(|(_, v)| *v).collect()
}
pub fn set_selected(&mut self, index: usize) {
self.selected = index.min(self.items.len().saturating_sub(1));
}
}
impl Focusable for SettingsList {
fn focused(&self) -> bool {
self.focused
}
fn set_focused(&mut self, focused: bool) {
self.focused = focused;
}
}
impl Component for SettingsList {
fn render(&self, width: u16) -> Result<Rendered, RenderError> {
let theme = Theme::current();
let accent_style = Style::new().fg(theme.accent()).bold();
let primary_style = Style::new().fg(theme.text_primary());
let dim_style = Style::new().fg(theme.text_secondary());
let mut lines = Vec::new();
for (i, (name, value)) in self.items.iter().enumerate() {
let is_selected = i == self.selected;
let style = if is_selected && self.focused {
&accent_style
} else if is_selected {
&primary_style
} else {
&dim_style
};
let prefix = if is_selected { "> " } else { " " };
let check = if *value { "[x]" } else { "[ ]" };
let line = stylize(&format!("{}{} {}", prefix, check, name), style);
lines.push(crate::utils::truncate_to_width(&line, width, "…"));
}
Ok(Rendered {
lines,
cursor: None,
images: Vec::new(),
})
}
fn handle_input(&mut self, event: &Event) -> InputResult {
if let Event::Key(key) = event {
match key.code {
| KeyCode::Down => {
if self.selected + 1 < self.items.len() {
self.selected += 1;
}
InputResult::Handled
},
| KeyCode::Up => {
if self.selected > 0 {
self.selected -= 1;
}
InputResult::Handled
},
| KeyCode::Char('j') if !key.modifiers.contains(KeyModifiers::CONTROL) => {
if self.selected + 1 < self.items.len() {
self.selected += 1;
}
InputResult::Handled
},
| KeyCode::Char('k') if !key.modifiers.contains(KeyModifiers::CONTROL) => {
if self.selected > 0 {
self.selected -= 1;
}
InputResult::Handled
},
| KeyCode::Enter | KeyCode::Char(' ') => {
if let Some((_, value)) = self.items.get_mut(self.selected) {
*value = !*value;
if let Some(cb) = self.on_change {
cb(self.selected, *value);
}
}
InputResult::Handled
},
| _ => InputResult::Ignored,
}
} else {
InputResult::Ignored
}
}
fn as_focusable(&self) -> Option<&dyn Focusable> {
Some(self)
}
fn as_focusable_mut(&mut self) -> Option<&mut dyn Focusable> {
Some(self)
}
}