Skip to main content

ferroui_base/input/
keyboard_device.rs

1use 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
14/// Represents the keyboard: turns raw key and text input into routed
15/// events on the focused element, and is the source of truth about which
16/// element has keyboard focus.
17pub 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    /// Creates a keyboard device.
27    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    /// The keyboard device registered with the locator (as
38    /// `dyn IKeyboardDevice`), if it is a [`KeyboardDevice`].
39    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    /// The input manager registered with the locator.
46    pub fn input_manager(&self) -> Option<Rc<dyn IInputManager>> {
47        FerroLocator::current().get_service::<dyn IInputManager>()
48    }
49
50    /// The focus manager registered with the locator.
51    pub fn focus_manager(&self) -> Option<Rc<dyn IFocusManager>> {
52        FerroLocator::current().get_service::<dyn IFocusManager>()
53    }
54
55    /// Raised with the name of a property of the device that changed
56    /// (`"FocusedElement"`).
57    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    /// The element that has keyboard focus.
68    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            // A parent that is not an input element ends the walk.
82            el = Self::visual_parent_element(&current);
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(&current);
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(&current) == branch.as_ref() {
128                break;
129            }
130            current.set_is_keyboard_focus_within(true);
131            el = Self::visual_parent_element(&current);
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    /// Moves keyboard focus to an element (or clears it); the change can be
153    /// cancelled or redirected by `LosingFocus`/`GettingFocus` handlers.
154    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    /// Moves keyboard focus to an element (or clears it).
164    ///
165    /// `is_focus_change_cancellable` tells whether `LosingFocus` and
166    /// `GettingFocus` handlers may cancel or redirect the change.
167    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        // Clear keyboard focus from the currently focused element.
226        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                        // Work on a copy of the key bindings if there's a
304                        // binding which matches the event: handling a
305                        // binding may change the list (e.g. when a new view
306                        // is loaded which adds its own key bindings).
307                        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 {}