kui_core/runtime/focus.rs
1//! Keyboard focus and the Tab ring,
2//! the modal scope that narrows both,
3//! and the focus regions that split the ring into rings:
4//! the one focus, who is in the ring,
5//! what moving it does to the edit store and the held keys, the focus a
6//! modal displaces and gives back, and the region a press or a call enters.
7
8use super::*;
9
10impl Core {
11 /// Moves keyboard focus to the next / previous focusable node in tree
12 /// order (from the last laid-out frame; see `access::focusable`),
13 /// wrapping around; with no current focus, enters the first (or last,
14 /// going backwards). What Tab does. The landing node scrolls into
15 /// view, and the focus shows (ring or `focus_bg`), as keyboard focus
16 /// should.
17 pub fn focus_next(&mut self, forward: bool) {
18 let ring = self.focus_ring();
19 if ring.is_empty() {
20 return;
21 }
22 let at = self
23 .focus
24 .and_then(|cur| ring.iter().position(|(_, k)| *k == cur));
25 let (i, _) = match at {
26 Some(p) if forward => ring[(p + 1) % ring.len()],
27 Some(p) => ring[(p + ring.len() - 1) % ring.len()],
28 None if forward => ring[0],
29 None => ring[ring.len() - 1],
30 };
31 self.land_focus(i);
32 }
33
34 /// Puts keyboard focus on node `i` of the last frame the way a Tab
35 /// step does: shown, and scrolled into view.
36 pub(crate) fn land_focus(&mut self, i: usize) {
37 self.move_focus(Some(self.tree.keys[i]));
38 self.focus_visible = true;
39 let rect = Rect::from_pos_size(self.tree.pos[i], self.tree.size[i]);
40 self.scroll_rect_into_view(i, rect, false);
41 }
42
43 /// Where a ring is entered with nothing remembered: its
44 /// `initial_focus`, else its first stop — the rule a modal and a
45 /// region both enter by.
46 fn ring_entry(&self, ring: &[(usize, Key)]) -> Option<Key> {
47 ring.iter()
48 .find(|(i, _)| self.tree.specs[*i].initial_focus)
49 .or_else(|| ring.first())
50 .map(|(_, k)| *k)
51 }
52
53 /// The Tab ring: (tree index, key) of every focusable node of the
54 /// last frame in tree order, `role="none"` subtrees skipped whole —
55 /// and, when a modal is in effect, of its subtree only; otherwise of the region in
56 /// effect, with every region nested in the range skipped whole.
57 ///
58 /// A composite contributes **one** stop instead of one per item:
59 /// the walk enters
60 /// the container as usual — a focusable node inside it but outside
61 /// every item, a "+" at the end of a tab bar, keeps its own stop — and
62 /// then takes the whole of each item's subtree in one step, emitting
63 /// the stop only at the item [`Self::composite_entry`] picked. So the
64 /// stop sits where that item sits in tree order, and Tab out and back
65 /// lands on it again.
66 ///
67 /// A float anchored to a node by key (`FloatAnchor::Node`) is a stop
68 /// of the ring its *anchor* is in, not the one its tree position
69 /// says: skipped whole by the range walk, and walked after the range
70 /// when its anchor's region is the ring's — so a devtools tab's
71 /// content comes after the panel's own stops, and never into the
72 /// app's ring.
73 fn focus_ring(&self) -> Vec<(usize, Key)> {
74 let mut out = Vec::new();
75 let (start, end) = self.ring_range();
76 self.ring_walk(start, end, &mut out);
77 if self.tree.any_node_float && self.modal.is_none() {
78 let mut i = 0;
79 while i < self.tree.len() {
80 if self.anchored_elsewhere(i) {
81 let sub_end = self.tree.subtree_end(i);
82 if self.region_of(i) == self.region {
83 self.ring_walk(i, sub_end, &mut out);
84 }
85 i = sub_end;
86 } else {
87 i += 1;
88 }
89 }
90 }
91 out
92 }
93
94 /// Whether node `i` is a float anchored to a node by key — shown
95 /// somewhere other than where it sits in the tree.
96 fn anchored_elsewhere(&self, i: usize) -> bool {
97 self.tree.any_node_float
98 && matches!(
99 self.tree.specs[i].layout.float,
100 Some(crate::spec::FloatConfig {
101 anchor: crate::spec::FloatAnchor::Node(_),
102 ..
103 })
104 )
105 }
106
107 /// The stops of the tree range `start..end`, appended to `out`: what
108 /// `focus_ring` walks once for the ring's range and once per float
109 /// anchored into it.
110 fn ring_walk(&self, start: usize, end: usize, out: &mut Vec<(usize, Key)>) {
111 use crate::access::Role;
112 let mut i = start;
113 // The composites enclosing `i`, innermost last: how far each runs,
114 // which role its items carry, and the one item that is its stop.
115 let mut open: Vec<(usize, Role, Option<usize>)> = Vec::new();
116 let mut items = Vec::new();
117 while i < end {
118 while open.last().is_some_and(|(e, _, _)| i >= *e) {
119 open.pop();
120 }
121 let role = self.tree.specs[i].access().role;
122 if role == Some(Role::None) {
123 i = self.tree.subtree_end(i);
124 continue;
125 }
126 // A float anchored to a node is walked with its anchor's
127 // ring (`focus_ring`), not where it sits — the range's own
128 // root excepted, which is that walk.
129 if i != start && self.anchored_elsewhere(i) {
130 i = self.tree.subtree_end(i);
131 continue;
132 }
133 // A region inside the range is a ring of its own, not a part
134 // of this one — the range's own root excepted, which is the
135 // region being walked.
136 if self.tree.any_region && i != start && self.tree.specs[i].interact().focus_region {
137 i = self.tree.subtree_end(i);
138 continue;
139 }
140 if let Some(&(_, item, chosen)) = open.last()
141 && role == Some(item)
142 {
143 if chosen == Some(i) {
144 out.push((i, self.tree.keys[i]));
145 }
146 i = self.tree.subtree_end(i);
147 continue;
148 }
149 if let Some(item) = role.and_then(crate::composite::item_role) {
150 crate::composite::items(&self.tree, i, item, &mut items);
151 if crate::composite::is_composite(&self.tree, i, &items) {
152 let chosen = self.composite_entry(&items);
153 open.push((self.tree.subtree_end(i), item, chosen));
154 }
155 }
156 // The root is never a stop. It encloses everything, so a sink
157 // on it hears every key nothing below claims (ADR 0011) and a
158 // view may focus it outright — but a Tab stop is a thing the
159 // user acts on, and the whole window is not one. Without this
160 // a root sink drew the ring around the window.
161 if i != 0 && crate::access::focusable(&self.tree, i) {
162 out.push((i, self.tree.keys[i]));
163 }
164 i += 1;
165 }
166 }
167
168 /// The tree range the ring is made of: the modal's subtree when one is
169 /// in effect (a modal wins), else the
170 /// region in effect's, else the whole tree. A region the frame no
171 /// longer has falls back to the whole tree; `resolve_regions` is what
172 /// moves the region off it, and runs before any ring is asked for.
173 fn ring_range(&self) -> (usize, usize) {
174 if let Some((start, end, _)) = self.modal {
175 return (start, end);
176 }
177 if let Some(i) = self.region_index() {
178 return (i, self.tree.subtree_end(i));
179 }
180 (0, self.tree.len())
181 }
182
183 /// The region in effect's index in the last frame, if the frame
184 /// declared it as one.
185 fn region_index(&self) -> Option<usize> {
186 if !self.tree.any_region {
187 return None;
188 }
189 let key = self.region?;
190 self.tree
191 .keys
192 .iter()
193 .position(|k| *k == key)
194 .filter(|&i| self.tree.specs[i].interact().focus_region)
195 }
196
197 /// The region enclosing node `i` — the nearest ancestor-or-self
198 /// declaring `focus_region` — or `None` for the main ring. A float
199 /// anchored to a node by key belongs where it is *shown*: the walk
200 /// continues from its anchor, not its parent, so a devtools tab's
201 /// content is the dock's to Tab through and never the app's.
202 pub(crate) fn region_of(&self, i: usize) -> Option<Key> {
203 if !self.tree.any_region {
204 return None;
205 }
206 let mut n = i as u32;
207 while n != crate::tree::NIL {
208 let j = n as usize;
209 if self.tree.specs[j].interact().focus_region {
210 return Some(self.tree.keys[j]);
211 }
212 n = self.tree.region_parent(j);
213 }
214 None
215 }
216
217 /// The focus region in effect: the node whose subtree Tab walks, or
218 /// `None` for the main ring.
219 pub fn region(&self) -> Option<Key> {
220 self.region
221 }
222
223 /// Asks to enter a focus region at the end of the frame being built
224 /// (`None` is the main ring): focus lands on what that region last
225 /// held if the node is still there, else its ring's `initial_focus`,
226 /// else the ring's first stop, and shows. Deferred like
227 /// `request_focus_step`, and for one more reason: the caller that
228 /// toggles a dock on and enters it in one `update` names a node the
229 /// last frame did not build. A key the frame does not declare as a
230 /// region raises `focus-region-without-node` and moves nothing.
231 #[track_caller]
232 pub fn focus_region(&mut self, key: Option<Key>) {
233 self.pending_region = Some(match key {
234 Some(k) => RegionTarget::Key(k),
235 None => RegionTarget::Main,
236 });
237 self.owe_frame("focus_region");
238 }
239
240 /// `focus_region` by the label the region's node declares — the
241 /// spelling a caller has for a node that does not exist yet.
242 #[track_caller]
243 pub fn focus_region_by_label(&mut self, label: &str) {
244 self.pending_region = Some(RegionTarget::Label(label.to_string()));
245 self.owe_frame("focus_region_by_label");
246 }
247
248 /// Settles the region on the one enclosing `key`, for a press that
249 /// focused nothing (dead space, a sink keeping the keyboard): Tab
250 /// afterwards enters the ring under the pointer, not the one focus
251 /// left. A press on nothing at all is a
252 /// press on the main ring.
253 pub(crate) fn settle_region(&mut self, key: Option<Key>) {
254 if !self.tree.any_region {
255 return;
256 }
257 self.region = key
258 .and_then(|k| self.tree.index_of(k))
259 .and_then(|i| self.region_of(i));
260 // The press may have focused a sink outside this region (ADR 0011
261 // gives dead space to the enclosing sink); the settle holds over
262 // that focus until it moves.
263 self.region_held = self
264 .focus_index()
265 .is_some_and(|i| self.region_of(i) != self.region);
266 }
267
268 /// Remembers the focus under the region holding it — the one enclosing
269 /// the focused node when the frame has it, else the region in effect
270 /// (a node the frame no longer has is the departed region's) — so
271 /// entering that region again lands there. A blur remembers nothing:
272 /// the last node the region held is still the best place to land.
273 fn remember_region_focus(&mut self) {
274 let Some(focus) = self.focus else { return };
275 let region = match self.focus_index() {
276 Some(i) => self.region_of(i),
277 None => self.region,
278 };
279 match self.region_focus.iter_mut().find(|(r, _)| *r == region) {
280 Some(slot) => slot.1 = Some(focus),
281 None => self.region_focus.push((region, Some(focus))),
282 }
283 }
284
285 /// The focus `region` last held, if that node is in this frame and
286 /// still inside the region.
287 fn remembered_region_focus(&self, region: Option<Key>) -> Option<Key> {
288 let saved = self.region_focus.iter().find(|(r, _)| *r == region)?.1?;
289 let i = self.tree.index_of(saved)?;
290 (self.region_of(i) == region).then_some(saved)
291 }
292
293 /// Where entering `region` lands with nothing remembered: the ring's
294 /// `initial_focus`, else its first stop — the rule a modal enters by.
295 fn region_entry(&mut self, region: Option<Key>) -> Option<Key> {
296 let before = self.region;
297 self.region = region;
298 let ring = self.focus_ring();
299 self.region = before;
300 self.ring_entry(&ring)
301 }
302
303 /// The regions' half of the frame's focus bookkeeping, run after the
304 /// modal's and before a pending Tab step:
305 ///
306 /// - a `focus_region` asked for during the frame enters its target,
307 /// remembering what the region being left holds;
308 /// - a region in effect that this frame stopped declaring hands focus
309 /// back to main's;
310 /// - and the region follows a focus the build moved by declaration —
311 /// `set_focus` learns the region only when the key is in the tree,
312 /// which during a build it may not be yet.
313 pub(crate) fn resolve_regions(&mut self) {
314 let Some(target) = self.pending_region.take() else {
315 if self.tree.any_region || self.region.is_some() {
316 self.follow_region();
317 }
318 return;
319 };
320 let region = match target {
321 RegionTarget::Main => Some(None),
322 RegionTarget::Key(k) => self
323 .tree
324 .keys
325 .iter()
326 .position(|x| *x == k)
327 .filter(|&i| self.tree.any_region && self.tree.specs[i].interact().focus_region)
328 .map(|_| Some(k)),
329 RegionTarget::Label(ref label) => {
330 // The first in tree order, as `key_of` resolves a label,
331 // with the same warning when the name is not unique — the
332 // one lookup (AR45), without the fall-back to last frame:
333 // this runs at the frame's end, when its labels are whole.
334 self.find_label(label, false)
335 .and_then(|k| self.tree.index_of(k))
336 .filter(|&i| self.tree.any_region && self.tree.specs[i].interact().focus_region)
337 .map(|i| Some(self.tree.keys[i]))
338 }
339 };
340 let Some(region) = region else {
341 self.diag
342 .raise(crate::diag::focus_region_without_node(&target));
343 self.follow_region();
344 return;
345 };
346 // Under a modal the call still records where the user meant to be;
347 // the ring stays the modal's until it closes, and the restore then
348 // lands focus where the modal was opened from, which decision 3
349 // follows. Nothing to enter now.
350 // A call is a move on purpose, whatever a press settled before it.
351 self.region_held = false;
352 if self.modal.is_some() {
353 self.remember_region_focus();
354 self.region = region;
355 return;
356 }
357 self.remember_region_focus();
358 let landing = self
359 .remembered_region_focus(region)
360 .or_else(|| self.region_entry(region));
361 self.region = region;
362 match landing.and_then(|k| self.tree.index_of(k)) {
363 Some(i) => self.land_focus(i),
364 None => {
365 self.move_focus(landing);
366 self.focus_visible = true;
367 }
368 }
369 }
370
371 /// The region follows focus, and a region that went away hands focus
372 /// back.
373 fn follow_region(&mut self) {
374 if let Some(i) = self.focus_index() {
375 // Focus moved during the build to a node that was not in the
376 // tree yet (a declaration), so `set_focus` could not place it;
377 // the region it left keeps the memory it had — this focus was
378 // never that region's to remember. A region a press settled
379 // away from a focus that has not moved since stands.
380 if !self.region_held {
381 self.region = self.region_of(i);
382 }
383 self.remember_region_focus();
384 return;
385 }
386 // Nothing focused, or a focus on a node the frame no longer has:
387 // a region that is gone falls back to main's remembered focus. Its
388 // own memory is kept — a dock toggled off and on again lands where
389 // the user was, and a node that has not come back is checked for
390 // on the way in.
391 if self.region.is_some() && self.region_index().is_none() {
392 // The focus it had on the way out, node or no node: what the
393 // region is entered on if the node comes back with it.
394 self.remember_region_focus();
395 self.region = None;
396 let back = self.remembered_region_focus(None);
397 self.move_focus(back);
398 }
399 }
400
401 /// Which item of a composite is its Tab stop, in precedence order:
402 /// the item that currently holds focus,
403 /// else one declaring `initial_focus`, else the one declaring
404 /// `selected`, else the first. **No new retained state** — the roving
405 /// tabindex a browser keeps per composite is, in every pattern kui has,
406 /// the item the app already marks `selected`, and the same fact that
407 /// tells a reader which one is current tells the keyboard where to
408 /// enter. Only focusable items are candidates, so a disabled one is
409 /// skipped here as it is everywhere else.
410 fn composite_entry(&self, items: &[usize]) -> Option<usize> {
411 let live = |&&j: &&usize| crate::access::focusable(&self.tree, j);
412 let pick = |f: &dyn Fn(usize) -> bool| items.iter().filter(live).copied().find(|&j| f(j));
413 pick(&|j| Some(self.tree.keys[j]) == self.focus)
414 .or_else(|| pick(&|j| self.tree.specs[j].initial_focus))
415 .or_else(|| pick(&|j| self.tree.specs[j].access().selected))
416 .or_else(|| items.iter().filter(live).copied().next())
417 }
418
419 /// The frame's modal scope: the tree range of the last node declaring
420 /// `modal`, and its key. The last one wins, so a confirm declared
421 /// inside (or after) a dialog is the one in effect and the dialog
422 /// under it is as inert as the app under the dialog.
423 pub(crate) fn modal_scope(&self) -> Option<(usize, usize, Key)> {
424 let i = (0..self.tree.len())
425 .rev()
426 .find(|&i| self.tree.specs[i].events().modal.is_some())?;
427 Some((i, self.tree.subtree_end(i), self.tree.keys[i]))
428 }
429
430 /// The key of the modal in effect this frame, if any.
431 pub fn modal(&self) -> Option<Key> {
432 self.modal.map(|(_, _, key)| key)
433 }
434
435 /// Whether node `i` takes input: everything does, until a modal is in
436 /// effect — then its subtree does, and so does window chrome (the
437 /// window's own controls belong to the platform, not to the dialog).
438 pub(crate) fn interactive(&self, i: usize) -> bool {
439 match self.modal {
440 Some((start, end, _)) => {
441 (start..end).contains(&i) || self.tree.specs[i].window.is_some()
442 }
443 None => true,
444 }
445 }
446
447 /// Whether node `key` is inside the frame's modal scope (nothing is
448 /// outside one when there is no modal).
449 pub(crate) fn within_modal(&self, key: Key) -> bool {
450 let Some((start, end, _)) = self.modal else {
451 return true;
452 };
453 self.tree.keys[start..end].contains(&key)
454 }
455
456 /// Focus follows the modal: a modal that appears remembers the focus
457 /// it displaces and takes focus into itself; one that stops being
458 /// declared gives that focus back. Run once per frame, after the
459 /// scope is known.
460 pub(crate) fn resolve_modal_focus(&mut self) {
461 // This frame's modals in tree order, each carrying the focus it
462 // displaced when it first appeared.
463 let mut now: Vec<(Key, Option<Key>)> = Vec::new();
464 if self.tree.any_modal {
465 for i in 0..self.tree.len() {
466 if self.tree.specs[i].events().modal.is_none() {
467 continue;
468 }
469 let key = self.tree.keys[i];
470 let saved = self
471 .modal_focus
472 .iter()
473 .find(|(k, _)| *k == key)
474 .map_or(self.focus, |(_, f)| *f);
475 now.push((key, saved));
476 }
477 }
478 // The outermost modal that went away hands its focus back, so a
479 // dialog and the confirm inside it closing together land where
480 // the dialog was opened from.
481 let closed = self
482 .modal_focus
483 .iter()
484 .find(|(k, _)| !now.iter().any(|(n, _)| n == k))
485 .map(|(_, saved)| saved.filter(|key| self.tree.keys.contains(key)));
486 // A `keyFocus` *edge* on this same frame — a node declared focused
487 // now and not last frame — is the app saying where focus lands on
488 // the way out, and it wins over the restore (ADR 0003, decision
489 // 4): a rename editor opened on a node created in the same frame
490 // displaced the node the user was on *before*, and the restore
491 // is the default for an app that says nothing, not a rule for one
492 // that did. A sink redeclaring itself every frame is no edge, so
493 // the restore still lands where it always has under one.
494 // The same for an imperative move since the last frame — a
495 // handler closing the dialog and naming where focus lands
496 // (AR17): three doors, one precedence.
497 let edge = self
498 .declared_focus
499 .iter()
500 .any(|k| !self.declared_focus_last.contains(k))
501 || self.focus_asked;
502 if let Some(saved) = closed
503 && !edge
504 {
505 // Exactly what it displaced, nothing included: leaving focus
506 // on the dismissed modal's own button would be a focus on a
507 // node that is not there any more.
508 self.move_focus(saved);
509 }
510 self.modal_focus = now;
511 // Containment: focus outside the scope enters it, or is dropped
512 // when it holds none. Not `focus_visible`: the app showed the
513 // modal, nobody pressed a key. The entry is the first node in the
514 // ring declaring `initial_focus` — a destructive confirm opening
515 // on its Cancel — and the ring's first when none does. Only
516 // *entry* reads it: focus already inside the scope never reaches
517 // here, so a Tab press stands and a nested confirm closing leaves
518 // focus where it handed it back.
519 if self.modal.is_some() && !self.focus.is_some_and(|k| self.within_modal(k)) {
520 let ring = self.focus_ring();
521 let entry = self.ring_entry(&ring);
522 self.move_focus(entry);
523 }
524 // Read: the stamp covers the moves since the last frame's end —
525 // a handler's between frames and the view's during the build —
526 // and a redraw of the same tree starts clean.
527 self.focus_asked = false;
528 }
529
530 /// Whether a primary press on node `key` leaves the keyboard alone:
531 /// `keepFocus` on the node or an ancestor. Such a press
532 /// acts and touches nothing the keyboard has — not the focus, not the
533 /// ring Tab walks next (a press settling the region is a press
534 /// *taking* the keyboard), and not the selection, which is what a
535 /// toolbar's Copy or Bold acts on. One walk per press.
536 pub(crate) fn keeps_focus(&self, key: Key) -> bool {
537 let Some(i) = self.tree.index_of(key) else {
538 return false;
539 };
540 let mut n = i as u32;
541 while n != crate::tree::NIL {
542 if self.tree.specs[n as usize].events().keep_focus {
543 return true;
544 }
545 n = self.tree.parent[n as usize];
546 }
547 false
548 }
549
550 /// Where a primary press on node `key` puts the keyboard: the node
551 /// itself when it is focusable, and nothing when it is not — except
552 /// on window chrome, which leaves focus alone, and inside a key sink,
553 /// which keeps the keyboard through any press landing in its subtree.
554 ///
555 /// A sink is an app that owns its keyboard (the same reason Tab stays
556 /// with it). The panes, buttons and dead
557 /// space it draws are that app's surface, and a derived button among
558 /// them is focusable enough to be a Tab stop without being entitled to
559 /// take the keys away from the app drawing it — otherwise the first
560 /// click anywhere in a multiplexer kills every chord, permanently,
561 /// because `take_key_focus` is edge-triggered and will not ask twice.
562 /// An editor or a nested sink is its own keyboard owner and still
563 /// takes focus (the editor through the caret arm above). A press on a
564 /// `keepFocus` node never gets here: see [`Self::keeps_focus`].
565 pub(crate) fn press_focus(&self, key: Key, focusable: bool) -> Option<Key> {
566 let Some(i) = self.tree.index_of(key) else {
567 return focusable.then_some(key);
568 };
569 // Window chrome belongs to the platform, not to the app (decision
570 // 2, the same reason it is never a Tab stop): dragging a window by
571 // its titlebar, or pressing minimize, is not the app being asked
572 // to give up its keyboard.
573 if self.tree.specs[i].window.is_some() {
574 return self.focus;
575 }
576 if self.tree.specs[i].events().on_key.is_some() {
577 return Some(key);
578 }
579 match self.enclosing_sink(i) {
580 Some(j) => Some(self.tree.keys[j]),
581 None => focusable.then_some(key),
582 }
583 }
584
585 /// The nearest key sink strictly above node `i`: the walk
586 /// [`Self::press_focus`] makes for a press, made for a key as well.
587 ///
588 /// A disabled node is not a sink at all,
589 /// so it neither answers here nor hides a live sink further up. The
590 /// walk stops at the modal boundary rather than climbing through it:
591 /// the app around a dialog is inert, and a shell that
592 /// kept hearing shortcuts while its own dialog was up would be running
593 /// commands against a surface the user cannot see the state of.
594 pub(crate) fn enclosing_sink(&self, i: usize) -> Option<usize> {
595 let mut n = self.tree.parent[i];
596 while n != crate::tree::NIL {
597 let j = n as usize;
598 if !self.interactive(j) {
599 return None;
600 }
601 if self.tree.specs[j].events().on_key.is_some() && !self.tree.specs[j].disabled {
602 return Some(j);
603 }
604 n = self.tree.parent[j];
605 }
606 None
607 }
608
609 /// The node whose context menu a secondary press on node `i` opens:
610 /// `i` itself when it declares a live `on_context_menu`, else the
611 /// nearest enclosing node that does — the same walk and the same
612 /// modal boundary as `enclosing_sink`, because an unclaimed press is
613 /// unclaimed the way an unclaimed key is. A disabled
614 /// node's own menu is skipped like a disabled sink's, so the press
615 /// reaches the container's.
616 pub(crate) fn enclosing_menu(&self, i: usize) -> Option<usize> {
617 let offers = |j: usize| {
618 self.tree.specs[j].events().on_context_menu.is_some() && !self.tree.specs[j].disabled
619 };
620 if offers(i) {
621 return Some(i);
622 }
623 let mut n = self.tree.parent[i];
624 while n != crate::tree::NIL {
625 let j = n as usize;
626 if !self.interactive(j) {
627 return None;
628 }
629 if offers(j) {
630 return Some(j);
631 }
632 n = self.tree.parent[j];
633 }
634 None
635 }
636
637 /// The node a non-primary press of `button` on node `i` goes to:
638 /// `i` itself when its live `on_button` claims the
639 /// button, else the nearest enclosing node whose does — the walk
640 /// `enclosing_menu` takes, stopping at the modal boundary and skipping
641 /// a disabled node's own. For the secondary button a nearer live
642 /// `on_context_menu` ends the walk with nothing, the nested
643 /// declaration winning over its ancestor's either way; a node that
644 /// declares both is the button's, which is what claiming it says.
645 pub(crate) fn enclosing_button(&self, i: usize, button: MouseButton) -> Option<usize> {
646 let mut n = i as u32;
647 while n != crate::tree::NIL {
648 let j = n as usize;
649 if j != i && !self.interactive(j) {
650 return None;
651 }
652 let spec = &self.tree.specs[j];
653 if !spec.disabled {
654 let ev = spec.events();
655 if ev.on_button.is_some() && ev.buttons.contains(button) {
656 return Some(j);
657 }
658 if button == MouseButton::Secondary && ev.on_context_menu.is_some() {
659 return None;
660 }
661 }
662 n = self.tree.parent[j];
663 }
664 None
665 }
666
667 /// The zone node `i` belongs to: itself when it declares `on_drop`,
668 /// else the nearest enclosing declaration —
669 /// the same walk as `enclosing_menu`, stopping at the modal boundary
670 /// and skipping a disabled node's own.
671 pub(crate) fn enclosing_drop(&self, i: usize) -> Option<usize> {
672 let declares = |j: usize| {
673 self.tree.specs[j].events().on_drop.is_some() && !self.tree.specs[j].disabled
674 };
675 if declares(i) {
676 return Some(i);
677 }
678 let mut n = self.tree.parent[i];
679 while n != crate::tree::NIL {
680 let j = n as usize;
681 if !self.interactive(j) {
682 return None;
683 }
684 if declares(j) {
685 return Some(j);
686 }
687 n = self.tree.parent[j];
688 }
689 None
690 }
691
692 /// The focused node's index in the last frame, if it is there.
693 pub(crate) fn focus_index(&self) -> Option<usize> {
694 self.tree.index_of(self.focus?)
695 }
696
697 /// Whether `key` holds keyboard focus — any node (see `focus`).
698 pub fn is_focused(&self, key: Key) -> bool {
699 self.focus == Some(key)
700 }
701
702 /// The node holding keyboard focus: an editor, an `on_key` sink, or
703 /// a control Tab (or assistive technology, or `set_focus`) put it on.
704 pub fn focus(&self) -> Option<Key> {
705 self.focus
706 }
707
708 /// Whether focus got where it is by keyboard or assistive technology
709 /// rather than a click — when it shows (the default ring, or the
710 /// node's `focus_bg`).
711 pub fn focus_visible(&self) -> bool {
712 self.focus_visible
713 }
714
715 /// Moves keyboard focus (None blurs) — the app's door: `Ui::focus`,
716 /// Node's `focus`, `kui_focus`, Lua's `env.set_focus`. Any node can be
717 /// focused this way; only focusable ones (see `access::focusable`)
718 /// are reached by Tab. A move made here is the app saying where focus
719 /// goes, and it stands at the frame's end against a closing modal's
720 /// restore, the way a `keyFocus` edge does: the restore is the default for an app that said
721 /// nothing, and this is an app that did. The core's own moves — a
722 /// press, a Tab, an autofocus, the restore itself — go through
723 /// `move_focus` and say nothing.
724 pub fn set_focus(&mut self, key: Option<Key>) {
725 if self.focus != key {
726 self.focus_asked = true;
727 }
728 self.move_focus(key);
729 }
730
731 /// Emits `focus` events for the `on_focus` nodes the focus left and
732 /// entered since the last report, `by` what moved it: the input being
733 /// handled, or `"program"` at a frame's end. A node
734 /// hears focus anywhere in its subtree; leaving is reported innermost
735 /// first, entering outermost first, as the focus crosses them.
736 pub(crate) fn report_focus(&mut self, by: &'static str, out: &mut Vec<UiEvent>) {
737 // The `on_focus` nodes on the focused node's path, outermost first,
738 // by index: the tags are cloned only when the set changed, so a
739 // frame where the focus stayed put costs the walk and nothing else.
740 let mut path = Vec::new();
741 if let Some(mut i) = self.focus.and_then(|k| self.tree.index_of(k)) {
742 loop {
743 if self.tree.specs[i].events().on_focus.is_some() {
744 path.push(i);
745 }
746 let p = self.tree.parent[i];
747 if p == crate::tree::NIL {
748 break;
749 }
750 i = p as usize;
751 }
752 path.reverse();
753 }
754 if path.len() == self.focus_reported.len()
755 && path
756 .iter()
757 .zip(&self.focus_reported)
758 .all(|(&i, r)| self.tree.keys[i] == r.0)
759 {
760 return;
761 }
762 let now: Vec<_> = path
763 .iter()
764 .map(|&i| {
765 let tag = self.tree.specs[i]
766 .events()
767 .on_focus
768 .clone()
769 .unwrap_or_default();
770 (self.tree.keys[i], self.tree.origins[i], tag)
771 })
772 .collect();
773 let event = |(key, origin, tag): &(Key, crate::tree::OriginId, Value), phase: &str| {
774 let payload = Value::map([
775 ("kind", Value::str("focus")),
776 ("phase", Value::str(phase)),
777 ("by", Value::str(by)),
778 ]);
779 UiEvent::on(*origin, *key, payload).tagged(Some(tag))
780 };
781 for r in self.focus_reported.iter().rev() {
782 if !now.iter().any(|n| n.0 == r.0) {
783 out.push(event(r, "out"));
784 }
785 }
786 for n in &now {
787 if !self.focus_reported.iter().any(|r| r.0 == n.0) {
788 out.push(event(n, "in"));
789 }
790 }
791 self.focus_reported = now;
792 }
793
794 /// The one writer of `focus`: the edit store mirrors it for editor
795 /// keys, a landing editor scrolls its caret into view, the region
796 /// follows.
797 pub(crate) fn move_focus(&mut self, key: Option<Key>) {
798 let edit_key = key.filter(|k| self.edit.contains(*k));
799 if self.focus != key {
800 // The keys the leaving sink is holding come up first, while it
801 // is still the focus that routing resolves against.
802 self.release_held_keys();
803 if let Some(k) = edit_key {
804 self.edit.caret_moved = Some(k);
805 }
806 }
807 // The region follows focus (`docs/adr/0022`, decision 3) — when
808 // the key is in the tree; a build declaring focus on a node it has
809 // not pushed yet is caught up by `resolve_regions`. The region
810 // being left remembers what it held first, so entering it again
811 // lands there.
812 if key != self.focus {
813 self.region_held = false;
814 }
815 if self.tree.any_region
816 && let Some(k) = key
817 && let Some(i) = self.tree.index_of(k)
818 {
819 let region = self.region_of(i);
820 if region != self.region {
821 self.remember_region_focus();
822 self.region = region;
823 }
824 }
825 self.focus = key;
826 self.edit.set_focus(edit_key);
827 }
828
829 /// Asks for a Tab step (`forward`) / Shift-Tab step at the end of the
830 /// frame being built. `focus_next` moves focus now, against the last
831 /// finished tree — which is what a driver handling a key press between
832 /// frames wants, and exactly what a *view* cannot use, since its own
833 /// tree does not exist yet. A view asks with this instead and the step
834 /// lands on the frame it is declaring.
835 #[track_caller]
836 pub fn request_focus_step(&mut self, forward: bool) {
837 self.pending_focus_step = Some(forward);
838 self.owe_frame("request_focus_step");
839 }
840
841 /// Declares a node focused this frame (None blurs at once). The
842 /// declaration is edge-triggered: the node takes focus on the first
843 /// frame it is declared and keeps being declared without effect
844 /// afterwards, so a view that repeats it every frame (an app that
845 /// owns its keyboard, a `keyFocus` prop) does not clobber the focus a
846 /// Tab press or a click moved. Programmatic focus keeps the modality
847 /// of the last input (it shows after keyboard use, not after a
848 /// click). To move focus at any time, `set_focus`.
849 pub fn set_key_focus(&mut self, key: Option<Key>) {
850 let Some(k) = key else {
851 self.move_focus(None);
852 return;
853 };
854 if !self.declared_focus.contains(&k) {
855 self.declared_focus.push(k);
856 }
857 if !self.declared_focus_last.contains(&k) {
858 self.move_focus(Some(k));
859 }
860 }
861
862 /// The node holding keyboard focus (the same as `focus`; kept from
863 /// when only key sinks and editors could).
864 pub fn key_focus(&self) -> Option<Key> {
865 self.focus
866 }
867
868 /// The keys that declared key focus while the current frame was built
869 /// (`set_key_focus`). The scene corpus's coverage derivation reads it:
870 /// `keyFocus` leaves no mark on the tree, and the focus it takes is
871 /// indistinguishable from the focus a click takes.
872 #[cfg(feature = "conformance")]
873 pub(crate) fn declared_focus(&self) -> &[Key] {
874 &self.declared_focus
875 }
876}