tty_form/step/
keyvalue.rs1use crossterm::event::{KeyCode, KeyEvent};
2use tty_interface::{pos, Interface, Position};
3use tty_text::Key;
4
5use crate::{
6 dependency::{DependencyId, DependencyState, Evaluation},
7 style::help_style,
8 text::{DrawerContents, Segment, Text},
9 Form,
10};
11
12use super::{InputResult, Step};
13
14pub struct KeyValueStep {
16 prompt: String,
17 pairs: Vec<(tty_text::Text, tty_text::Text)>,
18 focused_pair: usize,
19 key_focused: bool,
20 evaluation: Option<(DependencyId, Evaluation)>,
21}
22
23impl KeyValueStep {
24 pub fn new(prompt: &str) -> Self {
25 Self {
26 prompt: prompt.to_string(),
27 pairs: vec![(tty_text::Text::new(false), tty_text::Text::new(false))],
28 focused_pair: 0,
29 key_focused: true,
30 evaluation: None,
31 }
32 }
33
34 pub fn set_evaluation(&mut self, evaluation: Evaluation) -> DependencyId {
35 let id = DependencyId::new();
36 self.evaluation = Some((id, evaluation));
37 id
38 }
39}
40
41impl Step for KeyValueStep {
42 fn initialize(&mut self, _dependency_state: &mut DependencyState, _index: usize) {}
43
44 fn render(
45 &self,
46 interface: &mut Interface,
47 _dependency_state: &DependencyState,
48 mut position: Position,
49 is_focused: bool,
50 ) -> u16 {
51 for (pair_index, (key, value)) in self.pairs.iter().enumerate() {
52 let line = if value.value().is_empty() {
53 key.value()
54 } else {
55 format!("{}: {}", key.value(), value.value())
56 };
57
58 interface.set(position, &line);
59
60 if is_focused && pair_index == self.focused_pair {
61 let cursor = pos!(
62 if self.key_focused {
63 key.cursor().0
64 } else {
65 key.value().len() + 2 + value.cursor().0
66 } as u16,
67 position.y()
68 );
69
70 interface.set_cursor(Some(cursor));
71 }
72
73 position = pos!(position.x(), position.y() + 1);
74 }
75
76 self.pairs.len() as u16
77 }
78
79 fn update(
80 &mut self,
81 _dependency_state: &mut DependencyState,
82 input: KeyEvent,
83 ) -> Option<InputResult> {
84 let text = if self.key_focused {
85 &mut self.pairs[self.focused_pair].0
86 } else {
87 &mut self.pairs[self.focused_pair].1
88 };
89
90 match input.code {
91 KeyCode::Enter | KeyCode::Tab => {
92 if text.value().is_empty() {
93 if self.key_focused {
94 if self.focused_pair > 0 {
95 self.pairs.remove(self.focused_pair);
96 self.focused_pair -= 1;
97 }
98
99 return Some(InputResult::AdvanceForm);
101 } else {
102 self.key_focused = true;
104 self.focused_pair += 1;
105 if self.focused_pair == self.pairs.len() {
106 self.pairs
107 .push((tty_text::Text::new(false), tty_text::Text::new(false)));
108 }
109 }
110 } else {
111 if self.key_focused {
112 self.key_focused = false;
114 } else {
115 self.key_focused = true;
117 self.focused_pair += 1;
118 if self.focused_pair == self.pairs.len() {
119 self.pairs
120 .push((tty_text::Text::new(false), tty_text::Text::new(false)));
121 }
122 }
123 }
124 }
125 KeyCode::Esc | KeyCode::BackTab => {
126 if !self.key_focused {
127 self.key_focused = true;
128 } else {
129 if self.focused_pair > 0 {
130 self.focused_pair -= 1;
131 self.key_focused = false;
132 } else {
133 return Some(InputResult::RetreatForm);
134 }
135 }
136 }
137 KeyCode::Char(ch) => text.handle_input(Key::Char(ch)),
138 KeyCode::Backspace => {
139 if text.value().is_empty() {
140 if !self.key_focused {
141 self.key_focused = true;
142 } else {
143 if self.focused_pair > 0 {
144 self.pairs.remove(self.focused_pair);
145 self.focused_pair -= 1;
146 self.key_focused = false;
147 } else {
148 return Some(InputResult::RetreatForm);
149 }
150 }
151 } else {
152 text.handle_input(Key::Backspace);
153 }
154 }
155 KeyCode::Left => text.handle_input(Key::Left),
156 KeyCode::Right => text.handle_input(Key::Right),
157 _ => {}
158 };
159
160 None
161 }
162
163 fn help(&self) -> Segment {
164 Text::new_styled(self.prompt.to_string(), help_style()).as_segment()
165 }
166
167 fn drawer(&self) -> Option<DrawerContents> {
168 None
169 }
170
171 fn result(&self, _dependency_state: &DependencyState) -> String {
172 let mut result = String::new();
173
174 for (_, (key, value)) in self.pairs.iter().enumerate() {
175 result.push_str(&key.value());
176
177 if !value.value().is_empty() {
178 result.push_str(&format!(": {}", value.value()));
179 }
180
181 result.push('\n');
182 }
183
184 result
185 }
186
187 fn add_to(self, form: &mut Form) {
188 form.add_step(Box::new(self));
189 }
190}