1use super::{EventCx, EventState};
9use crate::event::{Event, FocusSource};
10use crate::{Action, Id, Node};
11#[allow(unused)] use crate::{Tile, event::Command};
12
13#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
15#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
16#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17pub enum NavAdvance {
18 None,
20 Forward(bool),
24 Reverse(bool),
28}
29
30#[crate::impl_default(PendingNavFocus::None)]
31pub(super) enum PendingNavFocus {
32 None,
33 Set {
34 target: Option<Id>,
35 source: FocusSource,
36 },
37 Next {
38 target: Option<Id>,
39 advance: NavAdvance,
40 source: FocusSource,
41 },
42}
43
44impl PendingNavFocus {
45 #[inline]
46 pub(super) fn is_some(&self) -> bool {
47 !matches!(self, PendingNavFocus::None)
48 }
49}
50
51impl EventState {
52 #[inline]
54 pub fn has_nav_focus(&self, w_id: &Id) -> bool {
55 *w_id == self.nav_focus
56 }
57
58 #[inline]
66 pub fn nav_focus(&self) -> Option<&Id> {
67 self.nav_focus.as_ref()
68 }
69
70 pub fn clear_nav_focus(&mut self) {
72 self.pending_nav_focus = PendingNavFocus::Set {
73 target: None,
74 source: FocusSource::Synthetic,
75 };
76 }
77
78 pub(super) fn clear_nav_focus_on(&mut self, target: &Id) {
79 if let Some(id) = self.nav_focus.as_ref()
80 && target.is_ancestor_of(id)
81 {
82 if matches!(&self.pending_nav_focus, PendingNavFocus::Set { target, .. } if target.as_ref() == Some(id))
83 {
84 self.pending_nav_focus = PendingNavFocus::None;
85 }
86
87 if matches!(self.pending_nav_focus, PendingNavFocus::None) {
88 self.pending_nav_focus = PendingNavFocus::Set {
89 target: None,
90 source: FocusSource::Synthetic,
91 };
92 }
93 }
94 }
95
96 pub fn request_nav_focus(&mut self, id: Id, source: FocusSource) {
104 self.pending_nav_focus = PendingNavFocus::Next {
105 target: Some(id),
106 advance: NavAdvance::None,
107 source,
108 };
109 }
110
111 pub(crate) fn set_nav_focus(&mut self, id: Id, source: FocusSource) {
121 self.pending_nav_focus = PendingNavFocus::Set {
122 target: Some(id),
123 source,
124 };
125 }
126
127 pub fn next_nav_focus(
137 &mut self,
138 target: impl Into<Option<Id>>,
139 reverse: bool,
140 source: FocusSource,
141 ) {
142 let target = target.into();
143 let advance = match reverse {
144 false => NavAdvance::Forward(target.is_some()),
145 true => NavAdvance::Reverse(target.is_some()),
146 };
147 self.pending_nav_focus = PendingNavFocus::Next {
148 target,
149 advance,
150 source,
151 };
152 }
153
154 pub fn register_nav_fallback(&mut self, id: Id) {
166 if self.nav_fallback.is_none() {
167 log::debug!(target: "kas_core::event","register_nav_fallback: id={id}");
168 self.nav_fallback = Some(id);
169 }
170 }
171}
172
173impl<'a> EventCx<'a> {
174 #[inline]
176 pub(super) fn nav_next(
177 &mut self,
178 mut widget: Node<'_>,
179 focus: Option<&Id>,
180 advance: NavAdvance,
181 ) -> Option<Id> {
182 log::trace!(target: "kas_core::event", "nav_next: focus={focus:?}, advance={advance:?}");
183
184 widget._nav_next(&mut self.config_cx(), focus, advance)
185 }
186
187 pub(super) fn handle_pending_nav_focus(&mut self, widget: Node<'_>) {
188 match std::mem::take(&mut self.pending_nav_focus) {
189 PendingNavFocus::None => (),
190 PendingNavFocus::Set { target, source } => {
191 self.set_nav_focus_impl(widget, target, source)
192 }
193 PendingNavFocus::Next {
194 target,
195 advance,
196 source,
197 } => self.next_nav_focus_impl(widget, target, advance, source),
198 }
199 }
200
201 pub(super) fn set_nav_focus_impl(
203 &mut self,
204 mut widget: Node,
205 target: Option<Id>,
206 source: FocusSource,
207 ) {
208 if target == self.nav_focus || !self.config.nav_focus {
209 return;
210 }
211
212 self.clear_key_focus();
213
214 if let Some(old) = self.nav_focus.take() {
215 self.action(&old, Action::REDRAW);
216 self.send_event(widget.re(), old, Event::LostNavFocus);
217 }
218
219 self.nav_focus = target.clone();
220 log::debug!(target: "kas_core::event", "nav_focus = {target:?}");
221 if let Some(id) = target {
222 self.action(&id, Action::REDRAW);
223 self.send_event(widget, id, Event::NavFocus(source));
224 }
225 }
226
227 pub(super) fn next_nav_focus_impl(
229 &mut self,
230 mut widget: Node,
231 target: Option<Id>,
232 advance: NavAdvance,
233 source: FocusSource,
234 ) {
235 if !self.config.nav_focus || (target.is_some() && target == self.nav_focus) {
236 return;
237 }
238
239 if let Some(id) = self
240 .popups
241 .last()
242 .filter(|popup| popup.is_sized)
243 .map(|state| state.desc.id.clone())
244 {
245 if id.is_ancestor_of(widget.id_ref()) {
246 } else if let Some(r) = widget.find_node(&id, |node| {
248 self.next_nav_focus_impl(node, target, advance, source)
249 }) {
250 return r;
251 } else {
252 log::warn!(
253 target: "kas_core::event",
254 "next_nav_focus: have open pop-up which is not a child of widget",
255 );
256 return;
257 }
258 }
259
260 let focus = target.or_else(|| self.nav_focus.clone());
261
262 let restart = focus.is_some();
264
265 let mut opt_id = self.nav_next(widget.re(), focus.as_ref(), advance);
266 if restart && opt_id.is_none() {
267 opt_id = self.nav_next(widget.re(), None, advance);
268 }
269
270 self.set_nav_focus_impl(widget, opt_id, source);
271 }
272}