tty_form/control/
textinput.rs1use crossterm::event::{KeyCode, KeyEvent};
2use tty_text::Key;
3
4use crate::{
5 dependency::{Action, DependencyId, Evaluation},
6 step::CompoundStep,
7 style::help_style,
8 text::{DrawerContents, Segment, Text},
9};
10
11use super::Control;
12
13pub struct TextInput {
26 prompt: String,
27 text: tty_text::Text,
28 force_lowercase: bool,
29 evaluation: Option<(DependencyId, Evaluation)>,
30}
31
32impl TextInput {
33 pub fn new(prompt: &str, force_lowercase: bool) -> Self {
35 Self {
36 prompt: prompt.to_string(),
37 text: tty_text::Text::new(false),
38 force_lowercase,
39 evaluation: None,
40 }
41 }
42
43 pub fn set_prompt(&mut self, prompt: &str) {
45 self.prompt = prompt.to_string();
46 }
47
48 pub fn set_force_lowercase(&mut self, force: bool) {
50 self.force_lowercase = force;
51 }
52
53 pub fn set_evaluation(&mut self, evaluation: Evaluation) -> DependencyId {
55 let id = DependencyId::new();
56 self.evaluation = Some((id, evaluation));
57 id
58 }
59}
60
61impl Control for TextInput {
62 fn focusable(&self) -> bool {
63 true
64 }
65
66 fn update(&mut self, input: KeyEvent) {
67 match input.code {
68 KeyCode::Char(mut ch) => {
69 if self.force_lowercase {
70 ch = ch.to_lowercase().next().unwrap();
71 }
72
73 self.text.handle_input(Key::Char(ch));
74 }
75 KeyCode::Backspace => self.text.handle_input(Key::Backspace),
76 KeyCode::Left => self.text.handle_input(Key::Left),
77 KeyCode::Right => self.text.handle_input(Key::Right),
78 _ => {}
79 };
80 }
81
82 fn help(&self) -> Option<Segment> {
83 Some(Text::new_styled(self.prompt.clone(), help_style()).as_segment())
84 }
85
86 fn text(&self) -> (Segment, Option<u16>) {
87 let segment = Text::new(self.text.value()).as_segment();
88 let cursor_column = self.text.cursor().0 as u16;
89
90 (segment, Some(cursor_column))
91 }
92
93 fn drawer(&self) -> Option<DrawerContents> {
94 None
95 }
96
97 fn evaluation(&self) -> Option<(DependencyId, Evaluation)> {
98 self.evaluation.clone()
99 }
100
101 fn dependency(&self) -> Option<(DependencyId, Action)> {
102 None
103 }
104
105 fn evaluate(&self, evaluation: &Evaluation) -> bool {
106 match evaluation {
107 Evaluation::Equal(value) => &self.text.value() == value,
108 Evaluation::NotEqual(value) => &self.text.value() != value,
109 Evaluation::IsEmpty => self.text.value().is_empty(),
110 }
111 }
112
113 fn add_to(self, step: &mut CompoundStep) {
114 step.add_control(Box::new(self))
115 }
116}