promkit/preset/
checkbox.rs1use std::fmt::Display;
4
5use crate::{
6 core::{
7 crossterm::{
8 self,
9 event::Event,
10 style::{Attribute, Attributes, Color, ContentStyle},
11 },
12 render::{Renderer, SharedRenderer},
13 PaneFactory,
14 },
15 preset::Evaluator,
16 widgets::{
17 checkbox,
18 text::{self, Text},
19 },
20 Signal,
21};
22
23pub mod evaluate;
24
25#[derive(PartialEq, Eq, PartialOrd, Ord)]
27pub enum Index {
28 Title = 0,
29 Checkbox = 1,
30}
31
32pub struct Checkbox {
35 pub renderer: Option<SharedRenderer<Index>>,
37 pub evaluator: Evaluator<Self>,
39 pub title: text::State,
41 pub checkbox: checkbox::State,
43}
44
45#[async_trait::async_trait]
46impl crate::Prompt for Checkbox {
47 async fn initialize(&mut self) -> anyhow::Result<()> {
48 let size = crossterm::terminal::size()?;
49 self.renderer = Some(SharedRenderer::new(
50 Renderer::try_new_with_panes(
51 [
52 (Index::Title, self.title.create_pane(size.0, size.1)),
53 (Index::Checkbox, self.checkbox.create_pane(size.0, size.1)),
54 ],
55 true,
56 )
57 .await?,
58 ));
59 Ok(())
60 }
61
62 async fn evaluate(&mut self, event: &Event) -> anyhow::Result<Signal> {
63 let ret = (self.evaluator)(event, self).await;
64 let size = crossterm::terminal::size()?;
65 self.render(size.0, size.1).await?;
66 ret
67 }
68
69 type Return = Vec<String>;
70
71 fn finalize(&mut self) -> anyhow::Result<Self::Return> {
72 Ok(self
73 .checkbox
74 .checkbox
75 .get()
76 .iter()
77 .map(|e| e.to_string())
78 .collect())
79 }
80}
81
82impl Checkbox {
83 fn new_with_checkbox(checkbox: checkbox::Checkbox) -> Self {
84 Self {
85 renderer: None,
86 evaluator: |event, ctx| Box::pin(evaluate::default(event, ctx)),
87 title: text::State {
88 style: ContentStyle {
89 attributes: Attributes::from(Attribute::Bold),
90 ..Default::default()
91 },
92 ..Default::default()
93 },
94 checkbox: checkbox::State {
95 checkbox,
96 cursor: String::from("❯ "),
97 active_mark: '☒',
98 inactive_mark: '☐',
99 active_item_style: ContentStyle {
100 foreground_color: Some(Color::DarkCyan),
101 ..Default::default()
102 },
103 inactive_item_style: ContentStyle::default(),
104 lines: Default::default(),
105 },
106 }
107 }
108
109 pub fn new<T: Display, I: IntoIterator<Item = T>>(items: I) -> Self {
111 Self::new_with_checkbox(checkbox::Checkbox::from_displayable(items))
112 }
113
114 pub fn new_with_checked<T: Display, I: IntoIterator<Item = (T, bool)>>(items: I) -> Self {
116 Self::new_with_checkbox(checkbox::Checkbox::new_with_checked(items))
117 }
118
119 pub fn title<T: AsRef<str>>(mut self, text: T) -> Self {
121 self.title.text = Text::from(text);
122 self
123 }
124
125 pub fn title_style(mut self, style: ContentStyle) -> Self {
127 self.title.style = style;
128 self
129 }
130
131 pub fn cursor<T: AsRef<str>>(mut self, cursor: T) -> Self {
133 self.checkbox.cursor = cursor.as_ref().to_string();
134 self
135 }
136
137 pub fn active_mark(mut self, mark: char) -> Self {
139 self.checkbox.active_mark = mark;
140 self
141 }
142
143 pub fn active_item_style(mut self, style: ContentStyle) -> Self {
145 self.checkbox.active_item_style = style;
146 self
147 }
148
149 pub fn inactive_item_style(mut self, style: ContentStyle) -> Self {
151 self.checkbox.inactive_item_style = style;
152 self
153 }
154
155 pub fn checkbox_lines(mut self, lines: usize) -> Self {
157 self.checkbox.lines = Some(lines);
158 self
159 }
160
161 pub fn evaluator(mut self, evaluator: Evaluator<Self>) -> Self {
163 self.evaluator = evaluator;
164 self
165 }
166
167 async fn render(&mut self, width: u16, height: u16) -> anyhow::Result<()> {
169 match self.renderer.as_ref() {
170 Some(renderer) => {
171 renderer
172 .update([
173 (Index::Title, self.title.create_pane(width, height)),
174 (Index::Checkbox, self.checkbox.create_pane(width, height)),
175 ])
176 .render()
177 .await
178 }
179 None => Err(anyhow::anyhow!("Renderer not initialized")),
180 }
181 }
182}