1use std::ops::Deref;
2
3use anathema::component::*;
4use anathema::default_widgets::Overflow;
5use anathema::prelude::*;
6
7#[derive(Debug, Copy, Clone)]
11pub enum InputChange {
12 Insert(char, usize),
13 Remove(char, usize),
14}
15
16impl InputChange {
17 pub fn remove(character: char, pos: i32) -> Self {
18 Self::Remove(character, pos as usize)
19 }
20
21 pub fn insert(character: char, pos: i32) -> Self {
22 Self::Insert(character, pos as usize)
23 }
24}
25
26pub struct Text(String);
30
31impl Deref for Text {
32 type Target = str;
33
34 fn deref(&self) -> &Self::Target {
35 self.0.deref()
36 }
37}
38
39#[derive(Debug, Default, State)]
43pub struct InputState {
44 text: Value<String>,
45 screen_cursor: Value<i32>,
46}
47
48impl InputState {
49 pub fn new() -> Self {
50 Self {
51 text: String::new().into(),
52 screen_cursor: 0.into(),
53 }
54 }
55
56 fn update_cursor(&mut self, screen_cursor: i32) {
58 self.screen_cursor.set(screen_cursor);
59 }
60
61 fn char_count(&self) -> usize {
62 self.text.to_ref().chars().count()
63 }
64}
65
66#[derive(Debug, Default)]
70pub struct Input {
71 cursor: i32,
72}
73
74impl Input {
75 const ON_BLUR: &str = "on_blur";
76 const ON_CHANGE: &str = "on_change";
77 const ON_ENTER: &str = "on_enter";
78 const ON_FOCUS: &str = "on_focus";
79 pub const TEMPLATE: &'static str = include_str!("templates/input.aml");
80
81 pub fn new() -> Self {
82 Self { cursor: 0 }
83 }
84
85 pub fn template() -> SourceKind {
86 Self::TEMPLATE.to_template()
87 }
88
89 fn update_cursor(&mut self, state: &mut InputState, overflow: &mut Overflow, width: i32) {
90 let mut display_cursor = self.cursor - overflow.offset().x;
91
92 if display_cursor < 0 {
93 overflow.scroll_right_by(display_cursor);
94 display_cursor = 0;
95 }
96
97 if display_cursor >= width {
98 let offset = display_cursor + 1 - width;
99 overflow.scroll_right_by(offset);
100 display_cursor = width - 1;
101 }
102
103 state.update_cursor(display_cursor);
104 }
105
106 fn insert_char(&mut self, c: char, state: &mut InputState) {
107 if self.cursor == state.char_count() as i32 {
108 state.text.to_mut().push(c);
109 } else {
110 state.text.to_mut().insert(self.cursor as usize, c);
111 }
112 self.cursor += 1;
113 }
114}
115
116impl Component for Input {
117 type Message = ();
118 type State = InputState;
119
120 fn on_key(
121 &mut self,
122 key: KeyEvent,
123 state: &mut Self::State,
124 mut children: Children<'_, '_>,
125 mut context: Context<'_, '_, Self::State>,
126 ) {
127 children.elements().by_tag("overflow").first(|el, _| {
128 let width = el.size().width as i32;
129 let overflow = el.to::<Overflow>();
130
131 match key.code {
132 KeyCode::Char(c) => {
133 self.insert_char(c, state);
134
135 let change = InputChange::insert(c, self.cursor);
136 context.publish(Self::ON_CHANGE, change);
137 }
138 KeyCode::Backspace if self.cursor > 0 => {
139 self.cursor -= 1;
140 let c = state.text.to_mut().remove(self.cursor as usize);
141
142 let change = InputChange::remove(c, self.cursor);
143 context.publish(Self::ON_CHANGE, change);
144 }
145 KeyCode::Enter if !state.text.to_ref().is_empty() => {
146 let clear_on_enter = context
147 .attributes
148 .get_as::<bool>("clear_on_enter")
149 .unwrap_or(true);
150 let text = match clear_on_enter {
151 true => {
152 self.cursor = 0;
153 std::mem::take(&mut *state.text.to_mut())
154 }
155 false => state.text.to_ref().clone(),
156 };
157
158 context.publish(Self::ON_ENTER, Text(text));
159 }
160 KeyCode::Left if self.cursor > 0 => self.cursor -= 1,
161 KeyCode::Right if state.char_count() as i32 > self.cursor => self.cursor += 1,
162 KeyCode::Home => self.cursor = 0,
163 KeyCode::End => self.cursor = state.char_count() as i32,
164 KeyCode::Delete if state.char_count() > 0 => {
165 let c = state.text.to_mut().remove(self.cursor as usize);
166 let change = InputChange::remove(c, self.cursor);
167 context.publish(Self::ON_CHANGE, change);
168 return;
169 }
170 _ => (),
171 }
172
173 self.update_cursor(state, overflow, width);
174 });
175 }
176
177 fn on_blur(
178 &mut self,
179 _: &mut Self::State,
180 _: Children<'_, '_>,
181 mut context: Context<'_, '_, Self::State>,
182 ) {
183 context.publish(Self::ON_BLUR, ());
184 }
185
186 fn on_focus(
187 &mut self,
188 _: &mut Self::State,
189 _: Children<'_, '_>,
190 mut context: Context<'_, '_, Self::State>,
191 ) {
192 context.publish(Self::ON_FOCUS, ());
193 }
194}