1use std::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}
107
108impl TextFieldFocusState {
109 pub(crate) fn new() -> Self {
110 Self {
111 focused_field: RefCell::new(None),
112 focused_handler: RefCell::new(None),
113 }
114 }
115
116 fn request_focus(
117 &self,
118 is_focused: Rc<RefCell<bool>>,
119 handler: Rc<dyn FocusedTextFieldHandler>,
120 ) {
121 let mut current = self.focused_field.borrow_mut();
122
123 if let Some(ref weak) = *current {
124 if let Some(old_focused) = weak.upgrade() {
125 *old_focused.borrow_mut() = false;
126 }
127 }
128
129 *is_focused.borrow_mut() = true;
130 *current = Some(Rc::downgrade(&is_focused));
131 *self.focused_handler.borrow_mut() = Some(handler);
132 }
133
134 fn clear_focus(&self) {
135 let mut current = self.focused_field.borrow_mut();
136
137 if let Some(ref weak) = *current {
138 if let Some(focused) = weak.upgrade() {
139 *focused.borrow_mut() = false;
140 }
141 }
142
143 *current = None;
144 *self.focused_handler.borrow_mut() = None;
145 }
146
147 fn has_focused_field(&self) -> bool {
148 let mut current = self.focused_field.borrow_mut();
149 if let Some(ref weak) = *current {
150 if weak.upgrade().is_some() {
151 return true;
152 }
153 *current = None;
154 *self.focused_handler.borrow_mut() = None;
155 crate::cursor_animation::stop_cursor_blink();
156 }
157 false
158 }
159
160 fn focused_handler(&self) -> Option<Rc<dyn FocusedTextFieldHandler>> {
161 if !self.has_focused_field() {
162 return None;
163 }
164 self.focused_handler.borrow().as_ref().cloned()
165 }
166
167 fn dispatch_key_event(&self, event: &KeyEvent) -> bool {
168 if let Some(handler) = self.focused_handler() {
169 handler.handle_key(event)
170 } else {
171 false
172 }
173 }
174
175 fn dispatch_paste(&self, text: &str) -> bool {
176 if let Some(handler) = self.focused_handler() {
177 handler.insert_text(text);
178 true
179 } else {
180 false
181 }
182 }
183
184 fn dispatch_delete_surrounding(&self, before_bytes: usize, after_bytes: usize) -> bool {
185 if let Some(handler) = self.focused_handler() {
186 handler.delete_surrounding(before_bytes, after_bytes);
187 true
188 } else {
189 false
190 }
191 }
192
193 fn dispatch_copy(&self) -> Option<String> {
194 self.focused_handler()
195 .and_then(|handler| handler.copy_selection())
196 }
197
198 fn dispatch_cut(&self) -> Option<String> {
199 self.focused_handler()
200 .and_then(|handler| handler.cut_selection())
201 }
202
203 fn dispatch_select_all(&self) -> bool {
204 if let Some(handler) = self.focused_handler() {
205 handler.select_all();
206 true
207 } else {
208 false
209 }
210 }
211
212 fn dispatch_ime_preedit(&self, text: &str, cursor: Option<(usize, usize)>) -> bool {
213 if let Some(handler) = self.focused_handler() {
214 handler.set_composition(text, cursor);
215 true
216 } else {
217 false
218 }
219 }
220
221 fn dispatch_ime_finish_composing(&self) -> bool {
222 if let Some(handler) = self.focused_handler() {
223 handler.finish_composition();
224 true
225 } else {
226 false
227 }
228 }
229
230 fn dispatch_ime_set_composing_region(&self, start_bytes: usize, end_bytes: usize) -> bool {
231 if let Some(handler) = self.focused_handler() {
232 handler.set_composing_region(start_bytes, end_bytes);
233 true
234 } else {
235 false
236 }
237 }
238
239 fn dispatch_ime_set_selection(&self, start_bytes: usize, end_bytes: usize) -> bool {
240 if let Some(handler) = self.focused_handler() {
241 handler.set_selection(start_bytes, end_bytes);
242 true
243 } else {
244 false
245 }
246 }
247
248 fn focused_editor_state(&self) -> Option<ImeEditorState> {
249 self.focused_handler()
250 .and_then(|handler| handler.editor_state())
251 }
252
253 fn focused_caret_geometry(&self) -> Option<ImeCaretGeometry> {
254 self.focused_handler()
255 .and_then(|handler| handler.caret_geometry())
256 }
257}
258
259pub fn request_focus(is_focused: Rc<RefCell<bool>>, handler: Rc<dyn FocusedTextFieldHandler>) {
265 crate::render_state::with_text_field_focus(|state| state.request_focus(is_focused, handler));
266
267 crate::cursor_animation::start_cursor_blink();
269
270 crate::text_input_session::notify_text_input_focus_gained();
273
274 crate::request_render_invalidation();
277}
278
279pub fn clear_focus() {
281 crate::render_state::with_text_field_focus(|state| state.clear_focus());
282
283 crate::cursor_animation::stop_cursor_blink();
285
286 crate::text_input_session::notify_text_input_focus_lost();
288
289 crate::request_render_invalidation();
290}
291
292pub fn has_focused_field() -> bool {
295 let has_focus = crate::render_state::with_text_field_focus(|state| state.has_focused_field());
296 if !has_focus {
297 crate::text_input_session::notify_text_input_focus_lost();
301 }
302 has_focus
303}
304
305pub fn dispatch_key_event(event: &KeyEvent) -> bool {
312 crate::render_state::with_text_field_focus(|state| state.dispatch_key_event(event))
313}
314
315pub fn dispatch_paste(text: &str) -> bool {
318 crate::render_state::with_text_field_focus(|state| state.dispatch_paste(text))
319}
320
321pub fn dispatch_delete_surrounding(before_bytes: usize, after_bytes: usize) -> bool {
324 crate::render_state::with_text_field_focus(|state| {
325 state.dispatch_delete_surrounding(before_bytes, after_bytes)
326 })
327}
328
329pub fn dispatch_copy() -> Option<String> {
332 crate::render_state::with_text_field_focus(|state| state.dispatch_copy())
333}
334
335pub fn dispatch_cut() -> Option<String> {
338 crate::render_state::with_text_field_focus(|state| state.dispatch_cut())
339}
340
341pub fn dispatch_select_all() -> bool {
344 crate::render_state::with_text_field_focus(|state| state.dispatch_select_all())
345}
346
347pub fn dispatch_ime_preedit(text: &str, cursor: Option<(usize, usize)>) -> bool {
351 crate::render_state::with_text_field_focus(|state| state.dispatch_ime_preedit(text, cursor))
352}
353
354pub fn dispatch_ime_finish_composing() -> bool {
358 crate::render_state::with_text_field_focus(|state| state.dispatch_ime_finish_composing())
359}
360
361pub fn dispatch_ime_set_composing_region(start_bytes: usize, end_bytes: usize) -> bool {
365 crate::render_state::with_text_field_focus(|state| {
366 state.dispatch_ime_set_composing_region(start_bytes, end_bytes)
367 })
368}
369
370pub fn dispatch_ime_set_selection(start_bytes: usize, end_bytes: usize) -> bool {
375 crate::render_state::with_text_field_focus(|state| {
376 state.dispatch_ime_set_selection(start_bytes, end_bytes)
377 })
378}
379
380pub fn focused_editor_state() -> Option<ImeEditorState> {
384 crate::render_state::with_text_field_focus(|state| state.focused_editor_state())
385}
386
387pub fn focused_caret_geometry() -> Option<ImeCaretGeometry> {
390 crate::render_state::with_text_field_focus(|state| state.focused_caret_geometry())
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396 use std::cell::Cell;
397
398 struct MockHandler;
400 impl FocusedTextFieldHandler for MockHandler {
401 fn handle_key(&self, _: &KeyEvent) -> bool {
402 false
403 }
404 fn insert_text(&self, _: &str) {}
405 fn delete_surrounding(&self, _: usize, _: usize) {}
406 fn copy_selection(&self) -> Option<String> {
407 None
408 }
409 fn cut_selection(&self) -> Option<String> {
410 None
411 }
412 fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {}
413 }
414
415 fn mock_handler() -> Rc<dyn FocusedTextFieldHandler> {
416 Rc::new(MockHandler)
417 }
418
419 #[test]
420 fn request_focus_sets_flag() {
421 let _app_context = crate::render_state::app_context_test_scope();
422 let focus = Rc::new(RefCell::new(false));
423 request_focus(focus.clone(), mock_handler());
424 assert!(*focus.borrow());
425 clear_focus();
426 }
427
428 #[test]
429 fn request_focus_clears_previous() {
430 let _app_context = crate::render_state::app_context_test_scope();
431 let focus1 = Rc::new(RefCell::new(false));
432 let focus2 = Rc::new(RefCell::new(false));
433
434 request_focus(focus1.clone(), mock_handler());
435 assert!(*focus1.borrow());
436
437 request_focus(focus2.clone(), mock_handler());
438 assert!(!*focus1.borrow()); assert!(*focus2.borrow()); clear_focus();
441 }
442
443 #[test]
444 fn clear_focus_unfocuses_current() {
445 let _app_context = crate::render_state::app_context_test_scope();
446 let focus = Rc::new(RefCell::new(false));
447 request_focus(focus.clone(), mock_handler());
448 assert!(*focus.borrow());
449
450 clear_focus();
451 assert!(!*focus.borrow());
452 }
453
454 #[derive(Default)]
455 struct DispatchRecordingHandler {
456 key_count: Cell<usize>,
457 insert_count: Cell<usize>,
458 delete_count: Cell<usize>,
459 copy_count: Cell<usize>,
460 cut_count: Cell<usize>,
461 preedit_count: Cell<usize>,
462 last_delete: Cell<Option<(usize, usize)>>,
463 }
464
465 impl DispatchRecordingHandler {
466 fn bump(cell: &Cell<usize>) {
467 cell.set(cell.get() + 1);
468 }
469
470 fn total_calls(&self) -> usize {
471 self.key_count.get()
472 + self.insert_count.get()
473 + self.delete_count.get()
474 + self.copy_count.get()
475 + self.cut_count.get()
476 + self.preedit_count.get()
477 }
478 }
479
480 impl FocusedTextFieldHandler for DispatchRecordingHandler {
481 fn handle_key(&self, _: &KeyEvent) -> bool {
482 Self::bump(&self.key_count);
483 true
484 }
485
486 fn insert_text(&self, _: &str) {
487 Self::bump(&self.insert_count);
488 }
489
490 fn delete_surrounding(&self, before_bytes: usize, after_bytes: usize) {
491 Self::bump(&self.delete_count);
492 self.last_delete.set(Some((before_bytes, after_bytes)));
493 }
494
495 fn copy_selection(&self) -> Option<String> {
496 Self::bump(&self.copy_count);
497 Some("copy".to_string())
498 }
499
500 fn cut_selection(&self) -> Option<String> {
501 Self::bump(&self.cut_count);
502 Some("cut".to_string())
503 }
504
505 fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {
506 Self::bump(&self.preedit_count);
507 }
508 }
509
510 #[test]
511 fn dispatch_delete_surrounding_calls_handler() {
512 let _app_context = crate::render_state::app_context_test_scope();
513 let focus = Rc::new(RefCell::new(false));
514 let handler = Rc::new(DispatchRecordingHandler::default());
515
516 request_focus(Rc::clone(&focus), handler.clone());
517 assert!(dispatch_delete_surrounding(3, 1));
518 assert_eq!(handler.last_delete.get(), Some((3, 1)));
519
520 clear_focus();
521 }
522
523 #[test]
524 fn dispatch_clears_stale_focus_owner_before_invoking_handler() {
525 let _app_context = crate::render_state::app_context_test_scope();
526 let handler = Rc::new(DispatchRecordingHandler::default());
527
528 {
529 let focus = Rc::new(RefCell::new(false));
530 request_focus(Rc::clone(&focus), handler.clone());
531 assert!(has_focused_field());
532 }
533
534 let key_event = KeyEvent::key_down(crate::key_event::KeyCode::A, "a");
535
536 assert!(!dispatch_key_event(&key_event));
537 assert!(!dispatch_paste("stale paste"));
538 assert!(!dispatch_delete_surrounding(2, 1));
539 assert_eq!(dispatch_copy(), None);
540 assert_eq!(dispatch_cut(), None);
541 assert!(!dispatch_ime_preedit("preedit", Some((1, 1))));
542 assert!(!has_focused_field());
543 assert_eq!(
544 handler.total_calls(),
545 0,
546 "stale focused-field handlers must not receive input"
547 );
548 }
549
550 #[derive(Default)]
551 struct KeyboardProbe {
552 calls: RefCell<Vec<&'static str>>,
553 }
554
555 impl crate::text_input_session::PlatformTextInputHandler for KeyboardProbe {
556 fn show_keyboard(&self) {
557 self.calls.borrow_mut().push("show");
558 }
559
560 fn hide_keyboard(&self) {
561 self.calls.borrow_mut().push("hide");
562 }
563 }
564
565 #[test]
566 fn focus_transitions_drive_platform_keyboard() {
567 let _app_context = crate::render_state::app_context_test_scope();
568 let keyboard = Rc::new(KeyboardProbe::default());
569 crate::text_input_session::set_platform_text_input_handler(keyboard.clone());
570
571 let focus = Rc::new(RefCell::new(false));
572 request_focus(focus.clone(), mock_handler());
573 assert_eq!(*keyboard.calls.borrow(), vec!["show"]);
574
575 request_focus(focus, mock_handler());
578 assert_eq!(*keyboard.calls.borrow(), vec!["show", "show"]);
579
580 clear_focus();
581 assert_eq!(*keyboard.calls.borrow(), vec!["show", "show", "hide"]);
582 }
583
584 #[test]
585 fn stale_focus_detection_hides_platform_keyboard() {
586 let _app_context = crate::render_state::app_context_test_scope();
587 let keyboard = Rc::new(KeyboardProbe::default());
588 crate::text_input_session::set_platform_text_input_handler(keyboard.clone());
589
590 {
591 let focus = Rc::new(RefCell::new(false));
592 request_focus(focus, mock_handler());
593 }
596
597 assert!(!has_focused_field());
598 assert_eq!(*keyboard.calls.borrow(), vec!["show", "hide"]);
599
600 assert!(!has_focused_field());
602 assert_eq!(*keyboard.calls.borrow(), vec!["show", "hide"]);
603 }
604
605 #[test]
606 fn text_field_focus_is_scoped_by_app_context() {
607 let _app_context = crate::render_state::app_context_test_scope();
608 let first = crate::render_state::AppContext::new_with_density(1.0);
609 let second = crate::render_state::AppContext::new_with_density(1.0);
610 let first_focus = Rc::new(RefCell::new(false));
611 let second_focus = Rc::new(RefCell::new(false));
612
613 first.enter(|| {
614 request_focus(first_focus.clone(), mock_handler());
615 assert!(has_focused_field());
616 assert!(*first_focus.borrow());
617 });
618
619 second.enter(|| {
620 assert!(!has_focused_field());
621 request_focus(second_focus.clone(), mock_handler());
622 assert!(has_focused_field());
623 assert!(*second_focus.borrow());
624 });
625
626 first.enter(|| {
627 assert!(has_focused_field());
628 assert!(*first_focus.borrow());
629 clear_focus();
630 assert!(!has_focused_field());
631 assert!(!*first_focus.borrow());
632 });
633
634 second.enter(|| {
635 assert!(has_focused_field());
636 assert!(*second_focus.borrow());
637 clear_focus();
638 });
639 }
640}