tui/components/
checkbox.rs1use crossterm::event::KeyCode;
2
3use crate::components::{Component, Event, ViewContext};
4use crate::line::Line;
5use crate::rendering::frame::Frame;
6
7pub struct Checkbox {
9 pub checked: bool,
10}
11
12impl Checkbox {
13 pub fn new(checked: bool) -> Self {
14 Self { checked }
15 }
16
17 pub fn to_json(&self) -> serde_json::Value {
18 serde_json::Value::Bool(self.checked)
19 }
20}
21
22impl Component for Checkbox {
23 type Message = ();
24
25 async fn on_event(&mut self, event: &Event) -> Option<Vec<Self::Message>> {
26 let Event::Key(key) = event else {
27 return None;
28 };
29 match key.code {
30 KeyCode::Char(' ') => {
31 self.checked = !self.checked;
32 Some(vec![])
33 }
34 _ => None,
35 }
36 }
37
38 fn render(&mut self, context: &ViewContext) -> Frame {
39 Frame::new(self.render_field(context, true))
40 }
41}
42
43impl Checkbox {
44 pub fn render_field(&self, context: &ViewContext, focused: bool) -> Vec<Line> {
45 let display = if self.checked { "[x]" } else { "[ ]" };
46 let style = if focused {
47 context.theme.primary()
48 } else {
49 context.theme.text_primary()
50 };
51 vec![Line::styled(display, style)]
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use crossterm::event::{KeyEvent, KeyModifiers};
59
60 fn key(code: KeyCode) -> KeyEvent {
61 KeyEvent::new(code, KeyModifiers::NONE)
62 }
63
64 #[tokio::test]
65 async fn space_toggles() {
66 let mut cb = Checkbox::new(false);
67 cb.on_event(&Event::Key(key(KeyCode::Char(' ')))).await;
68 assert!(cb.checked);
69 cb.on_event(&Event::Key(key(KeyCode::Char(' ')))).await;
70 assert!(!cb.checked);
71 }
72
73 #[test]
74 fn to_json_returns_bool() {
75 assert_eq!(Checkbox::new(true).to_json(), serde_json::json!(true));
76 assert_eq!(Checkbox::new(false).to_json(), serde_json::json!(false));
77 }
78
79 #[tokio::test]
80 async fn other_keys_are_ignored() {
81 let mut cb = Checkbox::new(false);
82 let outcome = cb.on_event(&Event::Key(key(KeyCode::Char('a')))).await;
83 assert!(outcome.is_none());
84 assert!(!cb.checked);
85 }
86}