cranpose_ui/
text_field_focus.rs1use std::cell::RefCell;
14use std::rc::{Rc, Weak};
15
16use crate::key_event::KeyEvent;
17
18pub trait FocusedTextFieldHandler {
21 fn handle_key(&self, event: &KeyEvent) -> bool;
23 fn insert_text(&self, text: &str);
25 fn delete_surrounding(&self, before_bytes: usize, after_bytes: usize);
27 fn copy_selection(&self) -> Option<String>;
29 fn cut_selection(&self) -> Option<String>;
31 fn set_composition(&self, text: &str, cursor: Option<(usize, usize)>);
35}
36
37thread_local! {
40 static FOCUSED_FIELD: RefCell<Option<Weak<RefCell<bool>>>> = const { RefCell::new(None) };
41 static FOCUSED_HANDLER: RefCell<Option<Rc<dyn FocusedTextFieldHandler>>> = const { RefCell::new(None) };
43}
44
45pub fn request_focus(is_focused: Rc<RefCell<bool>>, handler: Rc<dyn FocusedTextFieldHandler>) {
51 FOCUSED_FIELD.with(|current| {
52 let mut current = current.borrow_mut();
53
54 if let Some(ref weak) = *current {
56 if let Some(old_focused) = weak.upgrade() {
57 *old_focused.borrow_mut() = false;
58 }
59 }
60
61 *is_focused.borrow_mut() = true;
63 *current = Some(Rc::downgrade(&is_focused));
64 });
65
66 FOCUSED_HANDLER.with(|h| {
68 *h.borrow_mut() = Some(handler);
69 });
70
71 crate::cursor_animation::start_cursor_blink();
73
74 crate::request_render_invalidation();
77}
78
79#[allow(dead_code)]
81pub fn clear_focus() {
82 FOCUSED_FIELD.with(|current| {
83 let mut current = current.borrow_mut();
84
85 if let Some(ref weak) = *current {
86 if let Some(focused) = weak.upgrade() {
87 *focused.borrow_mut() = false;
88 }
89 }
90
91 *current = None;
92 });
93
94 FOCUSED_HANDLER.with(|h| {
96 *h.borrow_mut() = None;
97 });
98
99 crate::cursor_animation::stop_cursor_blink();
101
102 crate::request_render_invalidation();
103}
104
105pub fn has_focused_field() -> bool {
108 FOCUSED_FIELD.with(|current| {
109 let mut borrow = current.borrow_mut();
110 if let Some(ref weak) = *borrow {
111 if weak.upgrade().is_some() {
112 return true;
113 }
114 *borrow = None;
116 FOCUSED_HANDLER.with(|h| {
118 *h.borrow_mut() = None;
119 });
120 crate::cursor_animation::stop_cursor_blink();
122 }
123 false
124 })
125}
126
127pub fn dispatch_key_event(event: &KeyEvent) -> bool {
134 FOCUSED_HANDLER.with(|h| {
135 if let Some(handler) = h.borrow().as_ref() {
136 handler.handle_key(event)
137 } else {
138 false
139 }
140 })
141}
142
143pub fn dispatch_paste(text: &str) -> bool {
146 FOCUSED_HANDLER.with(|h| {
147 if let Some(handler) = h.borrow().as_ref() {
148 handler.insert_text(text);
149 true
150 } else {
151 false
152 }
153 })
154}
155
156pub fn dispatch_delete_surrounding(before_bytes: usize, after_bytes: usize) -> bool {
159 FOCUSED_HANDLER.with(|h| {
160 if let Some(handler) = h.borrow().as_ref() {
161 handler.delete_surrounding(before_bytes, after_bytes);
162 true
163 } else {
164 false
165 }
166 })
167}
168
169pub fn dispatch_copy() -> Option<String> {
172 FOCUSED_HANDLER.with(|h| {
173 if let Some(handler) = h.borrow().as_ref() {
174 handler.copy_selection()
175 } else {
176 None
177 }
178 })
179}
180
181pub fn dispatch_cut() -> Option<String> {
184 FOCUSED_HANDLER.with(|h| {
185 if let Some(handler) = h.borrow().as_ref() {
186 handler.cut_selection()
187 } else {
188 None
189 }
190 })
191}
192
193pub fn dispatch_ime_preedit(text: &str, cursor: Option<(usize, usize)>) -> bool {
197 FOCUSED_HANDLER.with(|h| {
198 if let Some(handler) = h.borrow().as_ref() {
199 handler.set_composition(text, cursor);
200 true
201 } else {
202 false
203 }
204 })
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use std::cell::Cell;
211
212 struct MockHandler;
214 impl FocusedTextFieldHandler for MockHandler {
215 fn handle_key(&self, _: &KeyEvent) -> bool {
216 false
217 }
218 fn insert_text(&self, _: &str) {}
219 fn delete_surrounding(&self, _: usize, _: usize) {}
220 fn copy_selection(&self) -> Option<String> {
221 None
222 }
223 fn cut_selection(&self) -> Option<String> {
224 None
225 }
226 fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {}
227 }
228
229 fn mock_handler() -> Rc<dyn FocusedTextFieldHandler> {
230 Rc::new(MockHandler)
231 }
232
233 #[test]
234 fn request_focus_sets_flag() {
235 let focus = Rc::new(RefCell::new(false));
236 request_focus(focus.clone(), mock_handler());
237 assert!(*focus.borrow());
238 clear_focus();
239 }
240
241 #[test]
242 fn request_focus_clears_previous() {
243 let focus1 = Rc::new(RefCell::new(false));
244 let focus2 = Rc::new(RefCell::new(false));
245
246 request_focus(focus1.clone(), mock_handler());
247 assert!(*focus1.borrow());
248
249 request_focus(focus2.clone(), mock_handler());
250 assert!(!*focus1.borrow()); assert!(*focus2.borrow()); clear_focus();
253 }
254
255 #[test]
256 fn clear_focus_unfocuses_current() {
257 let focus = Rc::new(RefCell::new(false));
258 request_focus(focus.clone(), mock_handler());
259 assert!(*focus.borrow());
260
261 clear_focus();
262 assert!(!*focus.borrow());
263 }
264
265 struct DeleteHandler {
266 last_delete: Cell<Option<(usize, usize)>>,
267 }
268
269 impl FocusedTextFieldHandler for DeleteHandler {
270 fn handle_key(&self, _: &KeyEvent) -> bool {
271 false
272 }
273
274 fn insert_text(&self, _: &str) {}
275
276 fn delete_surrounding(&self, before_bytes: usize, after_bytes: usize) {
277 self.last_delete.set(Some((before_bytes, after_bytes)));
278 }
279
280 fn copy_selection(&self) -> Option<String> {
281 None
282 }
283
284 fn cut_selection(&self) -> Option<String> {
285 None
286 }
287
288 fn set_composition(&self, _: &str, _: Option<(usize, usize)>) {}
289 }
290
291 #[test]
292 fn dispatch_delete_surrounding_calls_handler() {
293 let focus = Rc::new(RefCell::new(false));
294 let handler = Rc::new(DeleteHandler {
295 last_delete: Cell::new(None),
296 });
297
298 request_focus(focus, handler.clone());
299 assert!(dispatch_delete_surrounding(3, 1));
300 assert_eq!(handler.last_delete.get(), Some((3, 1)));
301
302 clear_focus();
303 }
304}