Skip to main content

smudge_core/
tree.rs

1//! The retained widget tree.
2//!
3//! The tree stores one node per widget, keyed by a generational
4//! [`WidgetId`] that stays valid for the node's lifetime. It keeps its own
5//! parent and child links in step with the layout engine's tree, gives every
6//! node a reactive owner, and records how much of each node is out of date.
7//!
8//! The tree is the only owner of widget state. Effects never hold a reference
9//! to it: a binding pushes an update onto a queue and the frame drains that
10//! queue, so every mutation of the tree happens in one place and in one order.
11
12use std::any::Any;
13use std::collections::{HashMap, HashSet};
14use std::rc::Rc;
15
16use slotmap::{Key, SlotMap, new_key_type};
17
18use crate::geometry::{Point, Rect, Size};
19use crate::paint::PaintOp;
20use crate::reactive::{Owner, RwSignal};
21use crate::semantics::{A11yChanges, Role, SemanticsNode};
22use crate::style::{LayoutProps, PaintProps, Style};
23use crate::theme::Theme;
24use crate::variants::{Variants, resolve};
25use crate::widget::Widget;
26
27#[path = "display_list_pass.rs"]
28mod display_list_pass;
29#[path = "focus.rs"]
30pub(crate) mod focus;
31#[path = "interaction.rs"]
32pub(crate) mod interaction;
33#[path = "layout.rs"]
34mod layout;
35#[path = "paint_pass.rs"]
36mod paint_pass;
37
38use self::interaction::{Handlers, Interaction};
39
40new_key_type! {
41    /// Identifies one node in the widget tree.
42    ///
43    /// The id stays valid for as long as the node lives and is never reused for
44    /// a different node, so an id whose node was removed refers to nothing.
45    /// Every operation that takes one reports that rather than acting on
46    /// another node.
47    pub struct WidgetId;
48}
49
50impl WidgetId {
51    /// Returns the raw representation of the id.
52    ///
53    /// An accessibility backend identifies its nodes by `u64`, so the
54    /// projection maps ids through this conversion. The raw value of two
55    /// live ids is always different, and a value this tree never handed out
56    /// restores to an id that names no node.
57    pub fn as_u64(self) -> u64 {
58        self.data().as_ffi()
59    }
60
61    /// Restores the id that [`WidgetId::as_u64`] encoded.
62    ///
63    /// An id whose node was removed already, or one that names no node at
64    /// all, is a no-op everywhere the tree takes it.
65    pub fn from_u64(raw: u64) -> WidgetId {
66        WidgetId(slotmap::KeyData::from_ffi(raw))
67    }
68}
69
70/// How much of a node is out of date.
71///
72/// The variants are ordered, and [`Dirty::Layout`] implies [`Dirty::Paint`]. A
73/// node's flag only ever rises within a frame.
74#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
75pub enum Dirty {
76    /// Nothing about the node changed since the last frame.
77    Clean,
78    /// The node's paint output must be regenerated. Its layout is current.
79    Paint,
80    /// The node must be laid out again, which repaints it as well.
81    Layout,
82}
83
84/// The bounds a node painted in the last frame, and the clip it painted under.
85///
86/// The bounds are the union of the node's paint operation bounds before
87/// clipping, in the paint coordinates the node painted at, which are its
88/// window coordinates translated by `shift`. The clip is kept beside them,
89/// because damage converts the two to physical pixels separately: a pixel
90/// that a clip edge only partly covers is still drawn, and clipping the
91/// bounds in logical space first would miss it.
92#[derive(Copy, Clone, Debug, PartialEq)]
93struct PaintedBounds {
94    /// The union of the node's paint operation bounds, before clipping.
95    bounds: Rect,
96    /// The rectangle the node's subtree was clipped to, in window coordinates.
97    clip: Rect,
98    /// The paint shift the bounds are relative to, so damage can turn them
99    /// back into window coordinates.
100    shift: Point,
101}
102
103impl PaintedBounds {
104    /// Returns the bounds in window coordinates.
105    fn window_bounds(&self) -> Rect {
106        self.bounds.translated(Point {
107            x: -self.shift.x,
108            y: -self.shift.y,
109        })
110    }
111}
112
113/// One widget and everything the frame needs to know about it.
114struct Node {
115    /// The widget this node owns.
116    widget: Box<dyn Widget>,
117    /// The node this one is a child of, or `None` for the root.
118    parent: Option<WidgetId>,
119    /// The node's children, in paint order.
120    children: Vec<WidgetId>,
121    /// The node's base style, written by the element's builders and their
122    /// bindings.
123    base: Style,
124    /// The style layout and paint read, with the patches of the active
125    /// interaction states applied over the base style.
126    resolved: Style,
127    /// The style patches per interaction state, when the element declared
128    /// any. Without patches the resolved style tracks the base style.
129    variants: Option<Rc<Variants>>,
130    /// Whether the widget's last paint resolved a value from the theme, so a
131    /// theme switch repaints the node.
132    uses_tokens: bool,
133    /// The node's id in the layout engine's tree.
134    taffy: taffy::NodeId,
135    /// The reactive owner that every binding of this node runs under.
136    ///
137    /// Cleaning the owner up stops the node's bindings, so removal cleans it
138    /// up and nothing else has to track binding handles.
139    owner: Owner,
140    /// How much of the node is out of date.
141    dirty: Dirty,
142    /// The node's absolute rectangle, set by the walk that computes absolute
143    /// rectangles, which the layout pass and a scroll offset change run.
144    rect: Rect,
145    /// The node's content box: its rectangle inside the border and the
146    /// padding, set by the same walk.
147    content: Rect,
148    /// The translation from the node's window coordinates to its paint
149    /// coordinates, which is the sum of the scroll offsets that move it.
150    ///
151    /// The walk that computes absolute rectangles maintains it: a flowing
152    /// child adds the scroll offset of its parent to the parent's shift, and
153    /// an absolutely positioned child inherits the parent's shift. The paint
154    /// pass stores operations and painted bounds in paint coordinates, which
155    /// lets a scroll move content by changing offsets alone: the display list
156    /// wraps the moved children in an offset scope instead of repainting
157    /// them, and damage translates the bounds back with the shift.
158    paint_shift: Point,
159    /// The node's rectangle in paint coordinates, `rect` translated by
160    /// `paint_shift`.
161    ///
162    /// The walk computes it from the layout without the scroll offsets, so a
163    /// scroll leaves it exactly as it was and the walk can tell a node that
164    /// only scrolled from one that has to paint again.
165    paint_rect: Rect,
166    /// The node's content box in paint coordinates, `content` translated by
167    /// `paint_shift`.
168    paint_content: Rect,
169    /// The rectangle the node's subtree is clipped to, set by the same walk.
170    clip: Rect,
171    /// The translation applied to the node's flowing children after layout,
172    /// outside the layout engine. It is how a scroll view moves its content:
173    /// the walk that computes absolute rectangles adds it to the paint shift
174    /// of every child that stays in the flow, which moves the child's window
175    /// rectangle by minus the offset, and [`WidgetTree::set_scroll_offset`]
176    /// re-runs that walk for the subtree alone. An absolutely positioned
177    /// child is left where its insets place it.
178    scroll_offset: Point,
179    /// The signal that pins the node's scroll offset to the end of its
180    /// flowing content while it reads `true`, set by the element's builders.
181    /// While it does, [`WidgetTree::layout`] pins the offset after each pass;
182    /// see the resolution in the layout pass module.
183    follow: Option<RwSignal<bool>>,
184    /// The flowing child the node keeps in place, by index, and how far
185    /// below the top of its content box, set by the element's builders.
186    /// [`WidgetTree::layout`] applies it after each pass.
187    pin: Option<(usize, f32)>,
188    /// The node's interaction state: hovered, pressed, disabled and focus.
189    interaction: Interaction,
190    /// The cursor the node's element set for the pointer over it, or `None`
191    /// when the node leaves the choice to an ancestor or the platform.
192    cursor: Option<crate::window::Cursor>,
193    /// The input handlers the node owns, set by its element's builders.
194    handlers: Handlers,
195    /// The role the element's builders set for the accessibility projection,
196    /// which overrides the role the widget reports.
197    a11y_role: Option<Role>,
198    /// The accessible label the element's builders set, which overrides the
199    /// label the widget reports.
200    a11y_label: Option<String>,
201    /// The node's cached paint output, filled by the paint pass.
202    ops: Vec<PaintOp>,
203    /// The bounds the node painted last frame and the clip it painted under.
204    painted: Option<PaintedBounds>,
205}
206
207/// Returns whether two child lists that hold the same nodes in a different
208/// relative order restack any pair of them.
209///
210/// Children one list gained or lost are skipped: a mount and a removal move
211/// nothing that stayed, so their damage comes from the nodes themselves. Two
212/// survivors that traded places can overlap, and no rectangle or dirty flag
213/// announces the pixels they now share in the other order.
214fn children_differ_in_order(before: &[WidgetId], after: &[WidgetId]) -> bool {
215    let kept_after: HashSet<WidgetId> = after.iter().copied().collect();
216    let kept_before: HashSet<WidgetId> = before.iter().copied().collect();
217    let survivors_of_before: Vec<WidgetId> = before
218        .iter()
219        .filter(|id| kept_after.contains(id))
220        .copied()
221        .collect();
222    let survivors_of_after: Vec<WidgetId> = after
223        .iter()
224        .filter(|id| kept_before.contains(id))
225        .copied()
226        .collect();
227    survivors_of_before != survivors_of_after
228}
229
230/// How many pending accessibility changes the tree queues before the next
231/// projection rebuilds from scratch instead.
232const A11Y_DIRTY_CAP: usize = 4096;
233
234/// A mounted focus trap, and the focus to restore when it ends.
235#[derive(Clone, Copy)]
236struct FocusTrap {
237    /// The node whose subtree keeps the keyboard.
238    node: WidgetId,
239    /// The node that held the focus when the trap last became able to hold
240    /// it, by mounting visible or by showing again.
241    restore: Option<WidgetId>,
242    /// Whether the trap could hold the focus when it was last looked at, so
243    /// a change between showing and hiding is seen once.
244    active: bool,
245}
246
247/// The retained tree of widgets.
248///
249/// A tree has at most one root. Removing the root leaves the tree empty.
250pub(crate) struct WidgetTree {
251    /// The nodes, keyed by the id the tree handed out.
252    nodes: SlotMap<WidgetId, Node>,
253    /// The layout engine's tree, holding one node per widget node.
254    taffy: taffy::TaffyTree<WidgetId>,
255    /// The root node, when the tree has one.
256    root: Option<WidgetId>,
257    /// Nodes that need painting, in the order they were first marked.
258    paint_queue: Vec<WidgetId>,
259    /// Whether any node needs a new layout pass.
260    layout_dirty: bool,
261    /// The painted bounds the next frame must repaint: the nodes removed
262    /// since the last frame, and the subtrees a child reorder restacked.
263    pending_damage: Vec<PaintedBounds>,
264    /// The viewport the last layout pass ran for, or `None` before the first.
265    viewport: Option<Size>,
266    /// The scale the next layout pass measures at. A widget that sizes itself
267    /// from pixel data measures against it.
268    scale: f32,
269    /// How many times the layout engine has run for this tree.
270    layout_runs: u64,
271    /// The deepest node the pointer is over, or `None` when it is over no
272    /// node that can respond.
273    hovered: Option<WidgetId>,
274    /// The pointer position the last pointer event reported, in logical
275    /// pixels, so the hovered chain can be recomputed when the tree moves
276    /// under a pointer that stays still.
277    pointer: Point,
278    /// Whether the pointer is inside the window, so a layout pass only
279    /// recomputes hover from [`WidgetTree::pointer`] while it is.
280    pointer_inside: bool,
281    /// The node a primary press captured, while the button is held.
282    pressed: Option<WidgetId>,
283    /// Whether a press was released without its release, so a primary
284    /// release that arrives with no press held belongs to that press and
285    /// reaches no handler. The next primary press clears it.
286    released_early: bool,
287    /// The node that holds the keyboard focus, when one does.
288    focused: Option<WidgetId>,
289    /// The node the focus fell off of because it became disabled or hidden.
290    ///
291    /// The next key event still bubbles from that node, so a shortcut above a
292    /// disabled control keeps working. Any focus change consumes the anchor,
293    /// and so does any press that is not outside an active focus trap.
294    stale_focus: Option<WidgetId>,
295    /// Whether the focus last moved by keyboard navigation, so a focus ring
296    /// can draw for keyboard users alone. A Tab or an arrow key turns this on
297    /// and a pointer press turns it off.
298    keyboard_nav: bool,
299    /// The autofocus requests that mounted nodes queued and no layout pass
300    /// has resolved yet.
301    pending_autofocus: Vec<WidgetId>,
302    /// The mounted focus traps, oldest first.
303    ///
304    /// The last mounted trap wins, and removing a node ends every trap it
305    /// holds, which restores the one mounted before it and the focus held
306    /// when the oldest removed trap mounted.
307    focus_traps: Vec<FocusTrap>,
308    /// The nodes that carry a follow signal, so the follow resolution after
309    /// a layout pass visits them alone instead of every node. A removed node
310    /// leaves a stale id here until the next resolution drops it.
311    following: Vec<WidgetId>,
312    /// The nodes that pin a child, so the pin resolution after a layout
313    /// pass visits them alone. A removed node leaves a stale id here until
314    /// the next resolution drops it.
315    pinned: Vec<WidgetId>,
316    /// The theme that paint-time token resolution reads.
317    theme: Theme,
318    /// The nodes whose projected accessibility state may have changed, in the
319    /// order the changes arrived.
320    ///
321    /// Duplicates are allowed and removed when the changes are taken. When
322    /// the list outgrows [`A11Y_DIRTY_CAP`], it is cleared and
323    /// [`WidgetTree::a11y_reset`] is raised, which makes the next projection
324    /// rebuild from scratch instead.
325    a11y_dirty: Vec<WidgetId>,
326    /// Whether the next projection must be rebuilt from scratch.
327    a11y_reset: bool,
328}
329
330impl WidgetTree {
331    /// Creates an empty tree.
332    pub(crate) fn new() -> WidgetTree {
333        WidgetTree {
334            nodes: SlotMap::with_key(),
335            taffy: taffy::TaffyTree::new(),
336            root: None,
337            paint_queue: Vec::new(),
338            layout_dirty: false,
339            pending_damage: Vec::new(),
340            viewport: None,
341            scale: 1.0,
342            layout_runs: 0,
343            hovered: None,
344            pointer: Point::default(),
345            pointer_inside: false,
346            pressed: None,
347            released_early: false,
348            focused: None,
349            stale_focus: None,
350            keyboard_nav: false,
351            pending_autofocus: Vec::new(),
352            focus_traps: Vec::new(),
353            following: Vec::new(),
354            pinned: Vec::new(),
355            theme: Theme::light(),
356            a11y_dirty: Vec::new(),
357            a11y_reset: false,
358        }
359    }
360
361    /// Adds a node for `widget` under `parent`, or as the root when `parent` is
362    /// `None`, and returns the new node's id.
363    ///
364    /// The node starts [`Dirty::Layout`], because it has never been laid out.
365    ///
366    /// # Panics
367    ///
368    /// Panics when `parent` is not a live id, or when `parent` is `None` and
369    /// the tree already has a root. Both are internal invariants: callers pass
370    /// ids that this tree handed out.
371    pub(crate) fn insert(
372        &mut self,
373        parent: Option<WidgetId>,
374        widget: Box<dyn Widget>,
375        style: Style,
376    ) -> WidgetId {
377        match parent {
378            Some(parent) => assert!(
379                self.nodes.contains_key(parent),
380                "the parent id is a live node"
381            ),
382            None => assert!(
383                self.root.is_none(),
384                "a tree has one root, and this one already has a root node"
385            ),
386        }
387
388        let taffy = self
389            .taffy
390            .new_leaf(style.layout.to_taffy())
391            .expect("the layout engine accepts a new leaf node");
392        let owner = match parent {
393            Some(parent) => self
394                .nodes
395                .get(parent)
396                .expect("the parent id is a live node")
397                .owner
398                .child(),
399            None => Owner::new(),
400        };
401        let node = Node {
402            widget,
403            parent,
404            children: Vec::new(),
405            resolved: style.clone(),
406            base: style,
407            variants: None,
408            uses_tokens: false,
409            taffy,
410            owner,
411            dirty: Dirty::Clean,
412            rect: Rect::default(),
413            content: Rect::default(),
414            paint_shift: Point::default(),
415            paint_rect: Rect::default(),
416            paint_content: Rect::default(),
417            clip: Rect::default(),
418            scroll_offset: Point::default(),
419            follow: None,
420            pin: None,
421            interaction: Interaction::default(),
422            cursor: None,
423            handlers: Handlers::default(),
424            a11y_role: None,
425            a11y_label: None,
426            ops: Vec::new(),
427            painted: None,
428        };
429        let id = self.nodes.insert(node);
430        self.taffy
431            .set_node_context(taffy, Some(id))
432            .expect("the new layout node is live");
433
434        match parent {
435            Some(parent) => {
436                let parent_node = self.nodes.get_mut(parent).expect("the parent id is live");
437                parent_node.children.push(id);
438                let parent_taffy = parent_node.taffy;
439                self.taffy
440                    .add_child(parent_taffy, taffy)
441                    .expect("both layout nodes are live");
442            }
443            None => self.root = Some(id),
444        }
445
446        self.mark(id, Dirty::Layout);
447        if let Some(parent) = parent {
448            self.push_a11y(parent);
449        }
450        id
451    }
452
453    /// Removes the node `id` and every node below it.
454    ///
455    /// Each removed node cleans its reactive owner up, which stops its
456    /// bindings, and contributes its last painted bounds to the removed
457    /// damage. The node is detached from its parent, which is marked for
458    /// layout. A removed node loses the focus and the press capture. A stale
459    /// id does nothing.
460    pub(crate) fn remove(&mut self, id: WidgetId) {
461        let Some(node) = self.nodes.get(id) else {
462            return;
463        };
464        let parent = node.parent;
465        if self.root == Some(id) {
466            self.root = None;
467        }
468        if let Some(parent) = parent
469            && let Some(parent_node) = self.nodes.get_mut(parent)
470        {
471            parent_node.children.retain(|child| *child != id);
472        }
473
474        let mut order = Vec::new();
475        let mut stack = vec![id];
476        while let Some(current) = stack.pop() {
477            order.push(current);
478            if let Some(node) = self.nodes.get(current) {
479                stack.extend(node.children.iter().copied());
480            }
481        }
482        if self.focused.is_some_and(|focused| order.contains(&focused)) {
483            self.focused = None;
484        }
485        if self.stale_focus.is_some_and(|stale| order.contains(&stale)) {
486            self.stale_focus = None;
487        }
488        // A drag whose node is gone has nothing left to capture, and hover
489        // only follows the pointer again once no press is held.
490        if self.pressed.is_some_and(|pressed| order.contains(&pressed)) {
491            self.pressed = None;
492        }
493        // A removed node keeps neither its autofocus request nor its trap:
494        // the request is dropped, and the trap mounted before it, if any,
495        // takes over. The focus goes back to where it was when the oldest
496        // removed active trap became active, since every later one became
497        // active inside it. A hidden trap already gave its focus back.
498        self.pending_autofocus.retain(|id| !order.contains(id));
499        let restore = self
500            .focus_traps
501            .iter()
502            .find(|trap| trap.active && order.contains(&trap.node))
503            .and_then(|trap| trap.restore)
504            .filter(|restore| !order.contains(restore));
505        self.focus_traps.retain(|trap| !order.contains(&trap.node));
506
507        // Deepest node first, so every taffy node is still attached to a live
508        // parent when it is removed, and so an owner's bindings stop before
509        // the owners above it dispose the signals they read.
510        let mut removed_taffy = Vec::new();
511        for current in order.into_iter().rev() {
512            let Some(node) = self.nodes.get_mut(current) else {
513                continue;
514            };
515            let owner = node.owner.clone();
516            let painted = node.painted.take();
517            let taffy = node.taffy;
518            owner.cleanup();
519            if let Some(painted) = painted {
520                self.pending_damage.push(painted);
521            }
522            self.taffy
523                .remove(taffy)
524                .expect("every widget node has a live layout node");
525            self.nodes.remove(current);
526            removed_taffy.push(taffy);
527        }
528
529        if let Some(parent) = parent {
530            self.mark(parent, Dirty::Layout);
531            self.push_a11y(parent);
532        }
533        self.drop_stale_hoisted_children(parent, &removed_taffy);
534        if self.focused.is_none()
535            && let Some(restore) = restore
536        {
537            self.request_focus(restore);
538        }
539    }
540
541    /// Recreates the layout node of every ancestor, starting at `parent`,
542    /// whose hoisted child list still names one of `removed_taffy`.
543    ///
544    /// The layout engine records an absolutely positioned node in the hoisted
545    /// child list of its containing block, and clears that list only when the
546    /// block's own layout runs again. A block that lost its last child, or one
547    /// that is hidden, runs no layout of its own, so it keeps the stale id and
548    /// the next layout pass reads it and panics. A containing block is always
549    /// an ancestor, so only the removed node's ancestors need checking.
550    fn drop_stale_hoisted_children(
551        &mut self,
552        parent: Option<WidgetId>,
553        removed_taffy: &[taffy::NodeId],
554    ) {
555        let mut ancestor = parent;
556        while let Some(id) = ancestor {
557            let Some(node) = self.nodes.get(id) else {
558                break;
559            };
560            ancestor = node.parent;
561            let stale = self
562                .taffy
563                .hoisted_children(node.taffy)
564                .expect("every widget node has a live layout node")
565                .iter()
566                .any(|hoisted| removed_taffy.contains(hoisted));
567            if stale {
568                self.recreate_layout_node(id);
569            }
570        }
571    }
572
573    /// Replaces the layout node of `id` with a fresh one, which carries an
574    /// empty hoisted child list that the next layout pass rebuilds.
575    ///
576    /// The fresh node takes the old node's place among its parent's layout
577    /// children, because the engine only lays out nodes it reaches from the
578    /// root, and an unattached node would keep the default empty layout.
579    fn recreate_layout_node(&mut self, id: WidgetId) {
580        let node = self.nodes.get(id).expect("the node is live");
581        let style = node.resolved.layout.to_taffy();
582        let old = node.taffy;
583        let parent = self.taffy.parent(old);
584        let index = parent.and_then(|parent| {
585            self.taffy
586                .children(parent)
587                .expect("the parent is a live layout node")
588                .iter()
589                .position(|child| *child == old)
590        });
591        let children = self
592            .taffy
593            .children(old)
594            .expect("every widget node has a live layout node");
595        self.taffy
596            .remove(old)
597            .expect("every widget node has a live layout node");
598        let taffy = self
599            .taffy
600            .new_leaf(style)
601            .expect("the layout engine accepts a new leaf node");
602        self.taffy
603            .set_node_context(taffy, Some(id))
604            .expect("the new layout node is live");
605        for child in children {
606            self.taffy
607                .add_child(taffy, child)
608                .expect("the reattached children are live layout nodes");
609        }
610        if let Some(parent) = parent {
611            match index {
612                Some(index) => self
613                    .taffy
614                    .insert_child_at_index(parent, index, taffy)
615                    .expect("the index fits the parent's child list"),
616                None => self
617                    .taffy
618                    .add_child(parent, taffy)
619                    .expect("both layout nodes are live"),
620            }
621        }
622        self.nodes.get_mut(id).expect("the node is live").taffy = taffy;
623        self.mark(id, Dirty::Layout);
624    }
625
626    /// Reorders the children of `id` to `children` and marks the node for
627    /// layout.
628    ///
629    /// A reorder that changes the order also queues the painted bounds of the
630    /// whole subtree for a repaint, because the reorder restacks the pixels
631    /// the children share without changing any rectangle that would announce
632    /// it. The next frame repaints those bounds from the display list in the
633    /// new order, which puts the new stacking on the screen even when nothing
634    /// below `id` repaints on its own.
635    ///
636    /// # Panics
637    ///
638    /// Panics unless `children` is a permutation of the node's current children.
639    /// Callers reorder children the node already has.
640    pub(crate) fn set_children(&mut self, id: WidgetId, children: Vec<WidgetId>) {
641        let Some(node) = self.nodes.get(id) else {
642            return;
643        };
644        let taffy = node.taffy;
645        let restacked = children_differ_in_order(&node.children, &children);
646
647        let mut reordered_ids = children.clone();
648        reordered_ids.sort_unstable();
649        let mut current = node.children.clone();
650        current.sort_unstable();
651        assert_eq!(
652            reordered_ids, current,
653            "set_children reorders the children the node already has"
654        );
655        for child in &children {
656            assert_eq!(
657                self.nodes.get(*child).and_then(|node| node.parent),
658                Some(id),
659                "every new child is a child of the node"
660            );
661        }
662
663        let taffy_children: Vec<taffy::NodeId> = children
664            .iter()
665            .map(|child| self.nodes[*child].taffy)
666            .collect();
667        self.taffy
668            .set_children(taffy, &taffy_children)
669            .expect("the children are live layout nodes of this parent");
670        self.nodes[id].children = children;
671        if restacked {
672            self.queue_subtree_damage(id);
673        }
674        self.mark(id, Dirty::Layout);
675        self.push_a11y(id);
676    }
677
678    /// Adds the painted bounds of every node below `id`, `id` included, to the
679    /// pending damage.
680    ///
681    /// A child reorder restacks the pixels the reordered subtrees share
682    /// without moving or repainting any node, so no node is marked and no
683    /// rectangle changes. Repainting these bounds from the display list in the
684    /// new child order draws the new stacking.
685    fn queue_subtree_damage(&mut self, id: WidgetId) {
686        let mut stack = vec![id];
687        while let Some(current) = stack.pop() {
688            let Some(node) = self.nodes.get(current) else {
689                continue;
690            };
691            if let Some(painted) = node.painted {
692                self.pending_damage.push(painted);
693            }
694            stack.extend_from_slice(&node.children);
695        }
696    }
697
698    /// Raises the node's dirty flag to `dirty` and queues it for painting the
699    /// first time it leaves [`Dirty::Clean`].
700    ///
701    /// The node is queued for the accessibility projection too when the
702    /// projection could show the change: on a layout change, and on a
703    /// repaint of a node the projection shows.
704    ///
705    /// Marking the node for layout also invalidates the size the layout engine
706    /// cached for it and its ancestors, because a widget can change what it
707    /// measures without any change to its style.
708    ///
709    /// A stale id does nothing.
710    pub(crate) fn mark(&mut self, id: WidgetId, dirty: Dirty) {
711        let Some(node) = self.nodes.get_mut(id) else {
712            return;
713        };
714        if node.dirty >= dirty {
715            return;
716        }
717        let was_clean = node.dirty == Dirty::Clean;
718        let taffy = node.taffy;
719        node.dirty = dirty;
720        // A repaint changes what the projection shows only through the
721        // node's own semantics, so a node the projection leaves out, such as
722        // a blinking caret, queues nothing for it. A layout change can move
723        // the bounds of the projected nodes around it.
724        let projected = dirty == Dirty::Layout || Self::effective_role(node).is_some();
725        if was_clean {
726            self.paint_queue.push(id);
727        }
728        if projected && (was_clean || dirty == Dirty::Layout) {
729            self.push_a11y(id);
730        }
731        if dirty == Dirty::Layout {
732            self.layout_dirty = true;
733            self.taffy
734                .mark_dirty(taffy)
735                .expect("every widget node has a live layout node");
736        }
737    }
738
739    /// Returns the node's resolved style, with the active variant patches
740    /// applied, or `None` when the id is stale.
741    pub(crate) fn style(&self, id: WidgetId) -> Option<&Style> {
742        self.nodes.get(id).map(|node| &node.resolved)
743    }
744
745    /// Sets the style patches the node resolves its style from, and resolves
746    /// the style again.
747    ///
748    /// Called once, while the node is being mounted, before the interaction
749    /// bits are set. A stale id does nothing.
750    pub(crate) fn set_variants(&mut self, id: WidgetId, variants: Option<Rc<Variants>>) {
751        if let Some(node) = self.nodes.get_mut(id) {
752            node.variants = variants;
753        }
754        self.re_resolve(id);
755    }
756
757    /// Applies `apply` to the node's base layout properties, then resolves
758    /// the style again.
759    ///
760    /// The resolved style decides what the change invalidated: when its
761    /// layout properties differ from the ones the last resolution produced,
762    /// the layout engine's style is updated and the node marked for layout;
763    /// otherwise nothing happens when the resolved style did not change, or
764    /// the node is marked for paint when only its paint properties changed. A
765    /// patch can hold a base change back, and then no node is marked at all.
766    /// A stale id does nothing.
767    pub(crate) fn apply_layout(&mut self, id: WidgetId, apply: impl FnOnce(&mut LayoutProps)) {
768        let Some(node) = self.nodes.get_mut(id) else {
769            return;
770        };
771        let mut layout = node.base.layout.clone();
772        apply(&mut layout);
773        if layout == node.base.layout {
774            return;
775        }
776        node.base.layout = layout;
777        self.re_resolve(id);
778    }
779
780    /// Applies `apply` to the node's base paint properties, then resolves the
781    /// style again, as [`WidgetTree::apply_layout`] does for layout
782    /// properties. A stale id does nothing.
783    pub(crate) fn apply_paint(&mut self, id: WidgetId, apply: impl FnOnce(&mut PaintProps)) {
784        let Some(node) = self.nodes.get_mut(id) else {
785            return;
786        };
787        let mut paint = node.base.paint.clone();
788        apply(&mut paint);
789        if paint == node.base.paint {
790            return;
791        }
792        node.base.paint = paint;
793        self.re_resolve(id);
794    }
795
796    /// Recomputes the node's resolved style from its base style, its variant
797    /// patches and its interaction state, and marks the node for what the
798    /// change invalidated.
799    ///
800    /// A change to the resolved layout properties updates the layout engine's
801    /// style and marks the node for layout; a change to the paint properties
802    /// alone marks it for paint. An equal resolved style does nothing, so a
803    /// patch that rewrites a value the resolved style already has repaints
804    /// nothing. A node that the resolution hid loses the keyboard focus at
805    /// once, before the style can draw a focus ring on a hidden node. A stale
806    /// id does nothing.
807    pub(crate) fn re_resolve(&mut self, id: WidgetId) {
808        let Some(node) = self.nodes.get(id) else {
809            return;
810        };
811        let empty = Variants::default();
812        let variants = node.variants.as_deref().unwrap_or(&empty);
813        let resolved = resolve(&node.base, variants, node.interaction);
814        if resolved == node.resolved {
815            return;
816        }
817        let became_hidden = resolved.layout.hidden && !node.resolved.layout.hidden;
818        let became_visible = !resolved.layout.hidden && node.resolved.layout.hidden;
819        let layout_changed = resolved.layout != node.resolved.layout;
820        let taffy_style = resolved.layout.to_taffy();
821        let node = self.nodes.get_mut(id).expect("the node is live");
822        node.resolved = resolved;
823        if layout_changed {
824            let taffy = self.nodes[id].taffy;
825            self.taffy
826                .set_style(taffy, taffy_style)
827                .expect("every widget node has a live layout node");
828            self.mark(id, Dirty::Layout);
829        } else {
830            self.mark(id, Dirty::Paint);
831        }
832        if became_hidden {
833            self.drop_invalid_focus();
834        }
835        if (became_hidden || became_visible)
836            && let Some(parent) = self.nodes.get(id).and_then(|node| node.parent)
837        {
838            self.push_a11y(parent);
839        }
840        if became_visible {
841            self.push_a11y_visible_subtree(id);
842        }
843    }
844
845    /// Replaces the theme and marks every node whose last paint resolved a
846    /// value from the theme for repainting.
847    ///
848    /// Nodes whose paint consults no token paint the same pixels under any
849    /// theme, so they are left alone. An equal theme marks nothing. Which
850    /// nodes consulted the theme is what the paint pass recorded.
851    pub(crate) fn set_theme(&mut self, theme: Theme) {
852        if theme == self.theme {
853            return;
854        }
855        self.theme = theme;
856        let marked: Vec<WidgetId> = self
857            .nodes
858            .iter()
859            .filter(|(_, node)| node.uses_tokens)
860            .map(|(id, _)| id)
861            .collect();
862        for id in marked {
863            self.mark(id, Dirty::Paint);
864        }
865    }
866
867    /// Returns the node's widget as `W`, or `None` when the id is stale or the
868    /// widget has another type.
869    pub(crate) fn widget_mut<W: Widget>(&mut self, id: WidgetId) -> Option<&mut W> {
870        let node = self.nodes.get_mut(id)?;
871        (node.widget.as_mut() as &mut dyn Any).downcast_mut::<W>()
872    }
873
874    /// Returns the node's reactive owner, or `None` when the id is stale.
875    pub(crate) fn owner(&self, id: WidgetId) -> Option<&Owner> {
876        self.nodes.get(id).map(|node| &node.owner)
877    }
878
879    /// Visits every visible node in paint order, passing the node's depth, the
880    /// name of its widget's concrete type and its absolute rectangle.
881    ///
882    /// The root has depth zero. A hidden node and its subtree are skipped,
883    /// because they are laid out as empty and paint nothing. The tree itself
884    /// stays private: this is how a caller outside the crate reads its
885    /// structure.
886    pub(crate) fn visit_nodes(&self, visit: &mut dyn FnMut(usize, &'static str, Rect)) {
887        if let Some(root) = self.root {
888            self.visit_from(root, 0, visit);
889        }
890    }
891
892    /// Appends `id` and its subtree to the visit started by
893    /// [`WidgetTree::visit_nodes`].
894    fn visit_from(
895        &self,
896        id: WidgetId,
897        depth: usize,
898        visit: &mut dyn FnMut(usize, &'static str, Rect),
899    ) {
900        let Some(node) = self.nodes.get(id) else {
901            return;
902        };
903        if node.resolved.layout.hidden {
904            return;
905        }
906        visit(depth, node.widget.type_name(), node.rect);
907        for &child in &node.children {
908            self.visit_from(child, depth + 1, visit);
909        }
910    }
911
912    /// Returns whether any projected accessibility state may have changed
913    /// since the last [`WidgetTree::take_a11y_changes`].
914    pub(crate) fn has_a11y_changes(&self) -> bool {
915        self.a11y_reset || !self.a11y_dirty.is_empty()
916    }
917
918    /// Takes the projected nodes that changed since the last call.
919    ///
920    /// A change to an unprojected node changes the child list of its nearest
921    /// projected ancestor, so it is reported as that ancestor, or as
922    /// [`A11yChanges::roots_changed`] when no ancestor projects. Stale ids,
923    /// ids that became hidden, and duplicates drop out. When the change queue
924    /// overflowed since the last call, `reset` is set and the changed list is
925    /// empty.
926    pub(crate) fn take_a11y_changes(&mut self) -> A11yChanges {
927        let mut changes = A11yChanges {
928            reset: std::mem::take(&mut self.a11y_reset),
929            ..A11yChanges::default()
930        };
931        let dirty = std::mem::take(&mut self.a11y_dirty);
932        if changes.reset {
933            return changes;
934        }
935        let mut seen = HashSet::new();
936        for id in dirty {
937            if !seen.insert(id) || !self.a11y_id_is_visible(id) {
938                continue;
939            }
940            let projected = if self.nodes.get(id).and_then(Self::effective_role).is_some() {
941                Some(id)
942            } else {
943                self.nearest_projected_ancestor(id)
944            };
945            match projected {
946                Some(projected) if projected == id || seen.insert(projected) => {
947                    changes.changed.push(projected);
948                }
949                Some(_) => {}
950                None => changes.roots_changed = true,
951            }
952        }
953        changes
954    }
955
956    /// Returns whether `id` is live and neither it nor an ancestor is hidden.
957    fn a11y_id_is_visible(&self, id: WidgetId) -> bool {
958        let mut next = Some(id);
959        while let Some(current) = next {
960            let Some(node) = self.nodes.get(current) else {
961                return false;
962            };
963            if node.resolved.layout.hidden {
964                return false;
965            }
966            next = node.parent;
967        }
968        true
969    }
970
971    /// Records that the projected state of `id` may have changed.
972    ///
973    /// Changes queue freely and are deduplicated when taken. A queue that
974    /// outgrows [`A11Y_DIRTY_CAP`] is dropped and the next projection
975    /// rebuilds from scratch, which is cheaper than replaying thousands of
976    /// one-node updates.
977    pub(crate) fn push_a11y(&mut self, id: WidgetId) {
978        if self.a11y_dirty.len() >= A11Y_DIRTY_CAP {
979            self.a11y_dirty.clear();
980            self.a11y_reset = true;
981            return;
982        }
983        self.a11y_dirty.push(id);
984    }
985
986    /// Records `id` and its visible subtree as changed.
987    ///
988    /// A node that becomes visible again reattaches in the projection, so
989    /// every descendant that is visible again has to be rebuilt too.
990    fn push_a11y_visible_subtree(&mut self, id: WidgetId) {
991        let mut stack = vec![id];
992        while let Some(current) = stack.pop() {
993            let Some(node) = self.nodes.get(current) else {
994                continue;
995            };
996            if node.resolved.layout.hidden {
997                continue;
998            }
999            let children = node.children.clone();
1000            self.push_a11y(current);
1001            stack.extend_from_slice(&children);
1002        }
1003    }
1004
1005    /// Sets the role the projection reports for `id`.
1006    ///
1007    /// Called once, while the node is being mounted. A stale id does nothing.
1008    pub(crate) fn set_a11y_role(&mut self, id: WidgetId, role: Option<Role>) {
1009        if let Some(node) = self.nodes.get_mut(id) {
1010            node.a11y_role = role;
1011        }
1012    }
1013
1014    /// Sets the accessible label of `id` and records the change.
1015    ///
1016    /// Called when the node mounts and again by the binding of an
1017    /// [`accessible_label`](crate::element::Element::accessible_label)
1018    /// builder value. A stale id does nothing.
1019    pub(crate) fn set_a11y_label(&mut self, id: WidgetId, label: Option<String>) {
1020        let Some(node) = self.nodes.get_mut(id) else {
1021            return;
1022        };
1023        if node.a11y_label == label {
1024            return;
1025        }
1026        node.a11y_label = label;
1027        self.push_a11y(id);
1028    }
1029
1030    /// Projects every visible node in paint order, for an accessibility
1031    /// backend.
1032    ///
1033    /// A node is projected when its element set a role or its widget reports
1034    /// one; the children of an unprojected node attach to the nearest
1035    /// projected ancestor. A hidden node and its subtree are skipped, like
1036    /// in [`WidgetTree::visit_nodes`]. The tree itself stays private: this is
1037    /// how a caller outside the crate reads the accessibility tree.
1038    pub(crate) fn semantics_nodes(&self) -> Vec<SemanticsNode> {
1039        let mut nodes = Vec::new();
1040        let mut index_of = HashMap::new();
1041        if let Some(root) = self.root {
1042            self.project_from(root, None, &mut nodes, &mut index_of);
1043        }
1044        nodes
1045    }
1046
1047    /// Returns the projected nodes that no projected ancestor holds, in paint
1048    /// order.
1049    pub(crate) fn semantics_roots(&self) -> Vec<WidgetId> {
1050        let mut roots = Vec::new();
1051        if let Some(root) = self.root
1052            && let Some(node) = self.nodes.get(root)
1053            && !node.resolved.layout.hidden
1054        {
1055            if Self::effective_role(node).is_some() {
1056                roots.push(root);
1057            } else {
1058                self.collect_projected_children(root, &mut roots);
1059            }
1060        }
1061        roots
1062    }
1063
1064    /// Projects one node with the children the projection gives it, or
1065    /// [`None`] when the id is stale, hidden above or at `id`, or reports no
1066    /// role.
1067    pub(crate) fn semantics_node(&self, id: WidgetId) -> Option<SemanticsNode> {
1068        if !self.a11y_id_is_visible(id) {
1069            return None;
1070        }
1071        let node = self.nodes.get(id)?;
1072        let role = Self::effective_role(node)?;
1073        let parent = self.nearest_projected_ancestor(id);
1074        let mut out = Self::project_node(id, node, role, parent);
1075        self.collect_projected_children(id, &mut out.children);
1076        Some(out)
1077    }
1078
1079    /// Returns the nearest ancestor of `id` that projects, or [`None`] when
1080    /// every ancestor is unprojected.
1081    fn nearest_projected_ancestor(&self, id: WidgetId) -> Option<WidgetId> {
1082        let mut next = self.nodes.get(id).and_then(|node| node.parent);
1083        while let Some(current) = next {
1084            let node = self.nodes.get(current)?;
1085            if !node.resolved.layout.hidden && Self::effective_role(node).is_some() {
1086                return Some(current);
1087            }
1088            next = node.parent;
1089        }
1090        None
1091    }
1092
1093    /// Walks `id`'s visible subtree in paint order and appends the projected
1094    /// nodes to `nodes`.
1095    fn project_from(
1096        &self,
1097        id: WidgetId,
1098        projected_parent: Option<WidgetId>,
1099        nodes: &mut Vec<SemanticsNode>,
1100        index_of: &mut HashMap<WidgetId, usize>,
1101    ) {
1102        let Some(node) = self.nodes.get(id) else {
1103            return;
1104        };
1105        if node.resolved.layout.hidden {
1106            return;
1107        }
1108        let Some(role) = Self::effective_role(node) else {
1109            for &child in &node.children {
1110                self.project_from(child, projected_parent, nodes, index_of);
1111            }
1112            return;
1113        };
1114        let index = nodes.len();
1115        nodes.push(Self::project_node(id, node, role, projected_parent));
1116        index_of.insert(id, index);
1117        if let Some(parent) = projected_parent
1118            && let Some(&parent_index) = index_of.get(&parent)
1119        {
1120            nodes[parent_index].children.push(id);
1121        }
1122        for &child in &node.children {
1123            self.project_from(child, Some(id), nodes, index_of);
1124        }
1125    }
1126
1127    /// Builds the projected form of one node.
1128    fn project_node(
1129        id: WidgetId,
1130        node: &Node,
1131        role: Role,
1132        parent: Option<WidgetId>,
1133    ) -> SemanticsNode {
1134        // The widget builds its description, value included, once per
1135        // projection, since a text widget copies its whole text into it.
1136        let widget: &dyn Widget = node.widget.as_ref();
1137        let semantics = widget.semantics();
1138        let (label, value) = match semantics {
1139            Some(semantics) => (semantics.label, semantics.value),
1140            None => (None, None),
1141        };
1142        SemanticsNode {
1143            id,
1144            parent,
1145            children: Vec::new(),
1146            role,
1147            label: node.a11y_label.clone().or(label),
1148            value,
1149            bounds: node.rect,
1150            focusable: node.interaction.has(Interaction::FOCUSABLE),
1151            disabled: node.interaction.has(Interaction::DISABLED),
1152            focused: node.interaction.has(Interaction::FOCUSED),
1153            pressable: node.handlers.on_press.is_some(),
1154        }
1155    }
1156
1157    /// Appends the ids of `id`'s projected descendants to `out`, walking
1158    /// through unprojected children.
1159    fn collect_projected_children(&self, id: WidgetId, out: &mut Vec<WidgetId>) {
1160        let Some(node) = self.nodes.get(id) else {
1161            return;
1162        };
1163        if node.resolved.layout.hidden {
1164            return;
1165        }
1166        for &child in &node.children {
1167            let Some(child_node) = self.nodes.get(child) else {
1168                continue;
1169            };
1170            if child_node.resolved.layout.hidden {
1171                continue;
1172            }
1173            if Self::effective_role(child_node).is_some() {
1174                out.push(child);
1175            } else {
1176                self.collect_projected_children(child, out);
1177            }
1178        }
1179    }
1180
1181    /// Returns the role the projection reports for the node: the one its
1182    /// element set, else the one its widget reports.
1183    fn effective_role(node: &Node) -> Option<Role> {
1184        node.a11y_role.or_else(|| {
1185            let widget: &dyn Widget = node.widget.as_ref();
1186            widget.semantics().map(|semantics| semantics.role)
1187        })
1188    }
1189
1190    /// Panics unless the layout engine's tree and the node store describe the
1191    /// same parents, children, contexts and layout styles.
1192    #[cfg(test)]
1193    pub(crate) fn debug_assert_consistent(&self) {
1194        assert_eq!(
1195            self.taffy.total_node_count(),
1196            self.nodes.len(),
1197            "every widget node has exactly one layout node"
1198        );
1199        for (id, node) in &self.nodes {
1200            let taffy_children = self
1201                .taffy
1202                .children(node.taffy)
1203                .expect("every widget node has a live layout node");
1204            assert_eq!(
1205                taffy_children.len(),
1206                node.children.len(),
1207                "the child count agrees"
1208            );
1209            for (child, taffy_child) in node.children.iter().zip(taffy_children) {
1210                let child_node = &self.nodes[*child];
1211                assert_eq!(child_node.taffy, taffy_child, "the child order agrees");
1212                assert_eq!(
1213                    child_node.parent,
1214                    Some(id),
1215                    "every child points back at its parent"
1216                );
1217            }
1218            assert_eq!(
1219                self.taffy.parent(node.taffy),
1220                node.parent.map(|parent| self.nodes[parent].taffy),
1221                "the parent agrees"
1222            );
1223            assert_eq!(
1224                self.taffy.get_node_context(node.taffy),
1225                Some(&id),
1226                "the layout node's context is the widget id"
1227            );
1228            assert_eq!(
1229                self.taffy
1230                    .style(node.taffy)
1231                    .expect("every widget node has a live layout node"),
1232                &node.resolved.layout.to_taffy(),
1233                "the layout style agrees"
1234            );
1235        }
1236        for trap in &self.focus_traps {
1237            assert!(
1238                self.nodes.contains_key(trap.node),
1239                "every mounted focus trap is a live node"
1240            );
1241        }
1242        for id in &self.pending_autofocus {
1243            assert!(
1244                self.nodes.contains_key(*id),
1245                "every queued autofocus request belongs to a live node"
1246            );
1247        }
1248    }
1249}
1250
1251/// The state that tests outside this module observe without reading the store.
1252#[cfg(test)]
1253impl WidgetTree {
1254    /// Returns the node's dirty flag, or `None` when the id is stale.
1255    pub(crate) fn dirty(&self, id: WidgetId) -> Option<Dirty> {
1256        self.nodes.get(id).map(|node| node.dirty)
1257    }
1258
1259    /// Returns whether any node needs a new layout pass.
1260    pub(crate) fn layout_is_dirty(&self) -> bool {
1261        self.layout_dirty
1262    }
1263
1264    /// Returns the node's absolute rectangle, or `None` when the id is stale.
1265    pub(crate) fn rect(&self, id: WidgetId) -> Option<Rect> {
1266        self.nodes.get(id).map(|node| node.rect)
1267    }
1268
1269    /// Returns the rectangle the node is clipped to, or `None` when the id is
1270    /// stale.
1271    pub(crate) fn clip(&self, id: WidgetId) -> Option<Rect> {
1272        self.nodes.get(id).map(|node| node.clip)
1273    }
1274
1275    /// Returns how many times the layout engine has run for this tree.
1276    pub(crate) fn layout_runs(&self) -> u64 {
1277        self.layout_runs
1278    }
1279
1280    /// Returns the node's children in paint order, or `None` when the id is
1281    /// stale.
1282    pub(crate) fn children(&self, id: WidgetId) -> Option<&[WidgetId]> {
1283        self.nodes.get(id).map(|node| node.children.as_slice())
1284    }
1285
1286    /// Returns the number of nodes in the tree.
1287    pub(crate) fn node_count(&self) -> usize {
1288        self.nodes.len()
1289    }
1290
1291    /// Clears the dirty state of the whole tree, as a frame does once it has
1292    /// laid the tree out and painted every queued node.
1293    pub(crate) fn clear_dirty(&mut self) {
1294        for node in self.nodes.values_mut() {
1295            node.dirty = Dirty::Clean;
1296        }
1297        self.paint_queue.clear();
1298        self.layout_dirty = false;
1299        self.pending_damage.clear();
1300    }
1301}
1302
1303#[cfg(test)]
1304#[path = "tree_tests.rs"]
1305mod tests;