kui-core 0.1.0-alpha.45

kui contract: flat per-frame tree, clay-style flex layout, text stack, events as data, quad display list
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
//! Keyboard focus and the Tab ring,
//! the modal scope that narrows both,
//! and the focus regions that split the ring into rings:
//! the one focus, who is in the ring,
//! what moving it does to the edit store and the held keys, the focus a
//! modal displaces and gives back, and the region a press or a call enters.

use super::*;

impl Core {
    /// Moves keyboard focus to the next / previous focusable node in tree
    /// order (from the last laid-out frame; see `access::focusable`),
    /// wrapping around; with no current focus, enters the first (or last,
    /// going backwards). What Tab does. The landing node scrolls into
    /// view, and the focus shows (ring or `focus_bg`), as keyboard focus
    /// should.
    pub fn focus_next(&mut self, forward: bool) {
        let ring = self.focus_ring();
        if ring.is_empty() {
            return;
        }
        let at = self
            .focus
            .and_then(|cur| ring.iter().position(|(_, k)| *k == cur));
        let (i, _) = match at {
            Some(p) if forward => ring[(p + 1) % ring.len()],
            Some(p) => ring[(p + ring.len() - 1) % ring.len()],
            None if forward => ring[0],
            None => ring[ring.len() - 1],
        };
        self.land_focus(i);
    }

    /// Puts keyboard focus on node `i` of the last frame the way a Tab
    /// step does: shown, and scrolled into view.
    pub(crate) fn land_focus(&mut self, i: usize) {
        self.move_focus(Some(self.tree.keys[i]));
        self.focus_visible = true;
        let rect = Rect::from_pos_size(self.tree.pos[i], self.tree.size[i]);
        self.scroll_rect_into_view(i, rect, false);
    }

    /// Where a ring is entered with nothing remembered: its
    /// `initial_focus`, else its first stop — the rule a modal and a
    /// region both enter by.
    fn ring_entry(&self, ring: &[(usize, Key)]) -> Option<Key> {
        ring.iter()
            .find(|(i, _)| self.tree.specs[*i].initial_focus)
            .or_else(|| ring.first())
            .map(|(_, k)| *k)
    }

    /// The Tab ring: (tree index, key) of every focusable node of the
    /// last frame in tree order, `role="none"` subtrees skipped whole —
    /// and, when a modal is in effect, of its subtree only; otherwise of the region in
    /// effect, with every region nested in the range skipped whole.
    ///
    /// A composite contributes **one** stop instead of one per item:
    /// the walk enters
    /// the container as usual — a focusable node inside it but outside
    /// every item, a "+" at the end of a tab bar, keeps its own stop — and
    /// then takes the whole of each item's subtree in one step, emitting
    /// the stop only at the item [`Self::composite_entry`] picked. So the
    /// stop sits where that item sits in tree order, and Tab out and back
    /// lands on it again.
    ///
    /// A float anchored to a node by key (`FloatAnchor::Node`) is a stop
    /// of the ring its *anchor* is in, not the one its tree position
    /// says: skipped whole by the range walk, and walked after the range
    /// when its anchor's region is the ring's — so a devtools tab's
    /// content comes after the panel's own stops, and never into the
    /// app's ring.
    fn focus_ring(&self) -> Vec<(usize, Key)> {
        let mut out = Vec::new();
        let (start, end) = self.ring_range();
        self.ring_walk(start, end, &mut out);
        if self.tree.any_node_float && self.modal.is_none() {
            let mut i = 0;
            while i < self.tree.len() {
                if self.anchored_elsewhere(i) {
                    let sub_end = self.tree.subtree_end(i);
                    if self.region_of(i) == self.region {
                        self.ring_walk(i, sub_end, &mut out);
                    }
                    i = sub_end;
                } else {
                    i += 1;
                }
            }
        }
        out
    }

    /// Whether node `i` is a float anchored to a node by key — shown
    /// somewhere other than where it sits in the tree.
    fn anchored_elsewhere(&self, i: usize) -> bool {
        self.tree.any_node_float
            && matches!(
                self.tree.specs[i].layout.float,
                Some(crate::spec::FloatConfig {
                    anchor: crate::spec::FloatAnchor::Node(_),
                    ..
                })
            )
    }

    /// The stops of the tree range `start..end`, appended to `out`: what
    /// `focus_ring` walks once for the ring's range and once per float
    /// anchored into it.
    fn ring_walk(&self, start: usize, end: usize, out: &mut Vec<(usize, Key)>) {
        use crate::access::Role;
        let mut i = start;
        // The composites enclosing `i`, innermost last: how far each runs,
        // which role its items carry, and the one item that is its stop.
        let mut open: Vec<(usize, Role, Option<usize>)> = Vec::new();
        let mut items = Vec::new();
        while i < end {
            while open.last().is_some_and(|(e, _, _)| i >= *e) {
                open.pop();
            }
            let role = self.tree.specs[i].access().role;
            if role == Some(Role::None) {
                i = self.tree.subtree_end(i);
                continue;
            }
            // A float anchored to a node is walked with its anchor's
            // ring (`focus_ring`), not where it sits — the range's own
            // root excepted, which is that walk.
            if i != start && self.anchored_elsewhere(i) {
                i = self.tree.subtree_end(i);
                continue;
            }
            // A region inside the range is a ring of its own, not a part
            // of this one — the range's own root excepted, which is the
            // region being walked.
            if self.tree.any_region && i != start && self.tree.specs[i].interact().focus_region {
                i = self.tree.subtree_end(i);
                continue;
            }
            if let Some(&(_, item, chosen)) = open.last()
                && role == Some(item)
            {
                if chosen == Some(i) {
                    out.push((i, self.tree.keys[i]));
                }
                i = self.tree.subtree_end(i);
                continue;
            }
            if let Some(item) = role.and_then(crate::composite::item_role) {
                crate::composite::items(&self.tree, i, item, &mut items);
                if crate::composite::is_composite(&self.tree, i, &items) {
                    let chosen = self.composite_entry(&items);
                    open.push((self.tree.subtree_end(i), item, chosen));
                }
            }
            // The root is never a stop. It encloses everything, so a sink
            // on it hears every key nothing below claims (ADR 0011) and a
            // view may focus it outright — but a Tab stop is a thing the
            // user acts on, and the whole window is not one. Without this
            // a root sink drew the ring around the window.
            if i != 0 && crate::access::focusable(&self.tree, i) {
                out.push((i, self.tree.keys[i]));
            }
            i += 1;
        }
    }

    /// The tree range the ring is made of: the modal's subtree when one is
    /// in effect (a modal wins), else the
    /// region in effect's, else the whole tree. A region the frame no
    /// longer has falls back to the whole tree; `resolve_regions` is what
    /// moves the region off it, and runs before any ring is asked for.
    fn ring_range(&self) -> (usize, usize) {
        if let Some((start, end, _)) = self.modal {
            return (start, end);
        }
        if let Some(i) = self.region_index() {
            return (i, self.tree.subtree_end(i));
        }
        (0, self.tree.len())
    }

    /// The region in effect's index in the last frame, if the frame
    /// declared it as one.
    fn region_index(&self) -> Option<usize> {
        if !self.tree.any_region {
            return None;
        }
        let key = self.region?;
        self.tree
            .keys
            .iter()
            .position(|k| *k == key)
            .filter(|&i| self.tree.specs[i].interact().focus_region)
    }

    /// The region enclosing node `i` — the nearest ancestor-or-self
    /// declaring `focus_region` — or `None` for the main ring. A float
    /// anchored to a node by key belongs where it is *shown*: the walk
    /// continues from its anchor, not its parent, so a devtools tab's
    /// content is the dock's to Tab through and never the app's.
    pub(crate) fn region_of(&self, i: usize) -> Option<Key> {
        if !self.tree.any_region {
            return None;
        }
        let mut n = i as u32;
        while n != crate::tree::NIL {
            let j = n as usize;
            if self.tree.specs[j].interact().focus_region {
                return Some(self.tree.keys[j]);
            }
            n = self.tree.region_parent(j);
        }
        None
    }

    /// The focus region in effect: the node whose subtree Tab walks, or
    /// `None` for the main ring.
    pub fn region(&self) -> Option<Key> {
        self.region
    }

    /// Asks to enter a focus region at the end of the frame being built
    /// (`None` is the main ring): focus lands on what that region last
    /// held if the node is still there, else its ring's `initial_focus`,
    /// else the ring's first stop, and shows. Deferred like
    /// `request_focus_step`, and for one more reason: the caller that
    /// toggles a dock on and enters it in one `update` names a node the
    /// last frame did not build. A key the frame does not declare as a
    /// region raises `focus-region-without-node` and moves nothing.
    #[track_caller]
    pub fn focus_region(&mut self, key: Option<Key>) {
        self.pending_region = Some(match key {
            Some(k) => RegionTarget::Key(k),
            None => RegionTarget::Main,
        });
        self.owe_frame("focus_region");
    }

    /// `focus_region` by the label the region's node declares — the
    /// spelling a caller has for a node that does not exist yet.
    #[track_caller]
    pub fn focus_region_by_label(&mut self, label: &str) {
        self.pending_region = Some(RegionTarget::Label(label.to_string()));
        self.owe_frame("focus_region_by_label");
    }

    /// Settles the region on the one enclosing `key`, for a press that
    /// focused nothing (dead space, a sink keeping the keyboard): Tab
    /// afterwards enters the ring under the pointer, not the one focus
    /// left. A press on nothing at all is a
    /// press on the main ring.
    pub(crate) fn settle_region(&mut self, key: Option<Key>) {
        if !self.tree.any_region {
            return;
        }
        self.region = key
            .and_then(|k| self.tree.index_of(k))
            .and_then(|i| self.region_of(i));
        // The press may have focused a sink outside this region (ADR 0011
        // gives dead space to the enclosing sink); the settle holds over
        // that focus until it moves.
        self.region_held = self
            .focus_index()
            .is_some_and(|i| self.region_of(i) != self.region);
    }

    /// Remembers the focus under the region holding it — the one enclosing
    /// the focused node when the frame has it, else the region in effect
    /// (a node the frame no longer has is the departed region's) — so
    /// entering that region again lands there. A blur remembers nothing:
    /// the last node the region held is still the best place to land.
    fn remember_region_focus(&mut self) {
        let Some(focus) = self.focus else { return };
        let region = match self.focus_index() {
            Some(i) => self.region_of(i),
            None => self.region,
        };
        match self.region_focus.iter_mut().find(|(r, _)| *r == region) {
            Some(slot) => slot.1 = Some(focus),
            None => self.region_focus.push((region, Some(focus))),
        }
    }

    /// The focus `region` last held, if that node is in this frame and
    /// still inside the region.
    fn remembered_region_focus(&self, region: Option<Key>) -> Option<Key> {
        let saved = self.region_focus.iter().find(|(r, _)| *r == region)?.1?;
        let i = self.tree.index_of(saved)?;
        (self.region_of(i) == region).then_some(saved)
    }

    /// Where entering `region` lands with nothing remembered: the ring's
    /// `initial_focus`, else its first stop — the rule a modal enters by.
    fn region_entry(&mut self, region: Option<Key>) -> Option<Key> {
        let before = self.region;
        self.region = region;
        let ring = self.focus_ring();
        self.region = before;
        self.ring_entry(&ring)
    }

    /// The regions' half of the frame's focus bookkeeping, run after the
    /// modal's and before a pending Tab step:
    ///
    /// - a `focus_region` asked for during the frame enters its target,
    ///   remembering what the region being left holds;
    /// - a region in effect that this frame stopped declaring hands focus
    ///   back to main's;
    /// - and the region follows a focus the build moved by declaration —
    ///   `set_focus` learns the region only when the key is in the tree,
    ///   which during a build it may not be yet.
    pub(crate) fn resolve_regions(&mut self) {
        let Some(target) = self.pending_region.take() else {
            if self.tree.any_region || self.region.is_some() {
                self.follow_region();
            }
            return;
        };
        let region = match target {
            RegionTarget::Main => Some(None),
            RegionTarget::Key(k) => self
                .tree
                .keys
                .iter()
                .position(|x| *x == k)
                .filter(|&i| self.tree.any_region && self.tree.specs[i].interact().focus_region)
                .map(|_| Some(k)),
            RegionTarget::Label(ref label) => {
                // The first in tree order, as `key_of` resolves a label,
                // with the same warning when the name is not unique — the
                // one lookup (AR45), without the fall-back to last frame:
                // this runs at the frame's end, when its labels are whole.
                self.find_label(label, false)
                    .and_then(|k| self.tree.index_of(k))
                    .filter(|&i| self.tree.any_region && self.tree.specs[i].interact().focus_region)
                    .map(|i| Some(self.tree.keys[i]))
            }
        };
        let Some(region) = region else {
            self.diag
                .raise(crate::diag::focus_region_without_node(&target));
            self.follow_region();
            return;
        };
        // Under a modal the call still records where the user meant to be;
        // the ring stays the modal's until it closes, and the restore then
        // lands focus where the modal was opened from, which decision 3
        // follows. Nothing to enter now.
        // A call is a move on purpose, whatever a press settled before it.
        self.region_held = false;
        if self.modal.is_some() {
            self.remember_region_focus();
            self.region = region;
            return;
        }
        self.remember_region_focus();
        let landing = self
            .remembered_region_focus(region)
            .or_else(|| self.region_entry(region));
        self.region = region;
        match landing.and_then(|k| self.tree.index_of(k)) {
            Some(i) => self.land_focus(i),
            None => {
                self.move_focus(landing);
                self.focus_visible = true;
            }
        }
    }

    /// The region follows focus, and a region that went away hands focus
    /// back.
    fn follow_region(&mut self) {
        if let Some(i) = self.focus_index() {
            // Focus moved during the build to a node that was not in the
            // tree yet (a declaration), so `set_focus` could not place it;
            // the region it left keeps the memory it had — this focus was
            // never that region's to remember. A region a press settled
            // away from a focus that has not moved since stands.
            if !self.region_held {
                self.region = self.region_of(i);
            }
            self.remember_region_focus();
            return;
        }
        // Nothing focused, or a focus on a node the frame no longer has:
        // a region that is gone falls back to main's remembered focus. Its
        // own memory is kept — a dock toggled off and on again lands where
        // the user was, and a node that has not come back is checked for
        // on the way in.
        if self.region.is_some() && self.region_index().is_none() {
            // The focus it had on the way out, node or no node: what the
            // region is entered on if the node comes back with it.
            self.remember_region_focus();
            self.region = None;
            let back = self.remembered_region_focus(None);
            self.move_focus(back);
        }
    }

    /// Which item of a composite is its Tab stop, in precedence order:
    /// the item that currently holds focus,
    /// else one declaring `initial_focus`, else the one declaring
    /// `selected`, else the first. **No new retained state** — the roving
    /// tabindex a browser keeps per composite is, in every pattern kui has,
    /// the item the app already marks `selected`, and the same fact that
    /// tells a reader which one is current tells the keyboard where to
    /// enter. Only focusable items are candidates, so a disabled one is
    /// skipped here as it is everywhere else.
    fn composite_entry(&self, items: &[usize]) -> Option<usize> {
        let live = |&&j: &&usize| crate::access::focusable(&self.tree, j);
        let pick = |f: &dyn Fn(usize) -> bool| items.iter().filter(live).copied().find(|&j| f(j));
        pick(&|j| Some(self.tree.keys[j]) == self.focus)
            .or_else(|| pick(&|j| self.tree.specs[j].initial_focus))
            .or_else(|| pick(&|j| self.tree.specs[j].access().selected))
            .or_else(|| items.iter().filter(live).copied().next())
    }

    /// The frame's modal scope: the tree range of the last node declaring
    /// `modal`, and its key. The last one wins, so a confirm declared
    /// inside (or after) a dialog is the one in effect and the dialog
    /// under it is as inert as the app under the dialog.
    pub(crate) fn modal_scope(&self) -> Option<(usize, usize, Key)> {
        let i = (0..self.tree.len())
            .rev()
            .find(|&i| self.tree.specs[i].events().modal.is_some())?;
        Some((i, self.tree.subtree_end(i), self.tree.keys[i]))
    }

    /// The key of the modal in effect this frame, if any.
    pub fn modal(&self) -> Option<Key> {
        self.modal.map(|(_, _, key)| key)
    }

    /// Whether node `i` takes input: everything does, until a modal is in
    /// effect — then its subtree does, and so does window chrome (the
    /// window's own controls belong to the platform, not to the dialog).
    pub(crate) fn interactive(&self, i: usize) -> bool {
        match self.modal {
            Some((start, end, _)) => {
                (start..end).contains(&i) || self.tree.specs[i].window.is_some()
            }
            None => true,
        }
    }

    /// Whether node `key` is inside the frame's modal scope (nothing is
    /// outside one when there is no modal).
    pub(crate) fn within_modal(&self, key: Key) -> bool {
        let Some((start, end, _)) = self.modal else {
            return true;
        };
        self.tree.keys[start..end].contains(&key)
    }

    /// Focus follows the modal: a modal that appears remembers the focus
    /// it displaces and takes focus into itself; one that stops being
    /// declared gives that focus back. Run once per frame, after the
    /// scope is known.
    pub(crate) fn resolve_modal_focus(&mut self) {
        // This frame's modals in tree order, each carrying the focus it
        // displaced when it first appeared.
        let mut now: Vec<(Key, Option<Key>)> = Vec::new();
        if self.tree.any_modal {
            for i in 0..self.tree.len() {
                if self.tree.specs[i].events().modal.is_none() {
                    continue;
                }
                let key = self.tree.keys[i];
                let saved = self
                    .modal_focus
                    .iter()
                    .find(|(k, _)| *k == key)
                    .map_or(self.focus, |(_, f)| *f);
                now.push((key, saved));
            }
        }
        // The outermost modal that went away hands its focus back, so a
        // dialog and the confirm inside it closing together land where
        // the dialog was opened from.
        let closed = self
            .modal_focus
            .iter()
            .find(|(k, _)| !now.iter().any(|(n, _)| n == k))
            .map(|(_, saved)| saved.filter(|key| self.tree.keys.contains(key)));
        // A `keyFocus` *edge* on this same frame — a node declared focused
        // now and not last frame — is the app saying where focus lands on
        // the way out, and it wins over the restore (ADR 0003, decision
        // 4): a rename editor opened on a node created in the same frame
        // displaced the node the user was on *before*, and the restore
        // is the default for an app that says nothing, not a rule for one
        // that did. A sink redeclaring itself every frame is no edge, so
        // the restore still lands where it always has under one.
        // The same for an imperative move since the last frame — a
        // handler closing the dialog and naming where focus lands
        // (AR17): three doors, one precedence.
        let edge = self
            .declared_focus
            .iter()
            .any(|k| !self.declared_focus_last.contains(k))
            || self.focus_asked;
        if let Some(saved) = closed
            && !edge
        {
            // Exactly what it displaced, nothing included: leaving focus
            // on the dismissed modal's own button would be a focus on a
            // node that is not there any more.
            self.move_focus(saved);
        }
        self.modal_focus = now;
        // Containment: focus outside the scope enters it, or is dropped
        // when it holds none. Not `focus_visible`: the app showed the
        // modal, nobody pressed a key. The entry is the first node in the
        // ring declaring `initial_focus` — a destructive confirm opening
        // on its Cancel — and the ring's first when none does. Only
        // *entry* reads it: focus already inside the scope never reaches
        // here, so a Tab press stands and a nested confirm closing leaves
        // focus where it handed it back.
        if self.modal.is_some() && !self.focus.is_some_and(|k| self.within_modal(k)) {
            let ring = self.focus_ring();
            let entry = self.ring_entry(&ring);
            self.move_focus(entry);
        }
        // Read: the stamp covers the moves since the last frame's end —
        // a handler's between frames and the view's during the build —
        // and a redraw of the same tree starts clean.
        self.focus_asked = false;
    }

    /// Whether a primary press on node `key` leaves the keyboard alone:
    /// `keepFocus` on the node or an ancestor. Such a press
    /// acts and touches nothing the keyboard has — not the focus, not the
    /// ring Tab walks next (a press settling the region is a press
    /// *taking* the keyboard), and not the selection, which is what a
    /// toolbar's Copy or Bold acts on. One walk per press.
    pub(crate) fn keeps_focus(&self, key: Key) -> bool {
        let Some(i) = self.tree.index_of(key) else {
            return false;
        };
        let mut n = i as u32;
        while n != crate::tree::NIL {
            if self.tree.specs[n as usize].events().keep_focus {
                return true;
            }
            n = self.tree.parent[n as usize];
        }
        false
    }

    /// Where a primary press on node `key` puts the keyboard: the node
    /// itself when it is focusable, and nothing when it is not — except
    /// on window chrome, which leaves focus alone, and inside a key sink,
    /// which keeps the keyboard through any press landing in its subtree.
    ///
    /// A sink is an app that owns its keyboard (the same reason Tab stays
    /// with it). The panes, buttons and dead
    /// space it draws are that app's surface, and a derived button among
    /// them is focusable enough to be a Tab stop without being entitled to
    /// take the keys away from the app drawing it — otherwise the first
    /// click anywhere in a multiplexer kills every chord, permanently,
    /// because `take_key_focus` is edge-triggered and will not ask twice.
    /// An editor or a nested sink is its own keyboard owner and still
    /// takes focus (the editor through the caret arm above). A press on a
    /// `keepFocus` node never gets here: see [`Self::keeps_focus`].
    pub(crate) fn press_focus(&self, key: Key, focusable: bool) -> Option<Key> {
        let Some(i) = self.tree.index_of(key) else {
            return focusable.then_some(key);
        };
        // Window chrome belongs to the platform, not to the app (decision
        // 2, the same reason it is never a Tab stop): dragging a window by
        // its titlebar, or pressing minimize, is not the app being asked
        // to give up its keyboard.
        if self.tree.specs[i].window.is_some() {
            return self.focus;
        }
        if self.tree.specs[i].events().on_key.is_some() {
            return Some(key);
        }
        match self.enclosing_sink(i) {
            Some(j) => Some(self.tree.keys[j]),
            None => focusable.then_some(key),
        }
    }

    /// The nearest key sink strictly above node `i`: the walk
    /// [`Self::press_focus`] makes for a press, made for a key as well.
    ///
    /// A disabled node is not a sink at all,
    /// so it neither answers here nor hides a live sink further up. The
    /// walk stops at the modal boundary rather than climbing through it:
    /// the app around a dialog is inert, and a shell that
    /// kept hearing shortcuts while its own dialog was up would be running
    /// commands against a surface the user cannot see the state of.
    pub(crate) fn enclosing_sink(&self, i: usize) -> Option<usize> {
        let mut n = self.tree.parent[i];
        while n != crate::tree::NIL {
            let j = n as usize;
            if !self.interactive(j) {
                return None;
            }
            if self.tree.specs[j].events().on_key.is_some() && !self.tree.specs[j].disabled {
                return Some(j);
            }
            n = self.tree.parent[j];
        }
        None
    }

    /// The node whose context menu a secondary press on node `i` opens:
    /// `i` itself when it declares a live `on_context_menu`, else the
    /// nearest enclosing node that does — the same walk and the same
    /// modal boundary as `enclosing_sink`, because an unclaimed press is
    /// unclaimed the way an unclaimed key is. A disabled
    /// node's own menu is skipped like a disabled sink's, so the press
    /// reaches the container's.
    pub(crate) fn enclosing_menu(&self, i: usize) -> Option<usize> {
        let offers = |j: usize| {
            self.tree.specs[j].events().on_context_menu.is_some() && !self.tree.specs[j].disabled
        };
        if offers(i) {
            return Some(i);
        }
        let mut n = self.tree.parent[i];
        while n != crate::tree::NIL {
            let j = n as usize;
            if !self.interactive(j) {
                return None;
            }
            if offers(j) {
                return Some(j);
            }
            n = self.tree.parent[j];
        }
        None
    }

    /// The node a non-primary press of `button` on node `i` goes to:
    /// `i` itself when its live `on_button` claims the
    /// button, else the nearest enclosing node whose does — the walk
    /// `enclosing_menu` takes, stopping at the modal boundary and skipping
    /// a disabled node's own. For the secondary button a nearer live
    /// `on_context_menu` ends the walk with nothing, the nested
    /// declaration winning over its ancestor's either way; a node that
    /// declares both is the button's, which is what claiming it says.
    pub(crate) fn enclosing_button(&self, i: usize, button: MouseButton) -> Option<usize> {
        let mut n = i as u32;
        while n != crate::tree::NIL {
            let j = n as usize;
            if j != i && !self.interactive(j) {
                return None;
            }
            let spec = &self.tree.specs[j];
            if !spec.disabled {
                let ev = spec.events();
                if ev.on_button.is_some() && ev.buttons.contains(button) {
                    return Some(j);
                }
                if button == MouseButton::Secondary && ev.on_context_menu.is_some() {
                    return None;
                }
            }
            n = self.tree.parent[j];
        }
        None
    }

    /// The zone node `i` belongs to: itself when it declares `on_drop`,
    /// else the nearest enclosing declaration —
    /// the same walk as `enclosing_menu`, stopping at the modal boundary
    /// and skipping a disabled node's own.
    pub(crate) fn enclosing_drop(&self, i: usize) -> Option<usize> {
        let declares = |j: usize| {
            self.tree.specs[j].events().on_drop.is_some() && !self.tree.specs[j].disabled
        };
        if declares(i) {
            return Some(i);
        }
        let mut n = self.tree.parent[i];
        while n != crate::tree::NIL {
            let j = n as usize;
            if !self.interactive(j) {
                return None;
            }
            if declares(j) {
                return Some(j);
            }
            n = self.tree.parent[j];
        }
        None
    }

    /// The focused node's index in the last frame, if it is there.
    pub(crate) fn focus_index(&self) -> Option<usize> {
        self.tree.index_of(self.focus?)
    }

    /// Whether `key` holds keyboard focus — any node (see `focus`).
    pub fn is_focused(&self, key: Key) -> bool {
        self.focus == Some(key)
    }

    /// The node holding keyboard focus: an editor, an `on_key` sink, or
    /// a control Tab (or assistive technology, or `set_focus`) put it on.
    pub fn focus(&self) -> Option<Key> {
        self.focus
    }

    /// Whether focus got where it is by keyboard or assistive technology
    /// rather than a click — when it shows (the default ring, or the
    /// node's `focus_bg`).
    pub fn focus_visible(&self) -> bool {
        self.focus_visible
    }

    /// Moves keyboard focus (None blurs) — the app's door: `Ui::focus`,
    /// Node's `focus`, `kui_focus`, Lua's `env.set_focus`. Any node can be
    /// focused this way; only focusable ones (see `access::focusable`)
    /// are reached by Tab. A move made here is the app saying where focus
    /// goes, and it stands at the frame's end against a closing modal's
    /// restore, the way a `keyFocus` edge does: the restore is the default for an app that said
    /// nothing, and this is an app that did. The core's own moves — a
    /// press, a Tab, an autofocus, the restore itself — go through
    /// `move_focus` and say nothing.
    pub fn set_focus(&mut self, key: Option<Key>) {
        if self.focus != key {
            self.focus_asked = true;
        }
        self.move_focus(key);
    }

    /// Emits `focus` events for the `on_focus` nodes the focus left and
    /// entered since the last report, `by` what moved it: the input being
    /// handled, or `"program"` at a frame's end. A node
    /// hears focus anywhere in its subtree; leaving is reported innermost
    /// first, entering outermost first, as the focus crosses them.
    pub(crate) fn report_focus(&mut self, by: &'static str, out: &mut Vec<UiEvent>) {
        // The `on_focus` nodes on the focused node's path, outermost first,
        // by index: the tags are cloned only when the set changed, so a
        // frame where the focus stayed put costs the walk and nothing else.
        let mut path = Vec::new();
        if let Some(mut i) = self.focus.and_then(|k| self.tree.index_of(k)) {
            loop {
                if self.tree.specs[i].events().on_focus.is_some() {
                    path.push(i);
                }
                let p = self.tree.parent[i];
                if p == crate::tree::NIL {
                    break;
                }
                i = p as usize;
            }
            path.reverse();
        }
        if path.len() == self.focus_reported.len()
            && path
                .iter()
                .zip(&self.focus_reported)
                .all(|(&i, r)| self.tree.keys[i] == r.0)
        {
            return;
        }
        let now: Vec<_> = path
            .iter()
            .map(|&i| {
                let tag = self.tree.specs[i]
                    .events()
                    .on_focus
                    .clone()
                    .unwrap_or_default();
                (self.tree.keys[i], self.tree.origins[i], tag)
            })
            .collect();
        let event = |(key, origin, tag): &(Key, crate::tree::OriginId, Value), phase: &str| {
            let payload = Value::map([
                ("kind", Value::str("focus")),
                ("phase", Value::str(phase)),
                ("by", Value::str(by)),
            ]);
            UiEvent::on(*origin, *key, payload).tagged(Some(tag))
        };
        for r in self.focus_reported.iter().rev() {
            if !now.iter().any(|n| n.0 == r.0) {
                out.push(event(r, "out"));
            }
        }
        for n in &now {
            if !self.focus_reported.iter().any(|r| r.0 == n.0) {
                out.push(event(n, "in"));
            }
        }
        self.focus_reported = now;
    }

    /// The one writer of `focus`: the edit store mirrors it for editor
    /// keys, a landing editor scrolls its caret into view, the region
    /// follows.
    pub(crate) fn move_focus(&mut self, key: Option<Key>) {
        let edit_key = key.filter(|k| self.edit.contains(*k));
        if self.focus != key {
            // The keys the leaving sink is holding come up first, while it
            // is still the focus that routing resolves against.
            self.release_held_keys();
            if let Some(k) = edit_key {
                self.edit.caret_moved = Some(k);
            }
        }
        // The region follows focus (`docs/adr/0022`, decision 3) — when
        // the key is in the tree; a build declaring focus on a node it has
        // not pushed yet is caught up by `resolve_regions`. The region
        // being left remembers what it held first, so entering it again
        // lands there.
        if key != self.focus {
            self.region_held = false;
        }
        if self.tree.any_region
            && let Some(k) = key
            && let Some(i) = self.tree.index_of(k)
        {
            let region = self.region_of(i);
            if region != self.region {
                self.remember_region_focus();
                self.region = region;
            }
        }
        self.focus = key;
        self.edit.set_focus(edit_key);
    }

    /// Asks for a Tab step (`forward`) / Shift-Tab step at the end of the
    /// frame being built. `focus_next` moves focus now, against the last
    /// finished tree — which is what a driver handling a key press between
    /// frames wants, and exactly what a *view* cannot use, since its own
    /// tree does not exist yet. A view asks with this instead and the step
    /// lands on the frame it is declaring.
    #[track_caller]
    pub fn request_focus_step(&mut self, forward: bool) {
        self.pending_focus_step = Some(forward);
        self.owe_frame("request_focus_step");
    }

    /// Declares a node focused this frame (None blurs at once). The
    /// declaration is edge-triggered: the node takes focus on the first
    /// frame it is declared and keeps being declared without effect
    /// afterwards, so a view that repeats it every frame (an app that
    /// owns its keyboard, a `keyFocus` prop) does not clobber the focus a
    /// Tab press or a click moved. Programmatic focus keeps the modality
    /// of the last input (it shows after keyboard use, not after a
    /// click). To move focus at any time, `set_focus`.
    pub fn set_key_focus(&mut self, key: Option<Key>) {
        let Some(k) = key else {
            self.move_focus(None);
            return;
        };
        if !self.declared_focus.contains(&k) {
            self.declared_focus.push(k);
        }
        if !self.declared_focus_last.contains(&k) {
            self.move_focus(Some(k));
        }
    }

    /// The node holding keyboard focus (the same as `focus`; kept from
    /// when only key sinks and editors could).
    pub fn key_focus(&self) -> Option<Key> {
        self.focus
    }

    /// The keys that declared key focus while the current frame was built
    /// (`set_key_focus`). The scene corpus's coverage derivation reads it:
    /// `keyFocus` leaves no mark on the tree, and the focus it takes is
    /// indistinguishable from the focus a click takes.
    #[cfg(feature = "conformance")]
    pub(crate) fn declared_focus(&self) -> &[Key] {
        &self.declared_focus
    }
}