ferroui_base/input/
keyboard_navigation_handler.rs1use super::navigation::{TabNavigation, XYFocus};
2use super::{
3 FocusManager, IKeyboardNavigationHandler, InputElement, Key, KeyDeviceType, KeyEventArgs, KeyModifiers,
4 NavigationDirection, NavigationMethod,
5};
6use crate::interactivity::Interactive;
7use crate::Ref;
8use std::cell::RefCell;
9use std::rc::{Rc, Weak};
10
11type Element = Ref<InputElement>;
12
13pub struct KeyboardNavigationHandler {
15 this: Weak<KeyboardNavigationHandler>,
16 owner: RefCell<Option<crate::WeakRef<InputElement>>>,
18}
19
20impl KeyboardNavigationHandler {
21 pub fn new() -> Rc<Self> {
23 Rc::new_cyclic(|this| Self { this: this.clone(), owner: RefCell::new(None) })
24 }
25
26 fn owner(&self) -> Option<Element> {
27 self.owner.borrow().as_ref().and_then(crate::WeakRef::upgrade)
28 }
29
30 pub fn get_next(element: &Element, direction: NavigationDirection) -> Option<Element> {
38 Self::get_next_private(Some(element), None, direction, None)
39 }
40
41 fn find_custom(element: &Element) -> Option<Element> {
42 element.find_ancestor_of_type_where::<InputElement>(true, |e| e.as_custom_keyboard_navigation().is_some())
43 }
44
45 fn get_next_private(
46 element: Option<&Element>,
47 owner: Option<&Element>,
48 direction: NavigationDirection,
49 key_device_type: Option<KeyDeviceType>,
50 ) -> Option<Element> {
51 let element_or_owner = element.or(owner).expect("Either the element or the owner must be set.");
52
53 let custom = element.and_then(Self::find_custom);
56 if let Some(custom) = &custom {
57 if let Some(ce) = Self::handle_pre_custom_navigation(custom, element_or_owner, direction) {
58 return Some(ce);
59 }
60 }
61
62 let result = match direction {
63 NavigationDirection::Next => TabNavigation::get_next_tab(element_or_owner, false),
64 NavigationDirection::Previous => TabNavigation::get_prev_tab(Some(element_or_owner), None, false),
65 NavigationDirection::Up
66 | NavigationDirection::Down
67 | NavigationDirection::Left
68 | NavigationDirection::Right => match element {
69 None => TabNavigation::get_next_tab(element_or_owner, true),
72 Some(element) => XYFocus::try_directional_focus(direction, element, owner, None, key_device_type),
73 },
74 _ => panic!("The navigation direction {direction:?} is not supported."),
75 };
76
77 if custom.is_none() {
81 if let Some(ce) = Self::handle_post_custom_navigation(element_or_owner, result.as_ref(), direction) {
82 return Some(ce);
83 }
84 }
85
86 result
87 }
88
89 fn on_key_down(&self, _sender: &Interactive, e: &KeyEventArgs) {
90 let direction = match e.key {
91 Key::Tab => {
92 if !e.key_modifiers.contains(KeyModifiers::SHIFT) {
93 NavigationDirection::Next
94 } else {
95 NavigationDirection::Previous
96 }
97 }
98 Key::Left => NavigationDirection::Left,
99 Key::Right => NavigationDirection::Right,
100 Key::Up => NavigationDirection::Up,
101 Key::Down => NavigationDirection::Down,
102 _ => return,
103 };
104
105 let source = e.source().and_then(|source| source.downcast::<InputElement>().ok());
106 let current = source
107 .and_then(|source| FocusManager::get_focus_manager(&source))
108 .and_then(|focus_manager| focus_manager.get_focused_element());
109
110 e.set_handled(self.move_(current.as_ref(), direction, e.key_modifiers, Some(e.key_device_type)));
111 }
112
113 fn handle_pre_custom_navigation(
114 custom_handler: &Element,
115 element: &Element,
116 direction: NavigationDirection,
117 ) -> Option<Element> {
118 let (handled, next) = custom_handler.as_custom_keyboard_navigation()?.get_next(element, direction);
119
120 if handled {
121 if next.is_some() {
122 return next;
123 }
124
125 return match direction {
126 NavigationDirection::Next => TabNavigation::get_next_tab_outside(custom_handler),
127 NavigationDirection::Previous => TabNavigation::get_prev_tab_outside(custom_handler),
128 _ => None,
129 };
130 }
131
132 None
133 }
134
135 fn handle_post_custom_navigation(
136 element: &Element,
137 new_element: Option<&Element>,
138 direction: NavigationDirection,
139 ) -> Option<Element> {
140 let custom_handler = Self::find_custom(new_element?)?;
141 let (handled, next) = custom_handler.as_custom_keyboard_navigation()?.get_next(element, direction);
142
143 if handled {
144 next
145 } else {
146 None
147 }
148 }
149}
150
151impl IKeyboardNavigationHandler for KeyboardNavigationHandler {
152 fn set_owner(&self, owner: &Element) {
153 if self.owner.borrow().is_some() {
154 panic!("KeyboardNavigationHandler owner has already been set.");
155 }
156
157 *self.owner.borrow_mut() = Some(owner.downgrade());
158
159 let this = self.this.clone();
160 owner.add_handler(InputElement::key_down_event(), move |sender, e| {
161 if let Some(this) = this.upgrade() {
162 this.on_key_down(sender, e);
163 }
164 });
165 }
166
167 fn move_(
168 &self,
169 element: Option<&Element>,
170 direction: NavigationDirection,
171 key_modifiers: KeyModifiers,
172 device_type: Option<KeyDeviceType>,
173 ) -> bool {
174 let owner = self.owner();
175 let next = Self::get_next_private(element, owner.as_ref(), direction, device_type);
176
177 match next {
178 Some(next) => {
179 let method = if direction == NavigationDirection::Next || direction == NavigationDirection::Previous {
180 NavigationMethod::Tab
181 } else {
182 NavigationMethod::Directional
183 };
184
185 next.focus_with(method, key_modifiers)
186 }
187 None => false,
188 }
189 }
190}