Skip to main content

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}