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 set_composition(&self, text: &str, cursor: Option<(usize, usize)>);
53 fn finish_composition(&self) {}
57 fn set_composing_region(&self, start_bytes: usize, end_bytes: usize) {
62 let _ = (start_bytes, end_bytes);
63 }
64 fn editor_state(&self) -> Option<ImeEditorState> {
68 None
69 }
70}
71
72pub(crate) struct TextFieldFocusState {
73 focused_field: RefCell<Option<Weak<RefCell<bool>>>>,
74 focused_handler: RefCell<Option<Rc<dyn FocusedTextFieldHandler>>>,
75}
76
77impl TextFieldFocusState {
78 pub(crate) fn new() -> Self {
79 Self {
80 focused_field: RefCell::new(None),
81 focused_handler: RefCell::new(None),
82 }
83 }
84
85 fn request_focus(
86 &self,
87 is_focused: Rc<RefCell<bool>>,
88 handler: Rc<dyn FocusedTextFieldHandler>,
89 ) {
90 let mut current = self.focused_field.borrow_mut();
91
92 if let Some(ref weak) = *current {
93 if let Some(old_focused) = weak.upgrade() {
94 *old_focused.borrow_mut() = false;
95 }
96 }
97
98 *is_focused.borrow_mut() = true;
99 *current = Some(Rc::downgrade(&is_focused));
100 *self.focused_handler.borrow_mut() = Some(handler);
101 }
102
103 fn clear_focus(&self) {
104 let mut current = self.focused_field.borrow_mut();
105
106 if let Some(ref weak) = *current {
107 if let Some(focused) = weak.upgrade() {
108 *focused.borrow_mut() = false;
109 }
110 }
111
112 *current = None;
113 *self.focused_handler.borrow_mut() = None;
114 }
115
116 fn has_focused_field(&self) -> bool {
117 let mut current = self.focused_field.borrow_mut();
118 if let Some(ref weak) = *current {
119 if weak.upgrade().is_some() {
120 return true;
121 }
122 *current = None;
123 *self.focused_handler.borrow_mut() = None;
124 crate::cursor_animation::stop_cursor_blink();
125 }
126 false
127 }
128
129 fn focused_handler(&self) -> Option<Rc<dyn FocusedTextFieldHandler>> {
130 if !self.has_focused_field() {
131 return None;
132 }
133 self.focused_handler.borrow().as_ref().cloned()
134 }
135
136 fn dispatch_key_event(&self, event: &KeyEvent) -> bool {
137 if let Some(handler) = self.focused_handler() {
138 handler.handle_key(event)
139 } else {
140 false
141 }
142 }
143
144 fn dispatch_paste(&self, text: &str) -> bool {
145 if let Some(handler) = self.focused_handler() {
146 handler.insert_text(text);
147 true
148 } else {
149 false
150 }
151 }
152
153 fn dispatch_delete_surrounding(&self, before_bytes: usize, after_bytes: usize) -> bool {
154 if let Some(handler) = self.focused_handler() {
155 handler.delete_surrounding(before_bytes, after_bytes);
156 true
157 } else {
158 false
159 }
160 }
161
162 fn dispatch_copy(&self) -> Option<String> {
163 self.focused_handler()
164 .and_then(|handler| handler.copy_selection())
165 }
166
167 fn dispatch_cut(&self) -> Option<String> {
168 self.focused_handler()
169 .and_then(|handler| handler.cut_selection())
170 }
171
172 fn dispatch_ime_preedit(&self, text: &str, cursor: Option<(usize, usize)>) -> bool {
173 if let Some(handler) = self.focused_handler() {
174 handler.set_composition(text, cursor);
175 true
176 } else {
177 false
178 }
179 }
180
181 fn dispatch_ime_finish_composing(&self) -> bool {
182 if let Some(handler) = self.focused_handler() {
183 handler.finish_composition();
184 true
185 } else {
186 false
187 }
188 }
189
190 fn dispatch_ime_set_composing_region(&self, start_bytes: usize, end_bytes: usize) -> bool {
191 if let Some(handler) = self.focused_handler() {
192 handler.set_composing_region(start_bytes, end_bytes);
193 true
194 } else {
195 false
196 }
197 }
198
199 fn focused_editor_state(&self) -> Option<ImeEditorState> {
200 self.focused_handler()
201 .and_then(|handler| handler.editor_state())
202 }
203}
204
205pub fn request_focus(is_focused: Rc<RefCell<bool>>, handler: Rc<dyn FocusedTextFieldHandler>) {
211 crate::render_state::with_text_field_focus(|state| state.request_focus(is_focused, handler));
212
213 crate::cursor_animation::start_cursor_blink();
215
216 crate::text_input_session::notify_text_input_focus_gained();
219
220 crate::request_render_invalidation();
223}
224
225pub fn clear_focus() {
227 crate::render_state::with_text_field_focus(|state| state.clear_focus());
228
229 crate::cursor_animation::stop_cursor_blink();
231
232 crate::text_input_session::notify_text_input_focus_lost();
234
235 crate::request_render_invalidation();
236}
237
238pub fn has_focused_field() -> bool {
241 let has_focus = crate::render_state::with_text_field_focus(|state| state.has_focused_field());
242 if !has_focus {
243 crate::text_input_session::notify_text_input_focus_lost();
247 }
248 has_focus
249}
250
251pub fn dispatch_key_event(event: &KeyEvent) -> bool {
258 crate::render_state::with_text_field_focus(|state| state.dispatch_key_event(event))
259}
260
261pub fn dispatch_paste(text: &str) -> bool {
264 crate::render_state::with_text_field_focus(|state| state.dispatch_paste(text))
265}
266
267pub fn dispatch_delete_surrounding(before_bytes: usize, after_bytes: usize) -> bool {
270 crate::render_state::with_text_field_focus(|state| {
271 state.dispatch_delete_surrounding(before_bytes, after_bytes)
272 })
273}
274
275pub fn dispatch_copy() -> Option<String> {
278 crate::render_state::with_text_field_focus(|state| state.dispatch_copy())
279}
280
281pub fn dispatch_cut() -> Option<String> {
284 crate::render_state::with_text_field_focus(|state| state.dispatch_cut())
285}
286
287pub fn dispatch_ime_preedit(text: &str, cursor: Option<(usize, usize)>) -> bool {
291 crate::render_state::with_text_field_focus(|state| state.dispatch_ime_preedit(text, cursor))
292}
293
294pub fn dispatch_ime_finish_composing() -> bool {
298 crate::render_state::with_text_field_focus(|state| state.dispatch_ime_finish_composing())
299}
300
301pub fn dispatch_ime_set_composing_region(start_bytes: usize, end_bytes: usize) -> bool {
305 crate::render_state::with_text_field_focus(|state| {
306 state.dispatch_ime_set_composing_region(start_bytes, end_bytes)
307 })
308}
309
310pub fn focused_editor_state() -> Option<ImeEditorState> {
314 crate::render_state::with_text_field_focus(|state| state.focused_editor_state())
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320 use std::cell::Cell;
321
322 struct MockHandler;
324 impl FocusedTextFieldHandler for MockHandler {
325 fn handle_key(&self, _: &KeyEvent) -> bool {
326 false
327 }
328 fn insert_text(&self, _: &str) {}
329 fn delete_surrounding(&self, _: usize, _: usize) {}
330 fn copy_selection(&self) -> Option<String> {
331 None
332 }
333 fn cut_selection(&self) -> Option<String> {
334 None
335 }
336 fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {}
337 }
338
339 fn mock_handler() -> Rc<dyn FocusedTextFieldHandler> {
340 Rc::new(MockHandler)
341 }
342
343 #[test]
344 fn request_focus_sets_flag() {
345 let _app_context = crate::render_state::app_context_test_scope();
346 let focus = Rc::new(RefCell::new(false));
347 request_focus(focus.clone(), mock_handler());
348 assert!(*focus.borrow());
349 clear_focus();
350 }
351
352 #[test]
353 fn request_focus_clears_previous() {
354 let _app_context = crate::render_state::app_context_test_scope();
355 let focus1 = Rc::new(RefCell::new(false));
356 let focus2 = Rc::new(RefCell::new(false));
357
358 request_focus(focus1.clone(), mock_handler());
359 assert!(*focus1.borrow());
360
361 request_focus(focus2.clone(), mock_handler());
362 assert!(!*focus1.borrow()); assert!(*focus2.borrow()); clear_focus();
365 }
366
367 #[test]
368 fn clear_focus_unfocuses_current() {
369 let _app_context = crate::render_state::app_context_test_scope();
370 let focus = Rc::new(RefCell::new(false));
371 request_focus(focus.clone(), mock_handler());
372 assert!(*focus.borrow());
373
374 clear_focus();
375 assert!(!*focus.borrow());
376 }
377
378 #[derive(Default)]
379 struct DispatchRecordingHandler {
380 key_count: Cell<usize>,
381 insert_count: Cell<usize>,
382 delete_count: Cell<usize>,
383 copy_count: Cell<usize>,
384 cut_count: Cell<usize>,
385 preedit_count: Cell<usize>,
386 last_delete: Cell<Option<(usize, usize)>>,
387 }
388
389 impl DispatchRecordingHandler {
390 fn bump(cell: &Cell<usize>) {
391 cell.set(cell.get() + 1);
392 }
393
394 fn total_calls(&self) -> usize {
395 self.key_count.get()
396 + self.insert_count.get()
397 + self.delete_count.get()
398 + self.copy_count.get()
399 + self.cut_count.get()
400 + self.preedit_count.get()
401 }
402 }
403
404 impl FocusedTextFieldHandler for DispatchRecordingHandler {
405 fn handle_key(&self, _: &KeyEvent) -> bool {
406 Self::bump(&self.key_count);
407 true
408 }
409
410 fn insert_text(&self, _: &str) {
411 Self::bump(&self.insert_count);
412 }
413
414 fn delete_surrounding(&self, before_bytes: usize, after_bytes: usize) {
415 Self::bump(&self.delete_count);
416 self.last_delete.set(Some((before_bytes, after_bytes)));
417 }
418
419 fn copy_selection(&self) -> Option<String> {
420 Self::bump(&self.copy_count);
421 Some("copy".to_string())
422 }
423
424 fn cut_selection(&self) -> Option<String> {
425 Self::bump(&self.cut_count);
426 Some("cut".to_string())
427 }
428
429 fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {
430 Self::bump(&self.preedit_count);
431 }
432 }
433
434 #[test]
435 fn dispatch_delete_surrounding_calls_handler() {
436 let _app_context = crate::render_state::app_context_test_scope();
437 let focus = Rc::new(RefCell::new(false));
438 let handler = Rc::new(DispatchRecordingHandler::default());
439
440 request_focus(Rc::clone(&focus), handler.clone());
441 assert!(dispatch_delete_surrounding(3, 1));
442 assert_eq!(handler.last_delete.get(), Some((3, 1)));
443
444 clear_focus();
445 }
446
447 #[test]
448 fn dispatch_clears_stale_focus_owner_before_invoking_handler() {
449 let _app_context = crate::render_state::app_context_test_scope();
450 let handler = Rc::new(DispatchRecordingHandler::default());
451
452 {
453 let focus = Rc::new(RefCell::new(false));
454 request_focus(Rc::clone(&focus), handler.clone());
455 assert!(has_focused_field());
456 }
457
458 let key_event = KeyEvent::key_down(crate::key_event::KeyCode::A, "a");
459
460 assert!(!dispatch_key_event(&key_event));
461 assert!(!dispatch_paste("stale paste"));
462 assert!(!dispatch_delete_surrounding(2, 1));
463 assert_eq!(dispatch_copy(), None);
464 assert_eq!(dispatch_cut(), None);
465 assert!(!dispatch_ime_preedit("preedit", Some((1, 1))));
466 assert!(!has_focused_field());
467 assert_eq!(
468 handler.total_calls(),
469 0,
470 "stale focused-field handlers must not receive input"
471 );
472 }
473
474 #[derive(Default)]
475 struct KeyboardProbe {
476 calls: RefCell<Vec<&'static str>>,
477 }
478
479 impl crate::text_input_session::PlatformTextInputHandler for KeyboardProbe {
480 fn show_keyboard(&self) {
481 self.calls.borrow_mut().push("show");
482 }
483
484 fn hide_keyboard(&self) {
485 self.calls.borrow_mut().push("hide");
486 }
487 }
488
489 #[test]
490 fn focus_transitions_drive_platform_keyboard() {
491 let _app_context = crate::render_state::app_context_test_scope();
492 let keyboard = Rc::new(KeyboardProbe::default());
493 crate::text_input_session::set_platform_text_input_handler(keyboard.clone());
494
495 let focus = Rc::new(RefCell::new(false));
496 request_focus(focus.clone(), mock_handler());
497 assert_eq!(*keyboard.calls.borrow(), vec!["show"]);
498
499 request_focus(focus, mock_handler());
502 assert_eq!(*keyboard.calls.borrow(), vec!["show", "show"]);
503
504 clear_focus();
505 assert_eq!(*keyboard.calls.borrow(), vec!["show", "show", "hide"]);
506 }
507
508 #[test]
509 fn stale_focus_detection_hides_platform_keyboard() {
510 let _app_context = crate::render_state::app_context_test_scope();
511 let keyboard = Rc::new(KeyboardProbe::default());
512 crate::text_input_session::set_platform_text_input_handler(keyboard.clone());
513
514 {
515 let focus = Rc::new(RefCell::new(false));
516 request_focus(focus, mock_handler());
517 }
520
521 assert!(!has_focused_field());
522 assert_eq!(*keyboard.calls.borrow(), vec!["show", "hide"]);
523
524 assert!(!has_focused_field());
526 assert_eq!(*keyboard.calls.borrow(), vec!["show", "hide"]);
527 }
528
529 #[test]
530 fn text_field_focus_is_scoped_by_app_context() {
531 let _app_context = crate::render_state::app_context_test_scope();
532 let first = crate::render_state::AppContext::new_with_density(1.0);
533 let second = crate::render_state::AppContext::new_with_density(1.0);
534 let first_focus = Rc::new(RefCell::new(false));
535 let second_focus = Rc::new(RefCell::new(false));
536
537 first.enter(|| {
538 request_focus(first_focus.clone(), mock_handler());
539 assert!(has_focused_field());
540 assert!(*first_focus.borrow());
541 });
542
543 second.enter(|| {
544 assert!(!has_focused_field());
545 request_focus(second_focus.clone(), mock_handler());
546 assert!(has_focused_field());
547 assert!(*second_focus.borrow());
548 });
549
550 first.enter(|| {
551 assert!(has_focused_field());
552 assert!(*first_focus.borrow());
553 clear_focus();
554 assert!(!has_focused_field());
555 assert!(!*first_focus.borrow());
556 });
557
558 second.enter(|| {
559 assert!(has_focused_field());
560 assert!(*second_focus.borrow());
561 clear_focus();
562 });
563 }
564}