1use std::io;
2
3use crossterm::event::{Event, KeyCode};
4use log::debug;
5
6use crate::{
7 app::{EventHandlerResult, EventResult},
8 pos::Pos,
9};
10
11#[allow(dead_code)]
12#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
13pub enum Select {
14 None,
15 Single,
16 Multi,
17}
18
19#[derive(Debug, Clone)]
24pub struct Input {
25 pub pos: Pos,
26 pub length: u16,
27 pub name: String,
28 pub value: String,
29 pub default_value: String,
30 pub allowed_characters: Option<Vec<char>>,
31 pub mask_char: Option<char>,
32 pub select: Select,
33 pub select_static: Vec<(String, String)>,
34}
35
36impl Ord for Input {
37 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
38 self.pos.cmp(&other.pos)
39 }
40}
41
42impl Eq for Input {}
43
44impl PartialEq for Input {
45 fn eq(&self, other: &Self) -> bool {
46 self.pos == other.pos }
48}
49
50impl PartialOrd for Input {
51 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
52 Some(self.cmp(other))
53 }
54}
55
56impl Input {
57 pub(crate) fn has_focus(&self, cursor: Pos) -> bool {
58 cursor.within(self.pos, self.length).is_some()
59 }
60
61 pub fn builder(pos: impl Into<Pos>, length: u16, name: impl Into<String>) -> InputBuilder {
63 InputBuilder {
64 pos: pos.into(),
65 length,
66 name: name.into(),
67 value: Default::default(),
68 default_value: Default::default(),
69 allowed_characters: Default::default(),
70 mask_char: Default::default(),
71 select: Select::None,
72 select_static: Default::default(),
73 }
74 }
75
76 pub(crate) fn event_handler(
77 &mut self,
78 event: &Event,
79 current_pos: &mut Pos,
80 ) -> std::io::Result<EventHandlerResult> {
81 match event {
82 Event::Key(k) if k.code == KeyCode::Backspace => {
83 self.key_backspace(current_pos)?;
84 }
85 Event::Key(k) if k.code == KeyCode::Delete => {
86 self.key_delete(current_pos)?;
87 }
88 Event::Key(k) if k.modifiers.is_empty() => {
89 if let KeyCode::Char(c) = k.code {
90 if let Some(ac) = &self.allowed_characters {
91 if !ac.contains(&c) {
92 debug!("{} is not an allowed character for input {}", c, self.name);
93 return Ok(EventHandlerResult::Handled(EventResult::None));
94 }
95 }
96
97 self.key(c, current_pos);
98 } else {
99 return Ok(EventHandlerResult::NotHandled);
100 }
101 }
102 _ => return Ok(EventHandlerResult::NotHandled),
103 }
104
105 Ok(EventHandlerResult::Handled(EventResult::None))
106 }
107
108 pub(crate) fn key(&mut self, key: char, current_pos: &mut Pos) {
109 let str_pos = current_pos.x - self.pos.x;
110 *current_pos = current_pos.move_x(1, self.pos.x + self.length);
111
112 self.value = Self::set_char_in_string(&self.value, str_pos as usize, key);
113 }
114
115 pub(crate) fn set_char_in_string(s: &str, pos: usize, ch: char) -> String {
116 let mut s = s.to_string();
117
118 let len = s.chars().count();
119
120 if len <= pos {
121 let rep = " ".repeat(pos - len + 1);
122 s.push_str(&rep);
123 }
124
125 let mut output: String = s.chars().take(pos).collect();
126
127 output.push(ch);
128 output.extend(s.chars().skip(pos + 1));
129
130 output
131 }
132
133 pub(crate) fn delete_in_string(input: &str, pos: usize) -> String {
134 let input_len = input.chars().count();
135 if pos > input_len {
136 return input.to_string();
137 }
138
139 let mut output: String = input.chars().take(pos).collect();
140 output.extend(input.chars().skip(pos + 1));
141
142 output
143 }
144
145 pub(crate) fn key_backspace(&mut self, current_pos: &mut Pos) -> io::Result<()> {
146 let str_pos = current_pos.x - self.pos.x;
147
148 if str_pos == 0 {
150 return Ok(());
151 }
152
153 *current_pos = current_pos.move_x(-1, self.pos.x + self.length);
154
155 self.value = Self::delete_in_string(&self.value, (str_pos - 1) as usize);
156 Ok(())
157 }
158
159 fn key_delete(&mut self, current_pos: &Pos) -> io::Result<()> {
160 let str_pos = current_pos.x - self.pos.x;
161
162 self.value = Self::delete_in_string(&self.value, str_pos as usize);
163 Ok(())
164 }
165}
166
167pub struct InputBuilder {
168 pub pos: Pos,
169 pub length: u16,
170 pub name: String,
171 pub value: String,
172 pub default_value: String,
173 pub allowed_characters: Option<Vec<char>>,
174 pub mask_char: Option<char>,
175 pub select: Select,
176 pub select_static: Vec<(String, String)>,
177}
178
179impl InputBuilder {
180 pub fn with_value(mut self, value: impl Into<String>) -> Self {
181 self.value = value.into();
182
183 self
184 }
185
186 pub fn with_default_value(mut self, default_value: impl Into<String>) -> Self {
187 self.default_value = default_value.into();
188
189 self
190 }
191
192 pub fn with_allowed_characters(
193 mut self,
194 allowed_characters: impl IntoIterator<Item = char>,
195 ) -> Self {
196 self.allowed_characters = Some(allowed_characters.into_iter().collect());
197
198 self
199 }
200
201 pub fn with_mask_char(mut self, mask_char: char) -> Self {
202 self.mask_char = Some(mask_char);
203
204 self
205 }
206
207 #[allow(dead_code)]
208 pub fn with_select_static(mut self, select_static: &[(String, String)]) -> Self {
209 self.select_static = select_static.into();
210
211 self
212 }
213
214 pub fn build(self) -> Input {
215 Input {
216 pos: self.pos,
217 length: self.length,
218 name: self.name,
219 value: self.value,
220 default_value: self.default_value,
221 allowed_characters: self.allowed_characters,
222 mask_char: self.mask_char,
223 select: self.select,
224 select_static: self.select_static,
225 }
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 #[test]
234 fn set_char_in_string_plain() {
235 let input = "1234567890";
236 let pos = 2;
237 let ch = 'a';
238 let expected = "12a4567890";
239
240 let output = Input::set_char_in_string(input, pos, ch);
241
242 assert_eq!(output, expected);
243 }
244
245 #[test]
246 fn set_char_in_string_nls() {
247 let input = "1æ34567890";
248 let pos = 2;
249 let ch = 'ö';
250 let expected = "1æö4567890";
251
252 let output = Input::set_char_in_string(input, pos, ch);
253
254 assert_eq!(output, expected);
255 }
256}