1use super::{ConfigCx, EventCx, EventState};
9use crate::event::{Event, FocusSource};
10use crate::{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
44#[derive(Default)]
45pub(super) struct NavFocus {
46 focus: Option<Id>,
47 pub(super) fallback: Option<Id>,
48 pending_focus: PendingNavFocus,
49}
50
51impl NavFocus {
52 #[inline]
53 pub(super) fn has_pending_changes(&self) -> bool {
54 !matches!(self.pending_focus, PendingNavFocus::None)
55 }
56}
57
58impl EventState {
59 #[inline]
61 pub fn has_nav_focus(&self, w_id: &Id) -> bool {
62 *w_id == self.nav.focus
63 }
64
65 #[inline]
73 pub fn nav_focus(&self) -> Option<&Id> {
74 self.nav.focus.as_ref()
75 }
76
77 pub(super) fn clear_nav_focus_on(&mut self, target: &Id) {
78 if let Some(id) = self.nav.focus.as_ref()
79 && target.is_ancestor_of(id)
80 {
81 if matches!(&self.nav.pending_focus, PendingNavFocus::Set { target, .. } if target.as_ref() == Some(id))
82 {
83 self.nav.pending_focus = PendingNavFocus::None;
84 }
85
86 if matches!(self.nav.pending_focus, PendingNavFocus::None) {
87 self.nav.pending_focus = PendingNavFocus::Set {
88 target: None,
89 source: FocusSource::Synthetic,
90 };
91 }
92 }
93 }
94
95 pub(crate) fn set_nav_focus(&mut self, id: Id, source: FocusSource) {
105 self.nav.pending_focus = PendingNavFocus::Set {
106 target: Some(id),
107 source,
108 };
109 }
110}
111
112impl<'a> ConfigCx<'a> {
113 pub fn register_nav_fallback(&mut self, id: Id) {
125 if self.nav.fallback.is_none() {
126 log::debug!(target: "kas_core::event","register_nav_fallback: id={id}");
127 self.nav.fallback = Some(id);
128 }
129 }
130}
131
132impl<'a> EventCx<'a> {
133 pub fn clear_nav_focus(&mut self) {
135 self.nav.pending_focus = PendingNavFocus::Set {
136 target: None,
137 source: FocusSource::Synthetic,
138 };
139 }
140
141 pub fn request_nav_focus(&mut self, id: Id, source: FocusSource) {
149 self.nav.pending_focus = PendingNavFocus::Next {
150 target: Some(id),
151 advance: NavAdvance::None,
152 source,
153 };
154 }
155
156 pub fn next_nav_focus(
166 &mut self,
167 target: impl Into<Option<Id>>,
168 reverse: bool,
169 source: FocusSource,
170 ) {
171 let target = target.into();
172 let advance = match reverse {
173 false => NavAdvance::Forward(target.is_some()),
174 true => NavAdvance::Reverse(target.is_some()),
175 };
176 self.nav.pending_focus = PendingNavFocus::Next {
177 target,
178 advance,
179 source,
180 };
181 }
182
183 #[inline]
185 pub(super) fn nav_next(
186 &mut self,
187 tile: &dyn Tile,
188 focus: Option<&Id>,
189 advance: NavAdvance,
190 ) -> Option<Id> {
191 log::trace!(target: "kas_core::event", "nav_next: focus={focus:?}, advance={advance:?}");
192
193 tile._nav_next(self, focus, advance)
194 }
195
196 pub(super) fn handle_pending_nav_focus(&mut self, widget: Node<'_>) {
197 match std::mem::take(&mut self.nav.pending_focus) {
198 PendingNavFocus::None => (),
199 PendingNavFocus::Set { target, source } => {
200 self.set_nav_focus_impl(widget, target, source)
201 }
202 PendingNavFocus::Next {
203 target,
204 advance,
205 source,
206 } => self.next_nav_focus_impl(widget, target, advance, source),
207 }
208 }
209
210 pub(super) fn set_nav_focus_impl(
212 &mut self,
213 mut widget: Node,
214 target: Option<Id>,
215 source: FocusSource,
216 ) {
217 if target == self.nav.focus || !self.config.nav_focus {
218 return;
219 }
220
221 if let Some(id) = self.input.sel_focus().cloned()
222 && id != target
223 {
224 self.input.clear_sel_socus_on(&id);
225 }
226
227 if let Some(old) = self.nav.focus.take() {
228 self.redraw();
229 self.send_event(widget.re(), old, Event::LostNavFocus);
230 }
231
232 self.nav.focus = target.clone();
233 log::debug!(target: "kas_core::event", "nav_focus = {target:?}");
234 if let Some(id) = target {
235 self.redraw();
236 self.send_event(widget, id, Event::NavFocus(source));
237 }
238 }
239
240 pub(super) fn next_nav_focus_impl(
242 &mut self,
243 mut widget: Node,
244 target: Option<Id>,
245 advance: NavAdvance,
246 source: FocusSource,
247 ) {
248 if !self.config.nav_focus || (target.is_some() && target == self.nav.focus) {
249 return;
250 }
251
252 if let Some(id) = self
253 .popups
254 .last()
255 .filter(|popup| popup.is_sized)
256 .map(|state| state.desc.id.clone())
257 {
258 if id.is_ancestor_of(widget.id_ref()) {
259 } else if let Some(r) = widget.find_node(&id, |node| {
261 self.next_nav_focus_impl(node, target, advance, source)
262 }) {
263 return r;
264 } else {
265 log::warn!(
266 target: "kas_core::event",
267 "next_nav_focus: have open pop-up which is not a child of widget",
268 );
269 return;
270 }
271 }
272
273 let focus = target.or_else(|| self.nav.focus.clone());
274
275 let restart = focus.is_some();
277
278 let mut opt_id = self.nav_next(widget.as_tile(), focus.as_ref(), advance);
279 if restart && opt_id.is_none() {
280 opt_id = self.nav_next(widget.as_tile(), None, advance);
281 }
282
283 self.set_nav_focus_impl(widget, opt_id, source);
284 }
285}