use std::fmt::Display;
use crate::{
core::{
crossterm::{
self,
event::Event,
style::{Attribute, Attributes, Color, ContentStyle},
},
render::{Renderer, SharedRenderer},
Widget,
},
preset::Evaluator,
widgets::{
checkbox::{self, config::Config},
text::{self, Text},
},
Signal,
};
pub mod evaluate;
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Index {
Title = 0,
Checkbox = 1,
}
pub struct Checkbox {
pub renderer: Option<SharedRenderer<Index>>,
pub evaluator: Evaluator<Self>,
pub title: text::State,
pub checkbox: checkbox::State,
}
#[async_trait::async_trait]
impl crate::Prompt for Checkbox {
async fn initialize(&mut self) -> anyhow::Result<()> {
let size = crossterm::terminal::size()?;
self.renderer = Some(SharedRenderer::new(
Renderer::try_new_with_graphemes(
[
(Index::Title, self.title.create_graphemes(size.0, size.1)),
(
Index::Checkbox,
self.checkbox.create_graphemes(size.0, size.1),
),
],
true,
)
.await?,
));
Ok(())
}
async fn evaluate(&mut self, event: &Event) -> anyhow::Result<Signal> {
let ret = (self.evaluator)(event, self).await;
let size = crossterm::terminal::size()?;
self.render(size.0, size.1).await?;
ret
}
type Return = Vec<String>;
fn finalize(&mut self) -> anyhow::Result<Self::Return> {
Ok(self
.checkbox
.checkbox
.get()
.iter()
.map(|e| e.to_string())
.collect())
}
}
impl Checkbox {
fn new_with_checkbox(checkbox: checkbox::Checkbox) -> Self {
Self {
renderer: None,
evaluator: |event, ctx| Box::pin(evaluate::default(event, ctx)),
title: text::State {
config: text::config::Config {
style: Some(ContentStyle {
attributes: Attributes::from(Attribute::Bold),
..Default::default()
}),
..Default::default()
},
..Default::default()
},
checkbox: checkbox::State {
checkbox,
config: Config {
cursor: String::from("❯ "),
active_mark: '☒',
inactive_mark: '☐',
active_item_style: ContentStyle {
foreground_color: Some(Color::DarkCyan),
..Default::default()
},
inactive_item_style: ContentStyle::default(),
lines: Default::default(),
},
},
}
}
pub fn new<T: Display, I: IntoIterator<Item = T>>(items: I) -> Self {
Self::new_with_checkbox(checkbox::Checkbox::from_displayable(items))
}
pub fn new_with_checked<T: Display, I: IntoIterator<Item = (T, bool)>>(items: I) -> Self {
Self::new_with_checkbox(checkbox::Checkbox::new_with_checked(items))
}
pub fn title<T: AsRef<str>>(mut self, text: T) -> Self {
self.title.text = Text::from(text);
self
}
pub fn title_style(mut self, style: ContentStyle) -> Self {
self.title.config.style = Some(style);
self
}
pub fn cursor<T: AsRef<str>>(mut self, cursor: T) -> Self {
self.checkbox.config.cursor = cursor.as_ref().to_string();
self
}
pub fn active_mark(mut self, mark: char) -> Self {
self.checkbox.config.active_mark = mark;
self
}
pub fn active_item_style(mut self, style: ContentStyle) -> Self {
self.checkbox.config.active_item_style = style;
self
}
pub fn inactive_item_style(mut self, style: ContentStyle) -> Self {
self.checkbox.config.inactive_item_style = style;
self
}
pub fn checkbox_lines(mut self, lines: usize) -> Self {
self.checkbox.config.lines = Some(lines);
self
}
pub fn evaluator(mut self, evaluator: Evaluator<Self>) -> Self {
self.evaluator = evaluator;
self
}
async fn render(&mut self, width: u16, height: u16) -> anyhow::Result<()> {
match self.renderer.as_ref() {
Some(renderer) => {
renderer
.update([
(Index::Title, self.title.create_graphemes(width, height)),
(
Index::Checkbox,
self.checkbox.create_graphemes(width, height),
),
])
.render()
.await
}
None => Err(anyhow::anyhow!("Renderer not initialized")),
}
}
}