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
35pub trait FocusedTextFieldHandler {
38 fn handle_key(&self, event: &KeyEvent) -> bool;
40 fn insert_text(&self, text: &str);
42 fn delete_surrounding(&self, before_bytes: usize, after_bytes: usize);
44 fn copy_selection(&self) -> Option<String>;
46 fn cut_selection(&self) -> Option<String>;
48 fn select_all(&self) {}
50 fn set_composition(&self, text: &str, cursor: Option<(usize, usize)>);
55 fn finish_composition(&self) {}
59 fn set_composing_region(&self, start_bytes: usize, end_bytes: usize) {
64 let _ = (start_bytes, end_bytes);
65 }
66 fn set_selection(&self, start_bytes: usize, end_bytes: usize) {
72 let _ = (start_bytes, end_bytes);
73 }
74 fn editor_state(&self) -> Option<ImeEditorState> {
78 None
79 }
80}
81
82pub(crate) struct TextFieldFocusState {
83 focused_field: RefCell<Option<Weak<RefCell<bool>>>>,
84 focused_handler: RefCell<Option<Rc<dyn FocusedTextFieldHandler>>>,
85}
86
87impl TextFieldFocusState {
88 pub(crate) fn new() -> Self {
89 Self {
90 focused_field: RefCell::new(None),
91 focused_handler: RefCell::new(None),
92 }
93 }
94
95 fn request_focus(
96 &self,
97 is_focused: Rc<RefCell<bool>>,
98 handler: Rc<dyn FocusedTextFieldHandler>,
99 ) {
100 let mut current = self.focused_field.borrow_mut();
101
102 if let Some(ref weak) = *current {
103 if let Some(old_focused) = weak.upgrade() {
104 *old_focused.borrow_mut() = false;
105 }
106 }
107
108 *is_focused.borrow_mut() = true;
109 *current = Some(Rc::downgrade(&is_focused));
110 *self.focused_handler.borrow_mut() = Some(handler);
111 }
112
113 fn clear_focus(&self) {
114 let mut current = self.focused_field.borrow_mut();
115
116 if let Some(ref weak) = *current {
117 if let Some(focused) = weak.upgrade() {
118 *focused.borrow_mut() = false;
119 }
120 }
121
122 *current = None;
123 *self.focused_handler.borrow_mut() = None;
124 }
125
126 fn has_focused_field(&self) -> bool {
127 let mut current = self.focused_field.borrow_mut();
128 if let Some(ref weak) = *current {
129 if weak.upgrade().is_some() {
130 return true;
131 }
132 *current = None;
133 *self.focused_handler.borrow_mut() = None;
134 crate::cursor_animation::stop_cursor_blink();
135 }
136 false
137 }
138
139 fn focused_handler(&self) -> Option<Rc<dyn FocusedTextFieldHandler>> {
140 if !self.has_focused_field() {
141 return None;
142 }
143 self.focused_handler.borrow().as_ref().cloned()
144 }
145
146 fn dispatch_key_event(&self, event: &KeyEvent) -> bool {
147 if let Some(handler) = self.focused_handler() {
148 handler.handle_key(event)
149 } else {
150 false
151 }
152 }
153
154 fn dispatch_paste(&self, text: &str) -> bool {
155 if let Some(handler) = self.focused_handler() {
156 handler.insert_text(text);
157 true
158 } else {
159 false
160 }
161 }
162
163 fn dispatch_delete_surrounding(&self, before_bytes: usize, after_bytes: usize) -> bool {
164 if let Some(handler) = self.focused_handler() {
165 handler.delete_surrounding(before_bytes, after_bytes);
166 true
167 } else {
168 false
169 }
170 }
171
172 fn dispatch_copy(&self) -> Option<String> {
173 self.focused_handler()
174 .and_then(|handler| handler.copy_selection())
175 }
176
177 fn dispatch_cut(&self) -> Option<String> {
178 self.focused_handler()
179 .and_then(|handler| handler.cut_selection())
180 }
181
182 fn dispatch_select_all(&self) -> bool {
183 if let Some(handler) = self.focused_handler() {
184 handler.select_all();
185 true
186 } else {
187 false
188 }
189 }
190
191 fn dispatch_ime_preedit(&self, text: &str, cursor: Option<(usize, usize)>) -> bool {
192 if let Some(handler) = self.focused_handler() {
193 handler.set_composition(text, cursor);
194 true
195 } else {
196 false
197 }
198 }
199
200 fn dispatch_ime_finish_composing(&self) -> bool {
201 if let Some(handler) = self.focused_handler() {
202 handler.finish_composition();
203 true
204 } else {
205 false
206 }
207 }
208
209 fn dispatch_ime_set_composing_region(&self, start_bytes: usize, end_bytes: usize) -> bool {
210 if let Some(handler) = self.focused_handler() {
211 handler.set_composing_region(start_bytes, end_bytes);
212 true
213 } else {
214 false
215 }
216 }
217
218 fn dispatch_ime_set_selection(&self, start_bytes: usize, end_bytes: usize) -> bool {
219 if let Some(handler) = self.focused_handler() {
220 handler.set_selection(start_bytes, end_bytes);
221 true
222 } else {
223 false
224 }
225 }
226
227 fn focused_editor_state(&self) -> Option<ImeEditorState> {
228 self.focused_handler()
229 .and_then(|handler| handler.editor_state())
230 }
231}
232
233pub fn request_focus(is_focused: Rc<RefCell<bool>>, handler: Rc<dyn FocusedTextFieldHandler>) {
239 crate::render_state::with_text_field_focus(|state| state.request_focus(is_focused, handler));
240
241 crate::cursor_animation::start_cursor_blink();
243
244 crate::text_input_session::notify_text_input_focus_gained();
247
248 crate::request_render_invalidation();
251}
252
253pub fn clear_focus() {
255 crate::render_state::with_text_field_focus(|state| state.clear_focus());
256
257 crate::cursor_animation::stop_cursor_blink();
259
260 crate::text_input_session::notify_text_input_focus_lost();
262
263 crate::request_render_invalidation();
264}
265
266pub fn has_focused_field() -> bool {
269 let has_focus = crate::render_state::with_text_field_focus(|state| state.has_focused_field());
270 if !has_focus {
271 crate::text_input_session::notify_text_input_focus_lost();
275 }
276 has_focus
277}
278
279pub fn dispatch_key_event(event: &KeyEvent) -> bool {
286 crate::render_state::with_text_field_focus(|state| state.dispatch_key_event(event))
287}
288
289pub fn dispatch_paste(text: &str) -> bool {
292 crate::render_state::with_text_field_focus(|state| state.dispatch_paste(text))
293}
294
295pub fn dispatch_delete_surrounding(before_bytes: usize, after_bytes: usize) -> bool {
298 crate::render_state::with_text_field_focus(|state| {
299 state.dispatch_delete_surrounding(before_bytes, after_bytes)
300 })
301}
302
303pub fn dispatch_copy() -> Option<String> {
306 crate::render_state::with_text_field_focus(|state| state.dispatch_copy())
307}
308
309pub fn dispatch_cut() -> Option<String> {
312 crate::render_state::with_text_field_focus(|state| state.dispatch_cut())
313}
314
315pub fn dispatch_select_all() -> bool {
318 crate::render_state::with_text_field_focus(|state| state.dispatch_select_all())
319}
320
321pub fn dispatch_ime_preedit(text: &str, cursor: Option<(usize, usize)>) -> bool {
325 crate::render_state::with_text_field_focus(|state| state.dispatch_ime_preedit(text, cursor))
326}
327
328pub fn dispatch_ime_finish_composing() -> bool {
332 crate::render_state::with_text_field_focus(|state| state.dispatch_ime_finish_composing())
333}
334
335pub fn dispatch_ime_set_composing_region(start_bytes: usize, end_bytes: usize) -> bool {
339 crate::render_state::with_text_field_focus(|state| {
340 state.dispatch_ime_set_composing_region(start_bytes, end_bytes)
341 })
342}
343
344pub fn dispatch_ime_set_selection(start_bytes: usize, end_bytes: usize) -> bool {
349 crate::render_state::with_text_field_focus(|state| {
350 state.dispatch_ime_set_selection(start_bytes, end_bytes)
351 })
352}
353
354pub fn focused_editor_state() -> Option<ImeEditorState> {
358 crate::render_state::with_text_field_focus(|state| state.focused_editor_state())
359}
360
361#[cfg(test)]
362mod tests {
363 use super::*;
364 use std::cell::Cell;
365
366 struct MockHandler;
368 impl FocusedTextFieldHandler for MockHandler {
369 fn handle_key(&self, _: &KeyEvent) -> bool {
370 false
371 }
372 fn insert_text(&self, _: &str) {}
373 fn delete_surrounding(&self, _: usize, _: usize) {}
374 fn copy_selection(&self) -> Option<String> {
375 None
376 }
377 fn cut_selection(&self) -> Option<String> {
378 None
379 }
380 fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {}
381 }
382
383 fn mock_handler() -> Rc<dyn FocusedTextFieldHandler> {
384 Rc::new(MockHandler)
385 }
386
387 #[test]
388 fn request_focus_sets_flag() {
389 let _app_context = crate::render_state::app_context_test_scope();
390 let focus = Rc::new(RefCell::new(false));
391 request_focus(focus.clone(), mock_handler());
392 assert!(*focus.borrow());
393 clear_focus();
394 }
395
396 #[test]
397 fn request_focus_clears_previous() {
398 let _app_context = crate::render_state::app_context_test_scope();
399 let focus1 = Rc::new(RefCell::new(false));
400 let focus2 = Rc::new(RefCell::new(false));
401
402 request_focus(focus1.clone(), mock_handler());
403 assert!(*focus1.borrow());
404
405 request_focus(focus2.clone(), mock_handler());
406 assert!(!*focus1.borrow()); assert!(*focus2.borrow()); clear_focus();
409 }
410
411 #[test]
412 fn clear_focus_unfocuses_current() {
413 let _app_context = crate::render_state::app_context_test_scope();
414 let focus = Rc::new(RefCell::new(false));
415 request_focus(focus.clone(), mock_handler());
416 assert!(*focus.borrow());
417
418 clear_focus();
419 assert!(!*focus.borrow());
420 }
421
422 #[derive(Default)]
423 struct DispatchRecordingHandler {
424 key_count: Cell<usize>,
425 insert_count: Cell<usize>,
426 delete_count: Cell<usize>,
427 copy_count: Cell<usize>,
428 cut_count: Cell<usize>,
429 preedit_count: Cell<usize>,
430 last_delete: Cell<Option<(usize, usize)>>,
431 }
432
433 impl DispatchRecordingHandler {
434 fn bump(cell: &Cell<usize>) {
435 cell.set(cell.get() + 1);
436 }
437
438 fn total_calls(&self) -> usize {
439 self.key_count.get()
440 + self.insert_count.get()
441 + self.delete_count.get()
442 + self.copy_count.get()
443 + self.cut_count.get()
444 + self.preedit_count.get()
445 }
446 }
447
448 impl FocusedTextFieldHandler for DispatchRecordingHandler {
449 fn handle_key(&self, _: &KeyEvent) -> bool {
450 Self::bump(&self.key_count);
451 true
452 }
453
454 fn insert_text(&self, _: &str) {
455 Self::bump(&self.insert_count);
456 }
457
458 fn delete_surrounding(&self, before_bytes: usize, after_bytes: usize) {
459 Self::bump(&self.delete_count);
460 self.last_delete.set(Some((before_bytes, after_bytes)));
461 }
462
463 fn copy_selection(&self) -> Option<String> {
464 Self::bump(&self.copy_count);
465 Some("copy".to_string())
466 }
467
468 fn cut_selection(&self) -> Option<String> {
469 Self::bump(&self.cut_count);
470 Some("cut".to_string())
471 }
472
473 fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {
474 Self::bump(&self.preedit_count);
475 }
476 }
477
478 #[test]
479 fn dispatch_delete_surrounding_calls_handler() {
480 let _app_context = crate::render_state::app_context_test_scope();
481 let focus = Rc::new(RefCell::new(false));
482 let handler = Rc::new(DispatchRecordingHandler::default());
483
484 request_focus(Rc::clone(&focus), handler.clone());
485 assert!(dispatch_delete_surrounding(3, 1));
486 assert_eq!(handler.last_delete.get(), Some((3, 1)));
487
488 clear_focus();
489 }
490
491 #[test]
492 fn dispatch_clears_stale_focus_owner_before_invoking_handler() {
493 let _app_context = crate::render_state::app_context_test_scope();
494 let handler = Rc::new(DispatchRecordingHandler::default());
495
496 {
497 let focus = Rc::new(RefCell::new(false));
498 request_focus(Rc::clone(&focus), handler.clone());
499 assert!(has_focused_field());
500 }
501
502 let key_event = KeyEvent::key_down(crate::key_event::KeyCode::A, "a");
503
504 assert!(!dispatch_key_event(&key_event));
505 assert!(!dispatch_paste("stale paste"));
506 assert!(!dispatch_delete_surrounding(2, 1));
507 assert_eq!(dispatch_copy(), None);
508 assert_eq!(dispatch_cut(), None);
509 assert!(!dispatch_ime_preedit("preedit", Some((1, 1))));
510 assert!(!has_focused_field());
511 assert_eq!(
512 handler.total_calls(),
513 0,
514 "stale focused-field handlers must not receive input"
515 );
516 }
517
518 #[derive(Default)]
519 struct KeyboardProbe {
520 calls: RefCell<Vec<&'static str>>,
521 }
522
523 impl crate::text_input_session::PlatformTextInputHandler for KeyboardProbe {
524 fn show_keyboard(&self) {
525 self.calls.borrow_mut().push("show");
526 }
527
528 fn hide_keyboard(&self) {
529 self.calls.borrow_mut().push("hide");
530 }
531 }
532
533 #[test]
534 fn focus_transitions_drive_platform_keyboard() {
535 let _app_context = crate::render_state::app_context_test_scope();
536 let keyboard = Rc::new(KeyboardProbe::default());
537 crate::text_input_session::set_platform_text_input_handler(keyboard.clone());
538
539 let focus = Rc::new(RefCell::new(false));
540 request_focus(focus.clone(), mock_handler());
541 assert_eq!(*keyboard.calls.borrow(), vec!["show"]);
542
543 request_focus(focus, mock_handler());
546 assert_eq!(*keyboard.calls.borrow(), vec!["show", "show"]);
547
548 clear_focus();
549 assert_eq!(*keyboard.calls.borrow(), vec!["show", "show", "hide"]);
550 }
551
552 #[test]
553 fn stale_focus_detection_hides_platform_keyboard() {
554 let _app_context = crate::render_state::app_context_test_scope();
555 let keyboard = Rc::new(KeyboardProbe::default());
556 crate::text_input_session::set_platform_text_input_handler(keyboard.clone());
557
558 {
559 let focus = Rc::new(RefCell::new(false));
560 request_focus(focus, mock_handler());
561 }
564
565 assert!(!has_focused_field());
566 assert_eq!(*keyboard.calls.borrow(), vec!["show", "hide"]);
567
568 assert!(!has_focused_field());
570 assert_eq!(*keyboard.calls.borrow(), vec!["show", "hide"]);
571 }
572
573 #[test]
574 fn text_field_focus_is_scoped_by_app_context() {
575 let _app_context = crate::render_state::app_context_test_scope();
576 let first = crate::render_state::AppContext::new_with_density(1.0);
577 let second = crate::render_state::AppContext::new_with_density(1.0);
578 let first_focus = Rc::new(RefCell::new(false));
579 let second_focus = Rc::new(RefCell::new(false));
580
581 first.enter(|| {
582 request_focus(first_focus.clone(), mock_handler());
583 assert!(has_focused_field());
584 assert!(*first_focus.borrow());
585 });
586
587 second.enter(|| {
588 assert!(!has_focused_field());
589 request_focus(second_focus.clone(), mock_handler());
590 assert!(has_focused_field());
591 assert!(*second_focus.borrow());
592 });
593
594 first.enter(|| {
595 assert!(has_focused_field());
596 assert!(*first_focus.borrow());
597 clear_focus();
598 assert!(!has_focused_field());
599 assert!(!*first_focus.borrow());
600 });
601
602 second.enter(|| {
603 assert!(has_focused_field());
604 assert!(*second_focus.borrow());
605 clear_focus();
606 });
607 }
608}