1use crate::component::{Component, EventCx, LayoutCx, MeasureCx};
2use crate::event::Event;
3use crate::geom::{Rect, Size};
4use crate::layout::Constraint;
5use crate::render::RenderCx;
6use crate::style::Style;
7
8pub struct Input {
14 text: String,
15 cursor_byte: usize,
17 focused: bool,
18 placeholder: String,
19 style: Style,
20 focus_style: Style,
21 on_submit: Option<Box<dyn FnMut(&str)>>,
23}
24
25impl Input {
26 pub fn new() -> Self {
28 Self {
29 text: String::new(),
30 cursor_byte: 0,
31 focused: false,
32 placeholder: String::new(),
33 style: Style::default(),
34 focus_style: Style::default()
35 .bg(crate::style::Color::White)
36 .fg(crate::style::Color::Black),
37 on_submit: None,
38 }
39 }
40
41 pub fn placeholder(mut self, text: impl Into<String>) -> Self {
42 self.placeholder = text.into();
43 self
44 }
45
46 pub fn style(mut self, style: Style) -> Self {
47 self.style = style;
48 self
49 }
50
51 pub fn focus_style(mut self, style: Style) -> Self {
52 self.focus_style = style;
53 self
54 }
55
56 pub fn on_submit(mut self, f: impl FnMut(&str) + 'static) -> Self {
59 self.on_submit = Some(Box::new(f));
60 self
61 }
62
63 pub fn text(&self) -> &str {
64 &self.text
65 }
66
67 fn clamp_cursor(&mut self) {
68 if self.cursor_byte > self.text.len() {
69 self.cursor_byte = self.text.len();
70 }
71 }
72
73 fn cursor_left(&mut self) {
74 if self.cursor_byte == 0 {
75 return;
76 }
77 if let Some((i, _)) = self.text.char_indices().rev().find(|&(i, _)| i < self.cursor_byte) {
79 self.cursor_byte = i;
80 } else {
81 self.cursor_byte = 0;
82 }
83 }
84
85 fn cursor_right(&mut self) {
86 if let Some((i, _)) = self
88 .text
89 .char_indices()
90 .find(|&(i, _)| i > self.cursor_byte)
91 {
92 self.cursor_byte = i;
93 } else {
94 self.cursor_byte = self.text.len();
95 }
96 }
97
98 fn cursor_char_index(&self) -> usize {
100 self.text[..self.cursor_byte].chars().count()
101 }
102}
103
104impl Component for Input {
105 fn render(&self, cx: &mut RenderCx) {
106 let placeholder_mode = self.text.is_empty() && !self.focused;
107 let display_text = if placeholder_mode {
108 &self.placeholder
109 } else {
110 &self.text
111 };
112
113 let base_style = if placeholder_mode {
114 Style::default().fg(crate::style::Color::Gray)
115 } else {
116 Style::default()
117 };
118
119 let cursor_style = Style::default()
120 .bg(crate::style::Color::White)
121 .fg(crate::style::Color::Black);
122
123 let cursor_char = if placeholder_mode {
124 0
125 } else {
126 self.cursor_char_index()
127 };
128 for (i, ch) in display_text.chars().enumerate() {
129 if i == cursor_char && self.focused {
130 cx.set_style(cursor_style.clone());
131 } else {
132 cx.set_style(base_style.clone());
133 }
134 cx.text(ch.to_string());
135 }
136
137 if self.cursor_byte >= self.text.len() && self.focused {
138 cx.set_style(cursor_style);
139 cx.text(" ");
140 }
141 }
142
143 fn measure(&self, _constraint: Constraint, _cx: &mut MeasureCx) -> Size {
144 let display = if self.text.is_empty() {
145 &self.placeholder
146 } else {
147 &self.text
148 };
149 let width: u16 = display
150 .chars()
151 .map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0) as u16)
152 .sum();
153 Size {
154 width: (width + 1),
155 height: 1,
156 }
157 }
158
159 fn event(&mut self, event: &Event, cx: &mut EventCx) {
160 match event {
161 Event::Focus => {
162 self.focused = true;
163 cx.invalidate_paint();
164 return;
165 }
166 Event::Blur => {
167 self.focused = false;
168 cx.invalidate_paint();
169 return;
170 }
171 _ => {}
172 }
173
174 if cx.phase() != crate::event::EventPhase::Target {
175 return;
176 }
177
178 if let Event::Key(key_event) = event {
179 if key_event.key == crate::event::Key::Char('c') && key_event.modifiers.ctrl {
181 cx.quit();
182 return;
183 }
184 if key_event.key == crate::event::Key::Char('d') && key_event.modifiers.ctrl {
186 if self.cursor_byte < self.text.len() {
187 let end = self.text[self.cursor_byte..]
188 .chars()
189 .next()
190 .map(|c| self.cursor_byte + c.len_utf8())
191 .unwrap_or(self.cursor_byte);
192 self.text.drain(self.cursor_byte..end);
193 cx.invalidate_paint();
194 }
195 return;
196 }
197 if key_event.modifiers.ctrl || key_event.modifiers.alt {
198 return;
199 }
200
201 self.clamp_cursor();
202
203 match &key_event.key {
204 crate::event::Key::Enter => {
205 if let Some(ref mut f) = self.on_submit {
206 f(&self.text);
207 }
208 }
209 crate::event::Key::Char(ch) => {
210 self.text.insert(self.cursor_byte, *ch);
211 self.cursor_byte += ch.len_utf8();
212 cx.invalidate_paint();
213 }
214 crate::event::Key::Backspace => {
215 if self.cursor_byte > 0 {
216 let old = self.cursor_byte;
217 self.cursor_left();
218 self.text.drain(self.cursor_byte..old);
219 cx.invalidate_paint();
220 }
221 }
222 crate::event::Key::Delete => {
223 if self.cursor_byte < self.text.len() {
224 let end = self.text[self.cursor_byte..]
225 .chars()
226 .next()
227 .map(|c| self.cursor_byte + c.len_utf8())
228 .unwrap_or(self.cursor_byte);
229 self.text.drain(self.cursor_byte..end);
230 cx.invalidate_paint();
231 }
232 }
233 crate::event::Key::Left => {
234 let old = self.cursor_byte;
235 self.cursor_left();
236 if self.cursor_byte != old {
237 cx.invalidate_paint();
238 }
239 }
240 crate::event::Key::Right => {
241 let old = self.cursor_byte;
242 self.cursor_right();
243 if self.cursor_byte != old {
244 cx.invalidate_paint();
245 }
246 }
247 crate::event::Key::Home => {
248 if self.cursor_byte != 0 {
249 self.cursor_byte = 0;
250 cx.invalidate_paint();
251 }
252 }
253 crate::event::Key::End => {
254 if self.cursor_byte != self.text.len() {
255 self.cursor_byte = self.text.len();
256 cx.invalidate_paint();
257 }
258 }
259 _ => {}
260 }
261 }
262 }
263
264 fn layout(&mut self, _rect: Rect, _cx: &mut LayoutCx) {}
265
266 fn style(&self) -> Style {
267 self.style.clone()
268 }
269}