ferroui_base/input/
keyboard_device.rs1use super::raw::{IRawInputEventArgs, RawKeyEventArgs, RawKeyEventType, RawTextInputEventArgs};
2use super::text_input::TextInputMethodManager;
3use super::{
4 FocusChangedEventArgs, FocusChangingEventArgs, IFocusManager, IInputDevice, IInputManager, IInputRoot, IKeyboardDevice, InputElement,
5 KeyEventArgs, KeyModifiers, NavigationMethod, TextInputEventArgs,
6};
7use crate::reactive::{Disposable, IDisposable};
8use crate::utilities::HandlerList;
9use crate::{FerroLocator, LocatorExtensions, Ref};
10use std::any::Any;
11use std::cell::RefCell;
12use std::rc::{Rc, Weak};
13
14pub struct KeyboardDevice {
18 this: Weak<KeyboardDevice>,
19 focused_element: RefCell<Option<Ref<InputElement>>>,
20 focused_root: RefCell<Option<Rc<dyn IInputRoot>>>,
21 text_input_manager: Rc<TextInputMethodManager>,
22 property_changed: HandlerList<dyn Fn(&str)>,
23}
24
25impl KeyboardDevice {
26 pub fn new() -> Rc<KeyboardDevice> {
28 Rc::new_cyclic(|this| KeyboardDevice {
29 this: this.clone(),
30 focused_element: RefCell::new(None),
31 focused_root: RefCell::new(None),
32 text_input_manager: TextInputMethodManager::new(),
33 property_changed: HandlerList::new(),
34 })
35 }
36
37 pub fn instance() -> Option<Rc<KeyboardDevice>> {
40 FerroLocator::current()
41 .get_service::<dyn IKeyboardDevice>()
42 .and_then(|device| device.as_any().downcast_ref::<KeyboardDevice>().and_then(|d| d.this.upgrade()))
43 }
44
45 pub fn input_manager(&self) -> Option<Rc<dyn IInputManager>> {
47 FerroLocator::current().get_service::<dyn IInputManager>()
48 }
49
50 pub fn focus_manager(&self) -> Option<Rc<dyn IFocusManager>> {
52 FerroLocator::current().get_service::<dyn IFocusManager>()
53 }
54
55 pub fn property_changed(&self, handler: impl Fn(&str) + 'static) -> Rc<dyn IDisposable> {
58 let token = self.property_changed.add(Rc::new(handler));
59 let weak = self.this.clone();
60 Disposable::create(move || {
61 if let Some(this) = weak.upgrade() {
62 this.property_changed.remove(token);
63 }
64 })
65 }
66
67 pub fn focused_element(&self) -> Option<Ref<InputElement>> {
69 self.focused_element.borrow().clone()
70 }
71
72 fn visual_parent_element(element: &InputElement) -> Option<Ref<InputElement>> {
73 element.visual_parent().and_then(|parent| parent.downcast::<InputElement>().ok())
74 }
75
76 fn clear_focus_within_ancestors(element: Option<Ref<InputElement>>) {
77 let mut el = element;
78
79 while let Some(current) = el {
80 current.set_is_keyboard_focus_within(false);
81 el = Self::visual_parent_element(¤t);
83 }
84 }
85
86 fn clear_focus_within(element: &InputElement, clear_root: bool) {
87 if let Some(children) = element.visual_children_snapshot() {
88 for visual in children.iter() {
89 if let Some(el) = visual.downcast_ref::<InputElement>() {
90 if el.is_keyboard_focus_within() {
91 Self::clear_focus_within(el, true);
92 break;
93 }
94 }
95 }
96 }
97
98 if clear_root {
99 element.set_is_keyboard_focus_within(false);
100 }
101 }
102
103 fn set_is_focus_within(old_element: Option<&Ref<InputElement>>, new_element: Option<&Ref<InputElement>>) {
104 if new_element.is_none() && old_element.is_some() {
105 Self::clear_focus_within_ancestors(old_element.cloned());
106 return;
107 }
108
109 let mut branch: Option<Ref<InputElement>> = None;
110 let mut el = new_element.cloned();
111
112 while let Some(current) = el {
113 if current.is_keyboard_focus_within() {
114 branch = Some(current);
115 break;
116 }
117 el = Self::visual_parent_element(¤t);
118 }
119
120 if let (Some(_), Some(branch)) = (old_element, &branch) {
121 Self::clear_focus_within(branch, false);
122 }
123
124 el = new_element.cloned();
125
126 while let Some(current) = el {
127 if Some(¤t) == branch.as_ref() {
128 break;
129 }
130 current.set_is_keyboard_focus_within(true);
131 el = Self::visual_parent_element(¤t);
132 }
133 }
134
135 fn clear_children_focus_within(element: &InputElement, clear_root: bool) {
136 if let Some(children) = element.visual_children_snapshot() {
137 for visual in children.iter() {
138 if let Some(el) = visual.downcast_ref::<InputElement>() {
139 if el.is_keyboard_focus_within() {
140 Self::clear_children_focus_within(el, true);
141 break;
142 }
143 }
144 }
145 }
146
147 if clear_root {
148 element.set_is_keyboard_focus_within(false);
149 }
150 }
151
152 pub fn set_focused_element(
155 &self,
156 element: Option<&Ref<InputElement>>,
157 method: NavigationMethod,
158 key_modifiers: KeyModifiers,
159 ) {
160 self.set_focused_element_with(element, method, key_modifiers, true);
161 }
162
163 pub fn set_focused_element_with(
168 &self,
169 element: Option<&Ref<InputElement>>,
170 method: NavigationMethod,
171 key_modifiers: KeyModifiers,
172 is_focus_change_cancellable: bool,
173 ) {
174 let focused_element = self.focused_element();
175
176 if element == focused_element.as_ref() {
177 return;
178 }
179
180 let mut element = element.cloned();
181 let interactive = focused_element.clone();
182 let mut change_focus = true;
183
184 let mut losing_focus = FocusChangingEventArgs::new(InputElement::losing_focus_event());
185 losing_focus.old_focused_element = focused_element.clone();
186 losing_focus.set_new_focused_element(element.clone());
187 losing_focus.navigation_method = method;
188 losing_focus.key_modifiers = key_modifiers;
189 losing_focus.can_cancel_or_redirect_focus = is_focus_change_cancellable;
190
191 if let Some(interactive) = &interactive {
192 interactive.raise_event(&losing_focus);
193 }
194
195 if losing_focus.canceled() {
196 change_focus = false;
197 }
198
199 if change_focus {
200 if let Some(new_focus) = losing_focus.new_focused_element() {
201 let mut getting_focus = FocusChangingEventArgs::new(InputElement::getting_focus_event());
202 getting_focus.old_focused_element = focused_element.clone();
203 getting_focus.set_new_focused_element(Some(new_focus.clone()));
204 getting_focus.navigation_method = method;
205 getting_focus.key_modifiers = key_modifiers;
206 getting_focus.can_cancel_or_redirect_focus = is_focus_change_cancellable;
207
208 new_focus.raise_event(&getting_focus);
209
210 if getting_focus.canceled() {
211 change_focus = false;
212 }
213
214 element = getting_focus.new_focused_element();
215 }
216 }
217
218 if !change_focus {
219 return;
220 }
221
222 let old_element = self.focused_element();
223 let focused_root = self.focused_root.borrow().clone();
224
225 if let (Some(old), Some(focused_root)) = (&old_element, &focused_root) {
227 let new_root = element.as_ref().and_then(|e| e.get_input_root());
228 let same_root = new_root.as_ref().is_some_and(|r| std::ptr::addr_eq(Rc::as_ptr(r), Rc::as_ptr(focused_root)));
229
230 if !old.is_attached_to_visual_tree() || !same_root {
231 Self::clear_children_focus_within(&focused_root.root_element(), true);
232 }
233 }
234
235 Self::set_is_focus_within(old_element.as_ref(), element.as_ref());
236 drop(self.focused_element.replace(element.clone()));
237 drop(self.focused_root.replace(element.as_ref().and_then(|e| e.get_input_root())));
238
239 if let Some(interactive) = &interactive {
240 let mut lost_focus = FocusChangedEventArgs::new(InputElement::lost_focus_event());
241 lost_focus.old_focused_element = old_element.clone();
242 lost_focus.new_focused_element = element.clone();
243 lost_focus.navigation_method = method;
244 lost_focus.key_modifiers = key_modifiers;
245 interactive.raise_event(&lost_focus);
246 }
247
248 if let Some(new_element) = &element {
249 let mut got_focus = FocusChangedEventArgs::new(InputElement::got_focus_event());
250 got_focus.old_focused_element = old_element.clone();
251 got_focus.new_focused_element = element.clone();
252 got_focus.navigation_method = method;
253 got_focus.key_modifiers = key_modifiers;
254 new_element.raise_event(&got_focus);
255 }
256
257 self.text_input_manager.set_focused_element(element);
258 self.raise_property_changed("FocusedElement");
259 }
260
261 fn raise_property_changed(&self, property_name: &str) {
262 if self.property_changed.is_empty() {
263 return;
264 }
265 for (_, handler) in self.property_changed.snapshot().iter() {
266 handler(property_name);
267 }
268 }
269}
270
271impl IInputDevice for KeyboardDevice {
272 fn process_raw_event(&self, e: &dyn IRawInputEventArgs) {
273 if e.handled() {
274 return;
275 }
276
277 let element = self.focused_element().unwrap_or_else(|| e.root().focus_root());
278
279 if let Some(key_input) = e.downcast_ref::<RawKeyEventArgs>() {
280 let routed_event = match key_input.type_() {
281 RawKeyEventType::KeyDown => InputElement::key_down_event(),
282 RawKeyEventType::KeyUp => InputElement::key_up_event(),
283 };
284
285 let mut ev = KeyEventArgs::new();
286 ev.set_routed_event(Some(routed_event));
287 ev.key = key_input.key();
288 ev.key_modifiers = key_input.modifiers().to_key_modifiers();
289 ev.physical_key = key_input.physical_key();
290 ev.key_symbol = key_input.key_symbol();
291 ev.key_device_type = key_input.key_device_type();
292 ev.set_source(&element);
293
294 if key_input.type_() == RawKeyEventType::KeyDown {
295 let mut current_handler: Option<Ref<crate::Visual>> = Some(element.clone().upcast());
296
297 while let Some(current) = current_handler {
298 if ev.handled() {
299 break;
300 }
301
302 if let Some(input_element) = current.downcast_ref::<InputElement>() {
303 let bindings_copy = {
308 let bindings = input_element.key_bindings().snapshot();
309 if bindings.iter().any(|b| b.gesture().is_some_and(|g| g.matches(Some(&ev)))) {
310 Some(bindings)
311 } else {
312 None
313 }
314 };
315
316 if let Some(bindings_copy) = bindings_copy {
317 for binding in bindings_copy.iter() {
318 if ev.handled() {
319 break;
320 }
321 binding.try_handle(&ev);
322 }
323 }
324 }
325
326 current_handler = current.visual_parent();
327 }
328 }
329
330 element.raise_event(&ev);
331 e.set_handled(ev.handled());
332 }
333
334 if let Some(text) = e.downcast_ref::<RawTextInputEventArgs>() {
335 let mut ev = TextInputEventArgs::new();
336 ev.text = Some(text.text().to_string());
337 ev.set_source(&element);
338 ev.set_routed_event(Some(InputElement::text_input_event()));
339
340 element.raise_event(&ev);
341 e.set_handled(ev.handled());
342 }
343 }
344
345 fn as_any(&self) -> &dyn Any {
346 self
347 }
348}
349
350impl IKeyboardDevice for KeyboardDevice {}