use std::{collections::HashSet, fmt};
use promkit_core::grapheme::StyledGraphemes;
use crate::listbox::Listbox;
#[derive(Clone)]
pub struct Checkbox {
listbox: Listbox,
picked: HashSet<usize>,
}
impl Checkbox {
pub fn from_displayable<E: fmt::Display, I: IntoIterator<Item = E>>(items: I) -> Self {
Self {
listbox: Listbox::from(items),
picked: HashSet::new(),
}
}
pub fn from_styled_graphemes(items: Vec<StyledGraphemes>) -> Self {
Self {
listbox: Listbox::from_styled_graphemes(items),
picked: HashSet::new(),
}
}
pub fn new_with_checked<T: fmt::Display, I: IntoIterator<Item = (T, bool)>>(iter: I) -> Self {
let (listbox, picked): (Vec<_>, Vec<_>) = iter
.into_iter()
.enumerate()
.map(|(index, (item, is_picked))| ((index, item), is_picked))
.unzip();
let listbox_items = listbox
.into_iter()
.map(|(_, item)| item)
.collect::<Vec<_>>();
let picked_indices = picked
.into_iter()
.enumerate()
.filter_map(
|(index, is_picked)| {
if is_picked { Some(index) } else { None }
},
)
.collect::<HashSet<usize>>();
Self {
listbox: Listbox::from(listbox_items),
picked: picked_indices,
}
}
pub fn items(&self) -> &Vec<StyledGraphemes> {
self.listbox.items()
}
pub fn position(&self) -> usize {
self.listbox.position()
}
pub fn picked_indexes(&self) -> &HashSet<usize> {
&self.picked
}
pub fn get(&self) -> Vec<StyledGraphemes> {
self.picked
.iter()
.fold(Vec::<StyledGraphemes>::new(), |mut ret, idx| {
ret.push(self.listbox.items().get(*idx).unwrap().to_owned());
ret
})
}
pub fn toggle(&mut self) {
let Some(position) = self.listbox.selected() else {
return;
};
if self.picked.contains(&position) {
self.picked.remove(&position);
} else {
self.picked.insert(position);
}
}
pub fn toggle_at(&mut self, index: usize) -> bool {
if !self.listbox.move_to(index) {
return false;
}
self.toggle();
true
}
pub fn backward(&mut self) -> bool {
self.listbox.backward()
}
pub fn forward(&mut self) -> bool {
self.listbox.forward()
}
pub fn move_to_head(&mut self) {
self.listbox.move_to_head()
}
pub fn move_to_tail(&mut self) {
self.listbox.move_to_tail()
}
}
#[cfg(test)]
mod tests {
use super::*;
mod toggle {
use super::*;
#[test]
fn empty_checkbox_is_unchanged() {
let mut checkbox = Checkbox::from_displayable(Vec::<String>::new());
checkbox.toggle();
assert!(checkbox.picked_indexes().is_empty());
}
}
mod new_with_checked {
use super::*;
#[test]
fn initializes_items_and_checked_indexes() {
let items = vec![
(String::from("1"), true),
(String::from("2"), false),
(String::from("3"), true),
];
let checkbox = Checkbox::new_with_checked(items);
assert_eq!(
checkbox.items(),
&vec![
StyledGraphemes::from("1"),
StyledGraphemes::from("2"),
StyledGraphemes::from("3"),
]
);
let expected_picked_indexes: HashSet<usize> = [0, 2].iter().cloned().collect();
assert_eq!(checkbox.picked_indexes(), &expected_picked_indexes);
}
}
}