Skip to main content

kui_core/runtime/
focus.rs

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