1use std::rc::Rc;
12
13use crabshell_reactive::{Signal, untrack};
14
15use crate::cx::Cx;
16use crate::edit::Keyed;
17use crate::input::{Handler, run_isolated};
18use crate::text_input::AcceptHandler;
19use crate::tree::{Change, NodeId, NodeKind, Tree, borrow};
20use crate::window::Window;
21
22#[derive(Clone, Debug, PartialEq, Eq)]
24pub struct KeyEvent {
25 pub key: Key,
27 pub text: Option<String>,
30 pub modifiers: Modifiers,
32 pub repeat: bool,
34}
35
36#[derive(Clone, Debug, PartialEq, Eq)]
38pub enum Key {
39 Enter,
41 Escape,
43 Tab,
45 Backspace,
47 Delete,
49 Left,
51 Right,
53 Up,
55 Down,
57 Home,
59 End,
61 PageUp,
63 PageDown,
65 Function(u8),
67 Character(char),
70 Other(u32),
73}
74
75#[derive(Clone, Debug, PartialEq, Eq)]
77pub enum Clipboard {
78 Copy(String),
80 Paste,
83}
84
85#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
87pub struct Modifiers {
88 pub ctrl: bool,
90 pub alt: bool,
92 pub shift: bool,
94 pub logo: bool,
96}
97
98pub(crate) type KeyHandler = Rc<Handler<dyn Fn(&Cx, &KeyEvent) -> bool>>;
99
100#[derive(Default)]
102pub(crate) struct Keyboard {
103 pub(crate) entered: bool,
105 pub(crate) focused: Option<NodeId>,
107 pub(crate) handler: Option<KeyHandler>,
109}
110
111impl Keyboard {
112 pub(crate) fn forget(&mut self, removed: &[NodeId]) {
114 if self.focused.is_some_and(|id| removed.contains(&id)) {
115 self.focused = None;
116 }
117 }
118}
119
120impl Window {
121 pub fn keyboard_enter(&self) {
124 let mut tree = borrow(&self.tree);
125 tree.keyboard.entered = true;
126 let focused = tree.keyboard.focused;
127 tree.show_focus(focused, true);
128 }
129
130 pub fn keyboard_leave(&self) {
133 let mut tree = borrow(&self.tree);
134 tree.keyboard.entered = false;
135 let focused = tree.keyboard.focused;
136 tree.show_focus(focused, false);
137 }
138
139 pub fn key(&self, event: &KeyEvent) -> Option<Clipboard> {
149 let typed = borrow(&self.tree).input_key(event);
150 match typed {
151 Some(Typed::Edited(signal, text, clipboard)) => {
152 signal.try_set(text);
154 return clipboard;
155 }
156 Some(Typed::Clipboard(clipboard)) => return Some(clipboard),
157 Some(Typed::Moved) => return None,
158 Some(Typed::Accept(call)) => {
159 call.run();
160 return None;
161 }
162 None => {}
163 }
164 let (button, window) = {
165 let tree = self.tree.borrow();
166 (tree.focused_key_call(), tree.window_key_call())
167 };
168 for call in [button, window].into_iter().flatten() {
169 if call.run(event) {
170 return None;
171 }
172 }
173 let click = borrow(&self.tree).default_key(event);
174 if let Some(click) = click {
175 click.run();
176 }
177 None
178 }
179
180 pub fn focused(&self) -> Option<String> {
182 let tree = self.tree.borrow();
183 let id = tree.keyboard.focused?;
184 tree.node(id).map(|node| node.path.to_string())
185 }
186}
187
188impl Tree {
189 fn show_focus(&mut self, id: Option<NodeId>, shown: bool) {
193 let Some(id) = id else {
194 return;
195 };
196 if matches!(
197 self.node(id).map(|node| &node.kind),
198 Some(NodeKind::Input(_))
199 ) {
200 self.update(id, |node| match &mut node.kind {
201 NodeKind::Input(input) if input.focused != shown => {
202 input.focused = shown;
203 Change::Paint
204 }
205 _ => Change::None,
206 });
207 } else {
208 self.update_button_style(id, |button| button.focused = shown);
209 }
210 }
211
212 pub(crate) fn focus(&mut self, id: Option<NodeId>) {
214 let old = std::mem::replace(&mut self.keyboard.focused, id);
215 if old != id {
216 if let Some(old) = old
218 && matches!(self.node(old).map(|node| &node.kind), Some(NodeKind::Input(input)) if input.preedit.is_some())
219 {
220 self.update_input(old, |input| {
221 input.preedit = None;
222 input.refresh();
223 });
224 }
225 self.show_focus(old, false);
226 let entered = self.keyboard.entered;
227 self.show_focus(id, entered);
228 }
229 }
230
231 fn input_key(&mut self, event: &KeyEvent) -> Option<Typed> {
234 let id = self.keyboard.focused?;
235 let NodeKind::Input(input) = &mut self.node_mut(id)?.kind else {
236 return None;
237 };
238 let keyed = input.edit.key(event, !input.password);
239 let accept = keyed == Keyed::Accept;
240 let typed = match keyed {
241 Keyed::Unused => return None,
242 Keyed::Moved => Typed::Moved,
243 Keyed::Edited => Typed::Edited(input.signal, input.edit.text.clone(), None),
244 Keyed::Copy(text) => Typed::Clipboard(Clipboard::Copy(text)),
245 Keyed::Cut(text) => Typed::Edited(
246 input.signal,
247 input.edit.text.clone(),
248 Some(Clipboard::Copy(text)),
249 ),
250 Keyed::Paste => Typed::Clipboard(Clipboard::Paste),
251 Keyed::Accept => {
252 let handler = untrack(|| input.on_accept.try_with(Option::clone)).flatten();
253 let text = input.edit.text.clone();
254 let node = self.node(id)?;
255 Typed::Accept(AcceptCall {
257 cx: node.cx.clone(),
258 path: Rc::clone(&node.path),
259 handler: handler?,
260 text,
261 })
262 }
263 };
264 if !accept {
265 self.update_input(id, |input| {
266 input.refresh();
267 });
268 }
269 Some(typed)
270 }
271
272 fn focusable(&self) -> Vec<NodeId> {
275 let mut found = Vec::new();
276 let mut pending = vec![self.root];
277 while let Some(id) = pending.pop() {
278 if let Some(node) = self.node(id)
279 && matches!(node.kind, NodeKind::Button(_) | NodeKind::Input(_))
280 {
281 found.push(id);
282 }
283 let mut children = self.taffy.children(id).unwrap_or_default();
284 children.reverse();
285 pending.extend(children);
286 }
287 found
288 }
289
290 fn focused_key_call(&self) -> Option<KeyCall> {
291 let id = self.keyboard.focused?;
292 let node = self.node(id)?;
293 let NodeKind::Button(button) = &node.kind else {
294 return None;
295 };
296 let handler = untrack(|| button.handlers.try_with(|handlers| handlers.key.clone()))??;
298 Some(KeyCall {
299 cx: node.cx.clone(),
300 path: Rc::clone(&node.path),
301 handler,
302 })
303 }
304
305 fn window_key_call(&self) -> Option<KeyCall> {
306 let node = self.node(self.root)?;
307 Some(KeyCall {
308 cx: node.cx.clone(),
309 path: Rc::clone(&node.path),
310 handler: self.keyboard.handler.clone()?,
311 })
312 }
313
314 fn default_key(&mut self, event: &KeyEvent) -> Option<crate::input::Call> {
317 match event.key {
318 Key::Tab => {
319 let buttons = self.focusable();
320 let at = self
321 .keyboard
322 .focused
323 .and_then(|id| buttons.iter().position(|button| *button == id));
324 let count = buttons.len();
325 let next = match (at, event.modifiers.shift) {
326 _ if count == 0 => None,
327 (None, false) => Some(0),
328 (None, true) => Some(count - 1),
329 (Some(at), false) => Some((at + 1) % count),
330 (Some(at), true) => Some((at + count - 1) % count),
331 };
332 self.focus(next.map(|next| buttons[next]));
333 None
334 }
335 Key::Enter | Key::Character(' ') => self.click(self.keyboard.focused?),
336 _ => None,
337 }
338 }
339}
340
341enum Typed {
343 Edited(Signal<String>, String, Option<Clipboard>),
345 Clipboard(Clipboard),
347 Moved,
349 Accept(AcceptCall),
351}
352
353struct AcceptCall {
355 cx: Cx,
356 path: Rc<str>,
357 handler: AcceptHandler,
358 text: String,
359}
360
361impl AcceptCall {
362 fn run(self) {
363 run_isolated(&self.path, "on_accept", self.handler, |f| {
364 f(&self.cx, &self.text)
365 });
366 }
367}
368
369struct KeyCall {
371 cx: Cx,
372 path: Rc<str>,
373 handler: KeyHandler,
374}
375
376impl KeyCall {
377 fn run(self, event: &KeyEvent) -> bool {
379 run_isolated(&self.path, "on_key", self.handler, |f| f(&self.cx, event)).unwrap_or(false)
380 }
381}