1use std::cell::{Cell, RefCell};
11use std::rc::{Rc, Weak};
12
13use crate::key_event::KeyEvent;
14
15#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct ImeEditorState {
22 pub text: String,
24 pub selection_start: usize,
26 pub selection_end: usize,
28 pub composition: Option<(usize, usize)>,
30 pub single_line: bool,
33}
34
35#[derive(Clone, Debug, PartialEq)]
40pub struct ImeCaretGeometry {
41 pub caret_xs: Vec<f32>,
44 pub top: f32,
46 pub line_height: f32,
48}
49
50pub trait FocusedTextFieldHandler {
53 fn handle_key(&self, event: &KeyEvent) -> bool;
55 fn insert_text(&self, text: &str);
57 fn delete_surrounding(&self, before_bytes: usize, after_bytes: usize);
59 fn copy_selection(&self) -> Option<String>;
61 fn cut_selection(&self) -> Option<String>;
63 fn select_all(&self) {}
65 fn set_composition(&self, text: &str, cursor: Option<(usize, usize)>);
70 fn finish_composition(&self) {}
74 fn set_composing_region(&self, start_bytes: usize, end_bytes: usize) {
79 let _ = (start_bytes, end_bytes);
80 }
81 fn set_selection(&self, start_bytes: usize, end_bytes: usize) {
87 let _ = (start_bytes, end_bytes);
88 }
89 fn editor_state(&self) -> Option<ImeEditorState> {
93 None
94 }
95 fn caret_geometry(&self) -> Option<ImeCaretGeometry> {
99 None
100 }
101}
102
103pub(crate) struct TextFieldFocusState {
104 focused_field: RefCell<Option<Weak<RefCell<bool>>>>,
105 focused_handler: RefCell<Option<Rc<dyn FocusedTextFieldHandler>>>,
106 focused_modal_depth: Cell<usize>,
110}
111
112impl TextFieldFocusState {
113 pub(crate) fn new() -> Self {
114 Self {
115 focused_field: RefCell::new(None),
116 focused_handler: RefCell::new(None),
117 focused_modal_depth: Cell::new(0),
118 }
119 }
120
121 fn request_focus(
122 &self,
123 is_focused: Rc<RefCell<bool>>,
124 handler: Rc<dyn FocusedTextFieldHandler>,
125 modal_depth: usize,
126 ) {
127 let mut current = self.focused_field.borrow_mut();
128
129 if let Some(ref weak) = *current {
130 if let Some(old_focused) = weak.upgrade() {
131 *old_focused.borrow_mut() = false;
132 }
133 }
134
135 *is_focused.borrow_mut() = true;
136 *current = Some(Rc::downgrade(&is_focused));
137 *self.focused_handler.borrow_mut() = Some(handler);
138 self.focused_modal_depth.set(modal_depth);
139 }
140
141 fn clear_focus(&self) {
142 let mut current = self.focused_field.borrow_mut();
143
144 if let Some(ref weak) = *current {
145 if let Some(focused) = weak.upgrade() {
146 *focused.borrow_mut() = false;
147 }
148 }
149
150 *current = None;
151 *self.focused_handler.borrow_mut() = None;
152 self.focused_modal_depth.set(0);
153 }
154
155 fn focused_at_depth(&self, depth: usize) -> bool {
157 self.has_focused_field() && self.focused_modal_depth.get() == depth
158 }
159
160 fn has_focused_field(&self) -> bool {
161 let mut current = self.focused_field.borrow_mut();
162 if let Some(ref weak) = *current {
163 if weak.upgrade().is_some() {
164 return true;
165 }
166 *current = None;
167 *self.focused_handler.borrow_mut() = None;
168 crate::cursor_animation::stop_cursor_blink();
169 }
170 false
171 }
172
173 fn focused_handler(&self) -> Option<Rc<dyn FocusedTextFieldHandler>> {
174 if !self.has_focused_field() {
175 return None;
176 }
177 self.focused_handler.borrow().as_ref().cloned()
178 }
179
180 fn dispatch_key_event(&self, event: &KeyEvent) -> bool {
181 if let Some(handler) = self.focused_handler() {
182 handler.handle_key(event)
183 } else {
184 false
185 }
186 }
187
188 fn dispatch_paste(&self, text: &str) -> bool {
189 if let Some(handler) = self.focused_handler() {
190 handler.insert_text(text);
191 true
192 } else {
193 false
194 }
195 }
196
197 fn dispatch_delete_surrounding(&self, before_bytes: usize, after_bytes: usize) -> bool {
198 if let Some(handler) = self.focused_handler() {
199 handler.delete_surrounding(before_bytes, after_bytes);
200 true
201 } else {
202 false
203 }
204 }
205
206 fn dispatch_copy(&self) -> Option<String> {
207 self.focused_handler()
208 .and_then(|handler| handler.copy_selection())
209 }
210
211 fn dispatch_cut(&self) -> Option<String> {
212 self.focused_handler()
213 .and_then(|handler| handler.cut_selection())
214 }
215
216 fn dispatch_select_all(&self) -> bool {
217 if let Some(handler) = self.focused_handler() {
218 handler.select_all();
219 true
220 } else {
221 false
222 }
223 }
224
225 fn dispatch_ime_preedit(&self, text: &str, cursor: Option<(usize, usize)>) -> bool {
226 if let Some(handler) = self.focused_handler() {
227 handler.set_composition(text, cursor);
228 true
229 } else {
230 false
231 }
232 }
233
234 fn dispatch_ime_finish_composing(&self) -> bool {
235 if let Some(handler) = self.focused_handler() {
236 handler.finish_composition();
237 true
238 } else {
239 false
240 }
241 }
242
243 fn dispatch_ime_set_composing_region(&self, start_bytes: usize, end_bytes: usize) -> bool {
244 if let Some(handler) = self.focused_handler() {
245 handler.set_composing_region(start_bytes, end_bytes);
246 true
247 } else {
248 false
249 }
250 }
251
252 fn dispatch_ime_set_selection(&self, start_bytes: usize, end_bytes: usize) -> bool {
253 if let Some(handler) = self.focused_handler() {
254 handler.set_selection(start_bytes, end_bytes);
255 true
256 } else {
257 false
258 }
259 }
260
261 fn focused_editor_state(&self) -> Option<ImeEditorState> {
262 self.focused_handler()
263 .and_then(|handler| handler.editor_state())
264 }
265
266 fn focused_caret_geometry(&self) -> Option<ImeCaretGeometry> {
267 self.focused_handler()
268 .and_then(|handler| handler.caret_geometry())
269 }
270}
271
272pub fn request_focus(
285 is_focused: Rc<RefCell<bool>>,
286 handler: Rc<dyn FocusedTextFieldHandler>,
287 modal_depth: usize,
288) {
289 if modal_depth < crate::modal::current_modal_depth() {
290 return;
291 }
292
293 crate::render_state::with_text_field_focus(|state| {
294 state.request_focus(is_focused, handler, modal_depth)
295 });
296
297 crate::cursor_animation::start_cursor_blink();
299
300 crate::text_input_session::notify_text_input_focus_gained();
303
304 crate::request_render_invalidation();
307}
308
309pub(crate) fn clear_focus_for_closed_modal(depth: usize) {
315 let owns_focus =
316 crate::render_state::with_text_field_focus(|state| state.focused_at_depth(depth));
317 if owns_focus {
318 clear_focus();
319 }
320}
321
322pub fn clear_focus() {
324 crate::render_state::with_text_field_focus(|state| state.clear_focus());
325
326 crate::cursor_animation::stop_cursor_blink();
328
329 crate::text_input_session::notify_text_input_focus_lost();
331
332 crate::request_render_invalidation();
333}
334
335pub fn has_focused_field() -> bool {
338 let has_focus = crate::render_state::with_text_field_focus(|state| state.has_focused_field());
339 if !has_focus {
340 crate::text_input_session::notify_text_input_focus_lost();
344 }
345 has_focus
346}
347
348pub fn dispatch_key_event(event: &KeyEvent) -> bool {
355 crate::render_state::with_text_field_focus(|state| state.dispatch_key_event(event))
356}
357
358pub fn dispatch_paste(text: &str) -> bool {
361 crate::render_state::with_text_field_focus(|state| state.dispatch_paste(text))
362}
363
364pub fn dispatch_delete_surrounding(before_bytes: usize, after_bytes: usize) -> bool {
367 crate::render_state::with_text_field_focus(|state| {
368 state.dispatch_delete_surrounding(before_bytes, after_bytes)
369 })
370}
371
372pub fn dispatch_copy() -> Option<String> {
375 crate::render_state::with_text_field_focus(|state| state.dispatch_copy())
376}
377
378pub fn dispatch_cut() -> Option<String> {
381 crate::render_state::with_text_field_focus(|state| state.dispatch_cut())
382}
383
384pub fn dispatch_select_all() -> bool {
387 crate::render_state::with_text_field_focus(|state| state.dispatch_select_all())
388}
389
390pub fn dispatch_ime_preedit(text: &str, cursor: Option<(usize, usize)>) -> bool {
394 crate::render_state::with_text_field_focus(|state| state.dispatch_ime_preedit(text, cursor))
395}
396
397pub fn dispatch_ime_finish_composing() -> bool {
401 crate::render_state::with_text_field_focus(|state| state.dispatch_ime_finish_composing())
402}
403
404pub fn dispatch_ime_set_composing_region(start_bytes: usize, end_bytes: usize) -> bool {
408 crate::render_state::with_text_field_focus(|state| {
409 state.dispatch_ime_set_composing_region(start_bytes, end_bytes)
410 })
411}
412
413pub fn dispatch_ime_set_selection(start_bytes: usize, end_bytes: usize) -> bool {
418 crate::render_state::with_text_field_focus(|state| {
419 state.dispatch_ime_set_selection(start_bytes, end_bytes)
420 })
421}
422
423pub fn focused_editor_state() -> Option<ImeEditorState> {
427 crate::render_state::with_text_field_focus(|state| state.focused_editor_state())
428}
429
430pub fn focused_caret_geometry() -> Option<ImeCaretGeometry> {
433 crate::render_state::with_text_field_focus(|state| state.focused_caret_geometry())
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439
440 struct MockHandler;
442 impl FocusedTextFieldHandler for MockHandler {
443 fn handle_key(&self, _: &KeyEvent) -> bool {
444 false
445 }
446 fn insert_text(&self, _: &str) {}
447 fn delete_surrounding(&self, _: usize, _: usize) {}
448 fn copy_selection(&self) -> Option<String> {
449 None
450 }
451 fn cut_selection(&self) -> Option<String> {
452 None
453 }
454 fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {}
455 }
456
457 fn mock_handler() -> Rc<dyn FocusedTextFieldHandler> {
458 Rc::new(MockHandler)
459 }
460
461 #[test]
462 fn request_focus_sets_flag() {
463 let _app_context = crate::render_state::app_context_test_scope();
464 let focus = Rc::new(RefCell::new(false));
465 request_focus(focus.clone(), mock_handler(), 0);
466 assert!(*focus.borrow());
467 clear_focus();
468 }
469
470 #[test]
471 fn request_focus_clears_previous() {
472 let _app_context = crate::render_state::app_context_test_scope();
473 let focus1 = Rc::new(RefCell::new(false));
474 let focus2 = Rc::new(RefCell::new(false));
475
476 request_focus(focus1.clone(), mock_handler(), 0);
477 assert!(*focus1.borrow());
478
479 request_focus(focus2.clone(), mock_handler(), 0);
480 assert!(!*focus1.borrow()); assert!(*focus2.borrow()); clear_focus();
483 }
484
485 #[test]
486 fn clear_focus_unfocuses_current() {
487 let _app_context = crate::render_state::app_context_test_scope();
488 let focus = Rc::new(RefCell::new(false));
489 request_focus(focus.clone(), mock_handler(), 0);
490 assert!(*focus.borrow());
491
492 clear_focus();
493 assert!(!*focus.borrow());
494 }
495
496 #[derive(Default)]
497 struct DispatchRecordingHandler {
498 key_count: Cell<usize>,
499 insert_count: Cell<usize>,
500 delete_count: Cell<usize>,
501 copy_count: Cell<usize>,
502 cut_count: Cell<usize>,
503 preedit_count: Cell<usize>,
504 last_delete: Cell<Option<(usize, usize)>>,
505 }
506
507 impl DispatchRecordingHandler {
508 fn bump(cell: &Cell<usize>) {
509 cell.set(cell.get() + 1);
510 }
511
512 fn total_calls(&self) -> usize {
513 self.key_count.get()
514 + self.insert_count.get()
515 + self.delete_count.get()
516 + self.copy_count.get()
517 + self.cut_count.get()
518 + self.preedit_count.get()
519 }
520 }
521
522 impl FocusedTextFieldHandler for DispatchRecordingHandler {
523 fn handle_key(&self, _: &KeyEvent) -> bool {
524 Self::bump(&self.key_count);
525 true
526 }
527
528 fn insert_text(&self, _: &str) {
529 Self::bump(&self.insert_count);
530 }
531
532 fn delete_surrounding(&self, before_bytes: usize, after_bytes: usize) {
533 Self::bump(&self.delete_count);
534 self.last_delete.set(Some((before_bytes, after_bytes)));
535 }
536
537 fn copy_selection(&self) -> Option<String> {
538 Self::bump(&self.copy_count);
539 Some("copy".to_string())
540 }
541
542 fn cut_selection(&self) -> Option<String> {
543 Self::bump(&self.cut_count);
544 Some("cut".to_string())
545 }
546
547 fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {
548 Self::bump(&self.preedit_count);
549 }
550 }
551
552 #[test]
553 fn dispatch_delete_surrounding_calls_handler() {
554 let _app_context = crate::render_state::app_context_test_scope();
555 let focus = Rc::new(RefCell::new(false));
556 let handler = Rc::new(DispatchRecordingHandler::default());
557
558 request_focus(Rc::clone(&focus), handler.clone(), 0);
559 assert!(dispatch_delete_surrounding(3, 1));
560 assert_eq!(handler.last_delete.get(), Some((3, 1)));
561
562 clear_focus();
563 }
564
565 #[test]
566 fn dispatch_clears_stale_focus_owner_before_invoking_handler() {
567 let _app_context = crate::render_state::app_context_test_scope();
568 let handler = Rc::new(DispatchRecordingHandler::default());
569
570 {
571 let focus = Rc::new(RefCell::new(false));
572 request_focus(Rc::clone(&focus), handler.clone(), 0);
573 assert!(has_focused_field());
574 }
575
576 let key_event = KeyEvent::key_down(crate::key_event::KeyCode::A, "a");
577
578 assert!(!dispatch_key_event(&key_event));
579 assert!(!dispatch_paste("stale paste"));
580 assert!(!dispatch_delete_surrounding(2, 1));
581 assert_eq!(dispatch_copy(), None);
582 assert_eq!(dispatch_cut(), None);
583 assert!(!dispatch_ime_preedit("preedit", Some((1, 1))));
584 assert!(!has_focused_field());
585 assert_eq!(
586 handler.total_calls(),
587 0,
588 "stale focused-field handlers must not receive input"
589 );
590 }
591
592 #[derive(Default)]
593 struct KeyboardProbe {
594 calls: RefCell<Vec<&'static str>>,
595 }
596
597 impl crate::text_input_session::PlatformTextInputHandler for KeyboardProbe {
598 fn show_keyboard(&self) {
599 self.calls.borrow_mut().push("show");
600 }
601
602 fn hide_keyboard(&self) {
603 self.calls.borrow_mut().push("hide");
604 }
605 }
606
607 #[test]
608 fn focus_transitions_drive_platform_keyboard() {
609 let _app_context = crate::render_state::app_context_test_scope();
610 let keyboard = Rc::new(KeyboardProbe::default());
611 crate::text_input_session::set_platform_text_input_handler(keyboard.clone());
612
613 let focus = Rc::new(RefCell::new(false));
614 request_focus(focus.clone(), mock_handler(), 0);
615 assert_eq!(*keyboard.calls.borrow(), vec!["show"]);
616
617 request_focus(focus, mock_handler(), 0);
620 assert_eq!(*keyboard.calls.borrow(), vec!["show", "show"]);
621
622 clear_focus();
623 assert_eq!(*keyboard.calls.borrow(), vec!["show", "show", "hide"]);
624 }
625
626 #[test]
627 fn stale_focus_detection_hides_platform_keyboard() {
628 let _app_context = crate::render_state::app_context_test_scope();
629 let keyboard = Rc::new(KeyboardProbe::default());
630 crate::text_input_session::set_platform_text_input_handler(keyboard.clone());
631
632 {
633 let focus = Rc::new(RefCell::new(false));
634 request_focus(focus, mock_handler(), 0);
635 }
638
639 assert!(!has_focused_field());
640 assert_eq!(*keyboard.calls.borrow(), vec!["show", "hide"]);
641
642 assert!(!has_focused_field());
644 assert_eq!(*keyboard.calls.borrow(), vec!["show", "hide"]);
645 }
646
647 #[test]
648 fn text_field_focus_is_scoped_by_app_context() {
649 let _app_context = crate::render_state::app_context_test_scope();
650 let first = crate::render_state::AppContext::new_with_density(1.0);
651 let second = crate::render_state::AppContext::new_with_density(1.0);
652 let first_focus = Rc::new(RefCell::new(false));
653 let second_focus = Rc::new(RefCell::new(false));
654
655 first.enter(|| {
656 request_focus(first_focus.clone(), mock_handler(), 0);
657 assert!(has_focused_field());
658 assert!(*first_focus.borrow());
659 });
660
661 second.enter(|| {
662 assert!(!has_focused_field());
663 request_focus(second_focus.clone(), mock_handler(), 0);
664 assert!(has_focused_field());
665 assert!(*second_focus.borrow());
666 });
667
668 first.enter(|| {
669 assert!(has_focused_field());
670 assert!(*first_focus.borrow());
671 clear_focus();
672 assert!(!has_focused_field());
673 assert!(!*first_focus.borrow());
674 });
675
676 second.enter(|| {
677 assert!(has_focused_field());
678 assert!(*second_focus.borrow());
679 clear_focus();
680 });
681 }
682}