Skip to main content

cranpose_ui/layout/
mod.rs

1mod bottom_up;
2mod coordinator_chain;
3/// Re-exports of the alignment, arrangement and measurable types that layout policies use.
4pub mod core;
5pub(crate) mod flex_box;
6mod frame_layout_arena;
7pub(crate) mod grid;
8mod layout_child_measurable;
9mod layout_child_measure_state;
10mod layout_node_data;
11mod live_layout_node;
12mod measure_telemetry;
13mod measured_spare;
14mod modifier_chain_measure;
15mod placed_root_measurements;
16/// Measure policies behind the built-in layout widgets such as `Box`, `Row` and `Column`.
17pub mod policies;
18mod reveal;
19mod semantics_details;
20mod semantics_labels;
21mod semantics_tree;
22mod semantics_update;
23mod subcompose_measure;
24
25use std::{cell::RefCell, mem::size_of, rc::Rc};
26
27use cranpose_core::{
28    Applier, ConcreteApplierHost, MemoryApplier, Node, NodeError, NodeId, RuntimeHandle,
29};
30use cranpose_foundation::{
31    InvalidationKind, ModifierNodeContext, SemanticsConfiguration, SemanticsWidgetRole,
32};
33use cranpose_ui_graphics::ProjectiveTransform;
34use cranpose_ui_layout::{AlignmentLines, Constraints, MeasurePolicy, PlaceTarget};
35pub(crate) use frame_layout_arena::FrameLayoutArena;
36use frame_layout_arena::VecPools;
37use layout_child_measure_state::{
38    ChildPass, LayoutChildMeasureState, LayoutChildren, inherited_alignment_lines, read_cell,
39};
40use layout_node_data::{role_from_modifier_slices, snapshot_node_data};
41use live_layout_node::{LiveLayoutNode, read_live_layout_node};
42use measured_spare::MeasuredSpare;
43use placed_root_measurements::PlacedRootMeasurements;
44pub use semantics_tree::{
45    SemanticsAction, SemanticsCallback, SemanticsNode, SemanticsPlacement, SemanticsRole,
46    SemanticsText, SemanticsTree,
47};
48use semantics_tree::{modal_takes_space, top_modal_path};
49use web_time::Instant;
50
51#[cfg(test)]
52use self::core::{HorizontalAlignment, VerticalAlignment};
53use self::{
54    coordinator_chain::{CoordinatorChain, CoordinatorFrame},
55    core::Measurable,
56    measure_telemetry::{
57        LayoutStages, layout_measure_telemetry_threshold_ms, log_layout_measure_telemetry,
58        log_node_measure_telemetry,
59    },
60    semantics_update::semantics_placement,
61};
62pub use self::{
63    layout_node_data::{LayoutNodeData, LayoutNodeKind},
64    reveal::can_skip_scroll_reveal_from_applier,
65    semantics_details::SemanticsDetails,
66    semantics_update::{build_semantics_tree_from_applier, update_semantics_tree_from_applier},
67};
68use crate::{
69    modifier::{
70        DimensionConstraint, EdgeInsets, ModifierNodeSlicesDebugStats, Point, Rect as GeometryRect,
71        Size,
72    },
73    render_state::AppContext,
74    subcompose_layout::SubcomposeLayoutNode,
75    widgets::nodes::{ChildPlacement, IntrinsicKind, LayoutNode, LayoutParts, LayoutState},
76};
77
78#[derive(Default)]
79pub(crate) struct LayoutNodeContext {
80    invalidations: Vec<InvalidationKind>,
81    update_requested: bool,
82    density: f32,
83}
84
85impl LayoutNodeContext {
86    pub(crate) fn new(density: f32) -> Self {
87        Self {
88            density,
89            ..Self::default()
90        }
91    }
92
93    pub(crate) fn take_invalidations(&mut self) -> Vec<InvalidationKind> {
94        std::mem::take(&mut self.invalidations)
95    }
96}
97
98impl ModifierNodeContext for LayoutNodeContext {
99    fn invalidate(&mut self, kind: InvalidationKind) {
100        if !self.invalidations.contains(&kind) {
101            self.invalidations.push(kind);
102        }
103    }
104
105    fn request_update(&mut self) {
106        self.update_requested = true;
107    }
108
109    fn density(&self) -> f32 {
110        self.density
111    }
112}
113
114#[doc(hidden)]
115pub fn invalidate_all_layout_caches() {
116    crate::render_state::invalidate_layout_cache_epoch();
117}
118
119/// The host a layout pass lends its applier to. Between passes it holds an
120/// empty applier and the app context keeps it, so a pass builds neither.
121pub(crate) struct LayoutHost {
122    host: Rc<ConcreteApplierHost<MemoryApplier>>,
123}
124
125impl Default for LayoutHost {
126    fn default() -> Self {
127        Self {
128            host: Rc::new(ConcreteApplierHost::new(MemoryApplier::new())),
129        }
130    }
131}
132
133/// Lends `target` to a [`LayoutHost`] of `context` for one pass and gives it
134/// back when dropped, also after a panic.
135struct ApplierSlotGuard<'a> {
136    target: &'a mut MemoryApplier,
137    lent: LayoutHost,
138    context: &'a AppContext,
139}
140
141impl<'a> ApplierSlotGuard<'a> {
142    fn new(target: &'a mut MemoryApplier, context: &'a AppContext) -> Self {
143        let lent = context.take_layout_host();
144        std::mem::swap(target, &mut *lent.host.borrow_typed());
145        Self {
146            target,
147            lent,
148            context,
149        }
150    }
151
152    fn host(&self) -> &Rc<ConcreteApplierHost<MemoryApplier>> {
153        &self.lent.host
154    }
155}
156
157impl Drop for ApplierSlotGuard<'_> {
158    fn drop(&mut self) {
159        let host = &self.lent.host;
160        std::mem::swap(self.target, &mut *host.borrow_typed());
161        // A host still shared past the pass stays with whoever holds it.
162        if Rc::strong_count(host) == 1 {
163            self.context.replace_layout_host(LayoutHost {
164                host: Rc::clone(host),
165            });
166        }
167    }
168}
169
170#[derive(Clone, Copy, PartialEq)]
171struct ModifierChainInputs {
172    density: crate::density::Density,
173    window_root: bool,
174    offset: Point,
175    uses_chain: bool,
176}
177
178struct ModifierChainMeasurement {
179    size: Size,
180    alignment_lines: AlignmentLines,
181    content_offset: Point,
182    offset: Point,
183    window_root: bool,
184    /// The incoming constraints this measurement holds for besides its own.
185    hold: Option<cranpose_ui_layout::ConstraintsHold>,
186    /// Whether no child's size entered it: see
187    /// [`MeasurePolicy::ignores_child_sizes`].
188    ignores_child_sizes: bool,
189}
190
191/// Counts and heap sizes of the layout and semantics data that one measure pass retains, for memory
192/// diagnostics.
193#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
194pub struct LayoutAllocationDebugStats {
195    /// Number of `LayoutBox` nodes in the tree.
196    pub layout_box_count: usize,
197    /// Total number of child entries across all layout boxes.
198    pub layout_box_child_count: usize,
199    /// Total allocated capacity of the child vectors across all layout boxes.
200    pub layout_box_child_capacity: usize,
201    /// Heap bytes allocated for the child vectors of all layout boxes.
202    pub layout_box_heap_bytes: usize,
203    /// Number of modifier slice snapshots, one per layout box.
204    pub modifier_slice_count: usize,
205    /// Heap bytes held by all modifier slice snapshots.
206    pub modifier_slice_heap_bytes: usize,
207    /// Number of draw commands collected from modifier nodes.
208    pub modifier_draw_command_count: usize,
209    /// Allocated capacity of the draw command lists.
210    pub modifier_draw_command_capacity: usize,
211    /// Number of pointer input handlers collected from modifier nodes.
212    pub modifier_pointer_input_count: usize,
213    /// Allocated capacity of the pointer input handler lists.
214    pub modifier_pointer_input_capacity: usize,
215    /// Number of layout boxes that carry text content.
216    pub modifier_text_content_count: usize,
217    /// Number of layout boxes that carry a text style.
218    pub modifier_text_style_count: usize,
219    /// Number of layout boxes that carry text layout options.
220    pub modifier_text_layout_options_count: usize,
221    /// Number of layout boxes that carry a prepared text layout.
222    pub modifier_prepared_text_layout_count: usize,
223    /// Number of layout boxes that carry a graphics layer.
224    pub modifier_graphics_layer_count: usize,
225    /// Number of layout boxes that carry a graphics layer resolver.
226    pub modifier_graphics_layer_resolver_count: usize,
227    /// Number of nodes in the semantics tree.
228    pub semantics_node_count: usize,
229    /// Total number of actions across all semantics nodes.
230    pub semantics_action_count: usize,
231    /// Total allocated capacity of the action vectors across all semantics nodes.
232    pub semantics_action_capacity: usize,
233    /// Total number of child entries across all semantics nodes.
234    pub semantics_child_count: usize,
235    /// Total allocated capacity of the child vectors across all semantics nodes.
236    pub semantics_child_capacity: usize,
237    /// Number of semantics nodes that have a content description.
238    pub semantics_description_count: usize,
239    /// Allocated bytes of all content description strings.
240    pub semantics_description_bytes: usize,
241    /// Heap bytes held by the semantics tree: actions, children, details and descriptions.
242    pub semantics_heap_bytes: usize,
243}
244
245impl LayoutAllocationDebugStats {
246    fn add_modifier_slice(&mut self, stats: ModifierNodeSlicesDebugStats) {
247        self.modifier_slice_count += 1;
248        self.modifier_slice_heap_bytes += stats.heap_bytes;
249        self.modifier_draw_command_count += stats.draw_command_count;
250        self.modifier_draw_command_capacity += stats.draw_command_capacity;
251        self.modifier_pointer_input_count += stats.pointer_input_count;
252        self.modifier_pointer_input_capacity += stats.pointer_input_capacity;
253        self.modifier_text_content_count += usize::from(stats.has_text_content);
254        self.modifier_text_style_count += usize::from(stats.has_text_style);
255        self.modifier_text_layout_options_count += usize::from(stats.has_text_layout_options);
256        self.modifier_prepared_text_layout_count += usize::from(stats.has_prepared_text_layout);
257        self.modifier_graphics_layer_count += usize::from(stats.has_graphics_layer);
258        self.modifier_graphics_layer_resolver_count +=
259            usize::from(stats.has_graphics_layer_resolver);
260    }
261}
262
263/// Result of running layout for a Compose tree.
264#[derive(Debug, Clone)]
265pub struct LayoutTree {
266    root: LayoutBox,
267}
268
269impl LayoutTree {
270    /// Wraps `root` as a layout tree.
271    pub fn new(root: LayoutBox) -> Self {
272        Self { root }
273    }
274
275    /// Returns the root layout box.
276    pub fn root(&self) -> &LayoutBox {
277        &self.root
278    }
279
280    /// Returns the root layout box for in-place changes.
281    pub fn root_mut(&mut self) -> &mut LayoutBox {
282        &mut self.root
283    }
284
285    /// Consumes the tree and returns its root layout box.
286    pub fn into_root(self) -> LayoutBox {
287        self.root
288    }
289
290    /// Counts the allocations that the layout boxes of this tree retain.
291    pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
292        let mut stats = LayoutAllocationDebugStats::default();
293        record_layout_box_allocation_stats(&self.root, &mut stats);
294        stats
295    }
296}
297
298/// Layout information for a single node.
299#[derive(Debug, Clone)]
300pub struct LayoutBox {
301    /// Id of the runtime node this box describes.
302    pub node_id: NodeId,
303    /// Incarnation of the runtime node captured with these layout bounds.
304    pub node_generation: u32,
305    /// Position and size of the node in root coordinates, in logical pixels, before graphics-layer
306    /// transforms.
307    pub rect: GeometryRect,
308    /// Content offset for scroll/inner transforms (applies to children, NOT this node's position)
309    pub content_offset: Point,
310    /// Modifier, semantics and kind data captured with the node.
311    pub node_data: LayoutNodeData,
312    /// Layout boxes of the node's placed children.
313    pub children: Vec<Self>,
314}
315
316impl LayoutBox {
317    /// Creates a layout box with node generation zero.
318    pub fn new(
319        node_id: NodeId,
320        rect: GeometryRect,
321        content_offset: Point,
322        node_data: LayoutNodeData,
323        children: Vec<Self>,
324    ) -> Self {
325        Self {
326            node_id,
327            node_generation: 0,
328            rect,
329            content_offset,
330            node_data,
331            children,
332        }
333    }
334}
335
336/// Extension trait that equips `MemoryApplier` with layout computation.
337pub trait LayoutEngine {
338    /// Measures the tree under `root` within `max_size` and returns its layout tree.
339    fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError>;
340}
341
342impl LayoutEngine for MemoryApplier {
343    fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError> {
344        let measurements = measure_layout(self, root, max_size)?;
345        measurements
346            .into_layout_tree()
347            .ok_or(NodeError::MissingContext {
348                id: root,
349                reason: "layout tree was not requested",
350            })
351    }
352}
353
354/// Result of running the measure pass for a Compose layout tree.
355#[derive(Debug, Clone)]
356pub struct LayoutMeasurements {
357    root: Rc<MeasuredNode>,
358    // Boxed: a frame that builds neither tree moves a few words, not the
359    // hundreds of bytes the trees take inline.
360    semantics: Option<Box<SemanticsTree>>,
361    layout_tree: Option<Box<LayoutTree>>,
362}
363
364impl LayoutMeasurements {
365    fn new(
366        root: Rc<MeasuredNode>,
367        semantics: Option<SemanticsTree>,
368        layout_tree: Option<LayoutTree>,
369    ) -> Self {
370        Self {
371            root,
372            semantics: semantics.map(Box::new),
373            layout_tree: layout_tree.map(Box::new),
374        }
375    }
376
377    /// Returns the measured size of the root node.
378    pub fn root_size(&self) -> Size {
379        self.root.size
380    }
381
382    /// Returns the semantics tree, or `None` if the measure options left it out.
383    pub fn semantics_tree(&self) -> Option<&SemanticsTree> {
384        self.semantics.as_deref()
385    }
386
387    /// Counts the allocations that the layout tree and the semantics tree retain.
388    pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
389        let mut stats = self
390            .layout_tree
391            .as_deref()
392            .map(LayoutTree::debug_allocation_stats)
393            .unwrap_or_default();
394        if let Some(semantics) = &self.semantics {
395            record_semantics_allocation_stats(semantics.root(), &mut stats);
396        }
397        stats
398    }
399
400    /// Consumes the measurements and returns the built [`LayoutTree`], if requested.
401    pub fn into_layout_tree(self) -> Option<LayoutTree> {
402        self.layout_tree.map(|tree| *tree)
403    }
404
405    /// Returns a cloned [`LayoutTree`] for rendering/debug consumers, if requested.
406    pub fn layout_tree(&self) -> Option<LayoutTree> {
407        self.layout_tree.as_deref().cloned()
408    }
409}
410
411/// Builds a semantics tree from an existing [`LayoutTree`].
412///
413/// This is useful for consumers that need semantics on demand without forcing
414/// every layout pass to eagerly allocate a full [`SemanticsTree`].
415pub fn build_semantics_tree_from_layout_tree(layout_tree: &LayoutTree) -> SemanticsTree {
416    SemanticsTree::new(build_semantics_node_from_layout_box(
417        layout_tree.root(),
418        Point::default(),
419    ))
420}
421
422/// Builds a layout snapshot from retained layout state in the live applier tree.
423///
424/// Renderers use retained node state directly. This function exists for debug,
425/// robot, and tests that need an owned [`LayoutTree`] without forcing every
426/// layout pass to allocate one.
427pub fn build_layout_tree_from_applier(
428    applier: &mut MemoryApplier,
429    root: NodeId,
430) -> Result<Option<LayoutTree>, NodeError> {
431    let origin = layout_tree_origin(read_layout_node(applier, root, |state, _| state)?);
432    let mut child_stack = Vec::new();
433    place_layout_box(
434        applier,
435        root,
436        origin,
437        ProjectiveTransform::identity(),
438        &mut child_stack,
439    )
440    .map(|root| root.map(LayoutTree::new))
441}
442
443/// Whether any box placed under `root` has area, as
444/// [`build_layout_tree_from_applier`] would place it: the walk stops at the
445/// first such box and reads no node's modifiers or semantics.
446pub fn has_placed_content(applier: &mut MemoryApplier, root: NodeId) -> Result<bool, NodeError> {
447    walk_placed_boxes(applier, root, |_| std::ops::ControlFlow::Break(()))
448}
449
450/// The far right and bottom edges of the boxes with area placed under `root`,
451/// measured from the root's origin as [`build_layout_tree_from_applier`]
452/// places them, reading no node's modifiers or semantics. `None` while
453/// nothing under `root` has area.
454pub fn placed_content_extent(
455    applier: &mut MemoryApplier,
456    root: NodeId,
457) -> Result<Option<Size>, NodeError> {
458    let mut extent: Option<Size> = None;
459    walk_placed_boxes(applier, root, |rect| {
460        let (right, bottom) = (rect.x + rect.width, rect.y + rect.height);
461        extent = Some(extent.map_or_else(
462            || Size::new(right, bottom),
463            |extent| Size::new(extent.width.max(right), extent.height.max(bottom)),
464        ));
465        std::ops::ControlFlow::Continue(())
466    })?;
467    Ok(extent)
468}
469
470/// Hands `visit` every box with area placed under `root`, the root left out,
471/// at the rect [`build_layout_tree_from_applier`] gives it, skipping a window
472/// root's subtree and an unplaced node's, until `visit` breaks. Answers
473/// whether it did.
474fn walk_placed_boxes(
475    applier: &mut MemoryApplier,
476    root: NodeId,
477    mut visit: impl FnMut(GeometryRect) -> std::ops::ControlFlow<()>,
478) -> Result<bool, NodeError> {
479    let mut pending: Vec<(NodeId, Point)> = Vec::new();
480    let placed = read_layout_node(applier, root, |state, children| {
481        if state.is_placed() {
482            pending.extend(
483                children
484                    .iter()
485                    .map(|&child| (child, state.content_offset())),
486            );
487        }
488    })?;
489    if placed.is_none() {
490        return Ok(false);
491    }
492    while let Some((node_id, origin)) = pending.pop() {
493        if crate::modifier::is_window_root(applier, node_id) {
494            continue;
495        }
496        let rect = read_layout_node(applier, node_id, |state, children| {
497            if !state.is_placed() {
498                return None;
499            }
500            let (rect, content) = semantics_placement(&state, Some(origin));
501            pending.extend(children.iter().map(|&child| (child, content)));
502            Some(rect)
503        })?
504        .flatten();
505        if let Some(rect) = rect
506            && rect.width > 0.0
507            && rect.height > 0.0
508            && visit(rect).is_break()
509        {
510            return Ok(true);
511        }
512    }
513    Ok(false)
514}
515
516fn layout_tree_origin(root: Option<LayoutState>) -> Point {
517    let Some(state) = root else {
518        return Point::default();
519    };
520    let position = state.position();
521    Point {
522        x: -position.x,
523        y: -position.y,
524    }
525}
526
527/// Places the root at the origin, the one placement no parent makes, and
528/// names what the pass left unplaced.
529fn finish_placement(applier: &mut MemoryApplier, root: NodeId, context: &AppContext) {
530    if applier
531        .with_node::<LayoutNode, _>(root, |node| {
532            node.place(ChildPlacement::default(), None, context);
533        })
534        .is_err()
535    {
536        let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
537            node.place(ChildPlacement::default(), None, context);
538        });
539    }
540    if let Some(pass) = context.placement_pass().with_unplaced_nodes() {
541        record_unplaced_parents(applier, root, pass, context);
542    }
543}
544
545/// Names to the scene phase the parent of every node `pass` found placed and
546/// left unplaced: no geometry setter reported those, yet their layers leave
547/// the scene. Out of line: a pass rarely unplaces nodes.
548#[inline(never)]
549fn record_unplaced_parents(
550    applier: &mut MemoryApplier,
551    root: NodeId,
552    pass: u64,
553    context: &AppContext,
554) {
555    let mut parents = vec![root];
556    let mut children = Vec::new();
557    while let Some(parent) = parents.pop() {
558        children.clear();
559        if !matches!(
560            read_layout_node(applier, parent, |_, ids| children.extend_from_slice(ids)),
561            Ok(Some(()))
562        ) {
563            continue;
564        }
565        for &child in &children {
566            match read_layout_node(applier, child, |state, _| state.unplaced_in(pass)) {
567                Ok(Some(true)) => context.record_geometry_scene_node(parent),
568                Ok(Some(false)) => parents.push(child),
569                Ok(None) | Err(_) => {}
570            }
571        }
572    }
573}
574
575fn read_layout_node<R>(
576    applier: &mut MemoryApplier,
577    node_id: NodeId,
578    mut read: impl FnMut(LayoutState, &[NodeId]) -> R,
579) -> Result<Option<R>, NodeError> {
580    read_live_layout_node(applier, node_id, |node| {
581        node.with_children(|children| read(node.state(), children))
582    })
583}
584
585/// Places `node_id`'s box and its subtree. `child_stack` is shared by the
586/// whole walk: each node pushes its children above its parent's, reads them
587/// from there while its descendants push and pop above them, and pops them
588/// when done, so no node's child list is copied out of the applier.
589fn place_layout_box(
590    applier: &mut MemoryApplier,
591    node_id: NodeId,
592    parent_content_origin: Point,
593    parent_transform: ProjectiveTransform,
594    child_stack: &mut Vec<NodeId>,
595) -> Result<Option<LayoutBox>, NodeError> {
596    let first_child = child_stack.len();
597    let Some(state) = read_layout_node(applier, node_id, |state, children| {
598        if state.is_placed() {
599            child_stack.extend_from_slice(children);
600        }
601        state
602    })?
603    else {
604        return Ok(None);
605    };
606    if !state.is_placed() {
607        return Ok(None);
608    }
609
610    let top_left = Point {
611        x: parent_content_origin.x + state.position().x,
612        y: parent_content_origin.y + state.position().y,
613    };
614    let rect = GeometryRect {
615        x: top_left.x,
616        y: top_left.y,
617        width: state.size().width,
618        height: state.size().height,
619    };
620    let (data, window_transform) =
621        snapshot_node_data(applier, node_id, top_left, state.size(), parent_transform)?;
622    let child_origin = Point {
623        x: top_left.x + state.content_offset().x,
624        y: top_left.y + state.content_offset().y,
625    };
626    let end = child_stack.len();
627    let mut children = Vec::with_capacity(end - first_child);
628    for index in first_child..end {
629        let child_id = child_stack[index];
630        if crate::modifier::is_window_root(applier, child_id) {
631            continue;
632        }
633        if let Some(child) = place_layout_box(
634            applier,
635            child_id,
636            child_origin,
637            window_transform,
638            child_stack,
639        )? {
640            children.push(child);
641        }
642    }
643    child_stack.truncate(first_child);
644
645    Ok(Some(LayoutBox {
646        node_generation: applier.node_generation(node_id),
647        ..LayoutBox::new(node_id, rect, state.content_offset(), data, children)
648    }))
649}
650
651/// The modal the semantics tree of `root` would be rooted at, found without
652/// building the tree: the topmost placed, visible modal that takes space,
653/// or `None` when no modal is open. Unlike a tree update, it leaves the
654/// nodes' semantics dirty flags alone.
655pub fn top_modal_from_applier(
656    applier: &mut MemoryApplier,
657    root: NodeId,
658) -> Result<Option<NodeId>, NodeError> {
659    // One depth-first walk on a shared stack, last child first, with a
660    // modal's own step below its children's: the first modal step popped is
661    // the modal drawn on top. It runs every frame, so no node's children are
662    // copied and each node is looked up once.
663    enum Step {
664        Enter { node: NodeId, child: bool },
665        Modal(NodeId),
666    }
667
668    fn push_placed(
669        steps: &mut Vec<Step>,
670        node: NodeId,
671        reach: cranpose_foundation::SemanticsReach,
672        size: Size,
673        children: impl IntoIterator<Item = NodeId>,
674    ) {
675        if reach.hidden {
676            return;
677        }
678        if reach.is_modal && modal_takes_space(size) {
679            steps.push(Step::Modal(node));
680        }
681        steps.extend(
682            children
683                .into_iter()
684                .map(|node| Step::Enter { node, child: true }),
685        );
686    }
687
688    let mut steps = vec![Step::Enter {
689        node: root,
690        child: false,
691    }];
692    while let Some(step) = steps.pop() {
693        let (node_id, child) = match step {
694            Step::Modal(node) => return Ok(Some(node)),
695            Step::Enter { node, child } => (node, child),
696        };
697        let node = match applier.get_mut(node_id) {
698            Ok(node) => node.as_any_mut(),
699            Err(NodeError::Missing { .. }) => continue,
700            Err(error) => return Err(error),
701        };
702        if let Some(layout) = node.downcast_mut::<LayoutNode>() {
703            let state = layout.layout_state();
704            if state.is_placed() && !(child && layout.is_window_root()) {
705                let children = layout.children.iter().copied();
706                push_placed(
707                    &mut steps,
708                    node_id,
709                    layout.semantics_reach(),
710                    state.size(),
711                    children,
712                );
713            }
714        } else if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
715            let state = subcompose.layout_state();
716            if state.is_placed() {
717                let reach = subcompose.semantics_reach();
718                subcompose.with_active_children(|children| {
719                    push_placed(
720                        &mut steps,
721                        node_id,
722                        reach,
723                        state.size(),
724                        children.iter().copied(),
725                    );
726                });
727            }
728        }
729    }
730    Ok(None)
731}
732
733/// Selects which trees [`measure_layout_with_options`] builds.
734#[derive(Clone, Copy, Debug, PartialEq, Eq)]
735pub struct MeasureLayoutOptions {
736    /// Build the semantics tree.
737    pub collect_semantics: bool,
738    /// Build the layout tree.
739    pub build_layout_tree: bool,
740}
741
742impl Default for MeasureLayoutOptions {
743    fn default() -> Self {
744        Self {
745            collect_semantics: true,
746            build_layout_tree: true,
747        }
748    }
749}
750
751/// Check if a node or any of its descendants needs measure (selective measure optimization).
752/// This can be used by the app shell to skip layout when the tree is clean.
753///
754/// O(1) check - just looks at root's dirty flag.
755/// Works because all mutation paths bubble dirty flags to root via composer commands.
756///
757/// Returns Result to force caller to handle errors explicitly. No more unwrap_or(true) safety net.
758pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
759    Ok(applier.get_mut(root)?.layout_dirty())
760}
761
762/// Check if the root semantics snapshot is dirty.
763///
764/// Semantics invalidations bubble to the root the same way layout invalidations do,
765/// so a root check is sufficient to determine whether the next layout pass needs to
766/// rebuild semantic data even when geometry is otherwise unchanged.
767pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
768    Ok(applier.get_mut(root)?.needs_semantics())
769}
770
771#[cfg(test)]
772pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
773    cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
774}
775
776/// Runs the measure phase for the subtree rooted at `root`.
777pub fn measure_layout(
778    applier: &mut MemoryApplier,
779    root: NodeId,
780    max_size: Size,
781) -> Result<LayoutMeasurements, NodeError> {
782    measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
783}
784
785/// Runs the measure phase for the subtree rooted at `root` and builds only the trees that `options`
786/// select.
787pub fn measure_layout_with_options(
788    applier: &mut MemoryApplier,
789    root: NodeId,
790    max_size: Size,
791    options: MeasureLayoutOptions,
792) -> Result<LayoutMeasurements, NodeError> {
793    let mut stages = LayoutStages::start(layout_measure_telemetry_threshold_ms().is_some());
794    let context = crate::render_state::require_current_app_context("a layout pass");
795    context.begin_text_layout_pass();
796    context.placement_pass().begin();
797    process_pending_layout_repasses(&context, applier, root)?;
798    stages.reached();
799
800    let constraints = Constraints {
801        min_width: 0.0,
802        max_width: max_size.width,
803        min_height: 0.0,
804        max_height: max_size.height,
805    };
806
807    let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
808        .with_node::<LayoutNode, _>(root, |node| {
809            (
810                node.needs_measure() || Node::descendant_needs_measure(node),
811                node.needs_semantics(),
812                node.cache().epoch(),
813            )
814        }) {
815        Ok(tuple) => tuple,
816        Err(NodeError::TypeMismatch { .. }) => {
817            let node = applier.get_mut(root)?;
818            let measure_dirty = node.needs_measure() || node.descendant_needs_measure();
819            let semantics_dirty = node.needs_semantics();
820            (measure_dirty, semantics_dirty, 0)
821        }
822        Err(err) => return Err(err),
823    };
824
825    let epoch = if needs_remeasure {
826        context.next_layout_cache_epoch()
827    } else if cached_epoch != 0 {
828        cached_epoch
829    } else {
830        context.current_layout_cache_epoch()
831    };
832
833    let guard = ApplierSlotGuard::new(applier, &context);
834    let applier_host = guard.host();
835    stages.reached();
836
837    let builder = LayoutBuilder::new_with_epoch(Rc::clone(applier_host), epoch, &context);
838    stages.reached();
839
840    let measured = builder
841        .state
842        .measure_root(root, normalize_constraints(constraints))?;
843    stages.reached();
844
845    if let Ok(mut applier) = applier_host.try_borrow_typed() {
846        finish_placement(&mut applier, root, &context);
847    }
848    stages.reached();
849
850    let (layout_tree, semantics) = {
851        let mut applier_ref = applier_host.borrow_typed();
852        let layout_tree = if options.build_layout_tree {
853            Some(build_layout_tree(&mut applier_ref, &measured)?)
854        } else {
855            None
856        };
857        let semantics = if options.collect_semantics {
858            let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
859                clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
860                build_semantics_tree_from_layout_tree(layout_tree)
861            } else {
862                build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
863            };
864            Some(semantics_tree)
865        } else {
866            None
867        };
868        (layout_tree, semantics)
869    };
870    stages.reached();
871
872    drop(builder);
873    stages.reached();
874
875    drop(guard);
876    stages.reached();
877
878    log_layout_measure_telemetry(root, &stages);
879
880    Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
881}
882
883fn process_pending_layout_repasses(
884    context: &AppContext,
885    applier: &mut MemoryApplier,
886    root: NodeId,
887) -> Result<(), NodeError> {
888    for node_id in context.take_modifier_slice_repass_nodes() {
889        if let Ok(node) = applier.get_mut(node_id) {
890            let any = node.as_any_mut();
891            if let Some(layout) = any.downcast_mut::<LayoutNode>() {
892                layout.mark_modifier_slices_dirty();
893            } else if let Some(subcompose) = any.downcast_mut::<SubcomposeLayoutNode>() {
894                subcompose.mark_modifier_slices_dirty();
895            }
896        }
897    }
898    let measure_repass_nodes = context.take_measure_repass_nodes();
899    let repass_nodes = context.take_layout_repass_nodes();
900    if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
901        return Ok(());
902    }
903    for node_id in measure_repass_nodes {
904        cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
905    }
906    for node_id in repass_nodes {
907        cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
908    }
909    applier.get_mut(root)?.mark_descendant_needs_layout(false);
910    Ok(())
911}
912
913/// A layout pass's builder, with the frame arena it took from `context` and
914/// gives back when dropped.
915struct LayoutBuilder<'a> {
916    state: Rc<LayoutBuilderState>,
917    context: &'a AppContext,
918}
919
920impl<'a> LayoutBuilder<'a> {
921    fn new_with_epoch(
922        applier: Rc<ConcreteApplierHost<MemoryApplier>>,
923        epoch: u64,
924        context: &'a Rc<AppContext>,
925    ) -> Self {
926        Self {
927            state: Rc::new(LayoutBuilderState::new_with_epoch(
928                applier,
929                epoch,
930                Rc::clone(context),
931            )),
932            context,
933        }
934    }
935}
936
937impl Drop for LayoutBuilder<'_> {
938    fn drop(&mut self) {
939        if Rc::strong_count(&self.state) != 1 {
940            return;
941        }
942        let Ok(mut frame_arena) = self.state.frame_arena.try_borrow_mut() else {
943            return;
944        };
945        self.context
946            .replace_layout_frame_arena(std::mem::take(&mut *frame_arena));
947    }
948}
949
950struct LayoutBuilderState {
951    applier: Rc<ConcreteApplierHost<MemoryApplier>>,
952    runtime_handle: RefCell<Option<RuntimeHandle>>,
953    cache_epoch: u64,
954    cache_floor: u64,
955    frame_arena: RefCell<FrameLayoutArena>,
956    /// The context the pass lays out in, which takes the nodes whose
957    /// geometry it changed.
958    context: Rc<AppContext>,
959    /// The threshold of the measure telemetry, read once a pass: `None`
960    /// while the telemetry is off.
961    measure_telemetry_ms: Option<f64>,
962}
963
964struct LayoutRuntimeFrameBindingCleanup<'a> {
965    state: &'a RefCell<LayoutRuntimeState>,
966}
967
968impl Drop for LayoutRuntimeFrameBindingCleanup<'_> {
969    fn drop(&mut self) {
970        self.state.borrow().unbind_children();
971    }
972}
973
974enum LayoutNodeVisit<'a> {
975    Cached(Rc<MeasuredNode>),
976    /// Only nodes below this one changed: its measurement holds unless a
977    /// changed child measures differently.
978    Keep(KeptNode),
979    Measure(LayoutNodeMeasure<'a>),
980}
981
982/// A node whose measurement may hold, with the parts of it the keep reads
983/// and writes, so the keep needs no lookup of the node.
984struct KeptNode {
985    measured: Rc<MeasuredNode>,
986    parts: Rc<LayoutParts>,
987    /// Whether a child that measures to another size leaves the node as it
988    /// is.
989    ignores_child_sizes: bool,
990}
991
992/// A kept measurement after the walk over its changed children: `resized`
993/// when one of them measured to another size that the node ignores.
994struct KeptChildren {
995    measured: Rc<MeasuredNode>,
996    resized: bool,
997}
998
999/// What measuring one changed child of a kept node again needs.
1000#[derive(Clone, Copy)]
1001struct ChangedChild {
1002    constraints: Constraints,
1003    placed: bool,
1004}
1005
1006/// What the walk over a kept measurement finds of one child.
1007enum ChildChange {
1008    Unchanged,
1009    Changed(ChangedChild),
1010    /// The node has to run its policy.
1011    Refuse,
1012}
1013
1014/// A changed child of a kept node as a layout pass finds it, from a layout
1015/// node or a subcompose node.
1016struct ChildView {
1017    self_dirty: bool,
1018    /// The child's parent data, read only when the child changed itself:
1019    /// only its own modifiers give it.
1020    parent_data: Option<cranpose_ui_layout::ParentData>,
1021    /// The constraints the child's own cache holds.
1022    cached_constraints: Option<Constraints>,
1023    read_intrinsics: bool,
1024    placed: bool,
1025}
1026
1027impl ChildView {
1028    /// The child, when it or a node below it changed.
1029    fn of(node: &LiveLayoutNode<'_>) -> Option<Self> {
1030        let cache = node.cache();
1031        let dirt = cache.dirt();
1032        let self_dirty = dirt.measure || dirt.layout;
1033        if !self_dirty && !dirt.descendant.layout {
1034            return None;
1035        }
1036        Some(Self {
1037            self_dirty,
1038            parent_data: self_dirty.then(|| node.parent_data()),
1039            cached_constraints: cache.measured_constraints(),
1040            read_intrinsics: cache.has_intrinsics(),
1041            placed: node.is_placed(),
1042        })
1043    }
1044}
1045
1046struct LayoutNodeMeasure<'a> {
1047    parts: Rc<LayoutParts>,
1048    chain: ModifierChainInputs,
1049    pools: VecPools<'a>,
1050    spare: MeasuredSpare,
1051}
1052
1053#[expect(
1054    clippy::multiple_inherent_impl,
1055    reason = "the bottom-up pass, the chain measure and the subcompose measure in their files"
1056)]
1057impl LayoutBuilderState {
1058    fn new_with_epoch(
1059        applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1060        epoch: u64,
1061        context: Rc<AppContext>,
1062    ) -> Self {
1063        let runtime_handle = applier.borrow_typed().runtime_handle();
1064
1065        Self {
1066            applier,
1067            runtime_handle: RefCell::new(runtime_handle),
1068            cache_epoch: epoch,
1069            cache_floor: context.layout_cache_floor(),
1070            frame_arena: RefCell::new(context.take_layout_frame_arena()),
1071            context,
1072            measure_telemetry_ms: layout_measure_telemetry_threshold_ms(),
1073        }
1074    }
1075
1076    fn with_applier_result<R>(
1077        &self,
1078        f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1079    ) -> Result<R, NodeError> {
1080        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1081            return Err(NodeError::MissingContext {
1082                id: NodeId::default(),
1083                reason: "applier already borrowed",
1084            });
1085        };
1086        f(&mut applier)
1087    }
1088
1089    fn measure_subcompose_node(
1090        self: &Rc<Self>,
1091        node_id: NodeId,
1092        constraints: Constraints,
1093    ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1094        self.clear_subcompose_placed(node_id);
1095        if let Some(cached) = self.subcompose_measurement_to_keep(node_id, constraints)
1096            && let Some(kept) = self.keep_subcompose_measurement(node_id, constraints, &cached)?
1097        {
1098            return Ok(("subcompose kept", kept));
1099        }
1100        Ok(match self.try_measure_subcompose(node_id, constraints)? {
1101            Some(measured) => {
1102                self.store_subcompose_measurement(node_id, constraints, &measured);
1103                ("subcompose", measured)
1104            }
1105            None => (
1106                "fallback",
1107                Rc::new(MeasuredNode::new(
1108                    node_id,
1109                    Size::default(),
1110                    Point { x: 0.0, y: 0.0 },
1111                    Point::default(),
1112                    Vec::new(),
1113                )),
1114            ),
1115        })
1116    }
1117
1118    /// The measurement of a subcompose node whose own inputs did not change
1119    /// since it measured at `constraints`, as for a layout node: its policy
1120    /// does not run, so its content does not compose again.
1121    fn subcompose_measurement_to_keep(
1122        &self,
1123        node_id: NodeId,
1124        constraints: Constraints,
1125    ) -> Option<Rc<MeasuredNode>> {
1126        let mut applier = self.applier.try_borrow_typed().ok()?;
1127        applier
1128            .with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1129                if node.needs_measure() || Node::needs_layout(node) {
1130                    return None;
1131                }
1132                let cache = node.cache();
1133                if cache.epoch() < self.cache_floor {
1134                    return None;
1135                }
1136                cache.get_measurement(constraints)
1137            })
1138            .ok()
1139            .flatten()
1140    }
1141
1142    /// Keeps a subcompose node's measurement as [`Self::keep_measurement`]
1143    /// keeps a layout node's. A subcompose node holds no record of what its
1144    /// policy gave each child, so a changed child must have only changed
1145    /// nodes below it, and measures again at the constraints its own cache
1146    /// holds.
1147    fn keep_subcompose_measurement(
1148        self: &Rc<Self>,
1149        node_id: NodeId,
1150        constraints: Constraints,
1151        cached: &Rc<MeasuredNode>,
1152    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1153        let kept = self.remeasure_changed_children(
1154            cached,
1155            false,
1156            |child_id| self.subcompose_child_change(child_id),
1157            |child_id| self.mark_measured_without_policy(child_id),
1158        )?;
1159        let Some(KeptChildren { measured: kept, .. }) = kept else {
1160            return Ok(None);
1161        };
1162        self.with_applier_result(|applier| {
1163            applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1164                node.cache()
1165                    .store_measurement(constraints, Rc::clone(&kept));
1166                node.clear_needs_layout();
1167            })
1168        })?;
1169        Ok(Some(kept))
1170    }
1171
1172    /// What the walk over a kept subcompose node finds of a child. A changed
1173    /// child refuses the keep when it changed itself, a parent read its
1174    /// intrinsic sizes, or its cache holds no constraints.
1175    fn subcompose_child_change(&self, child_id: NodeId) -> Result<ChildChange, NodeError> {
1176        self.child_change(child_id, |view| {
1177            match view.cached_constraints.filter(|_| !view.self_dirty) {
1178                Some(constraints) if !view.read_intrinsics => ChildChange::Changed(ChangedChild {
1179                    constraints,
1180                    placed: view.placed,
1181                }),
1182                _ => ChildChange::Refuse,
1183            }
1184        })
1185    }
1186
1187    /// What the walk finds of a child: unchanged when neither it nor a node
1188    /// below it changed, otherwise what `decide` makes of it.
1189    fn child_change(
1190        &self,
1191        child_id: NodeId,
1192        decide: impl FnOnce(ChildView) -> ChildChange,
1193    ) -> Result<ChildChange, NodeError> {
1194        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1195            return Ok(ChildChange::Refuse);
1196        };
1197        Ok(
1198            match read_live_layout_node(&mut applier, child_id, |node| ChildView::of(&node))? {
1199                None => ChildChange::Refuse,
1200                Some(None) => ChildChange::Unchanged,
1201                Some(Some(view)) => decide(view),
1202            },
1203        )
1204    }
1205
1206    /// Marks a child a subcompose node measured again without its policy, so
1207    /// the policy checks it when it next runs: it may keep measurements of
1208    /// its own, as a lazy list keeps its items'.
1209    fn mark_measured_without_policy(&self, child_id: NodeId) {
1210        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1211            return;
1212        };
1213        if let Ok(node) = applier.get_mut(child_id) {
1214            node.mark_descendant_needs_layout(true);
1215        }
1216    }
1217
1218    fn clear_subcompose_placed(&self, node_id: NodeId) {
1219        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1220            return;
1221        };
1222        let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1223            node.clear_placed(&self.context);
1224        });
1225    }
1226
1227    fn measure_node(
1228        self: &Rc<Self>,
1229        node_id: NodeId,
1230        constraints: Constraints,
1231    ) -> Result<Rc<MeasuredNode>, NodeError> {
1232        let Some(threshold_ms) = self.measure_telemetry_ms else {
1233            return self
1234                .measure_node_untimed(node_id, constraints)
1235                .map(|(_, measured)| measured);
1236        };
1237        self.measure_node_timed(node_id, constraints, threshold_ms)
1238    }
1239
1240    // Out of line, so a measure without telemetry keeps no room for its
1241    // timing and log on the stack at each level of the tree.
1242    #[inline(never)]
1243    fn measure_node_timed(
1244        self: &Rc<Self>,
1245        node_id: NodeId,
1246        constraints: Constraints,
1247        threshold_ms: f64,
1248    ) -> Result<Rc<MeasuredNode>, NodeError> {
1249        let start = Instant::now();
1250        let (kind, measured) = self.measure_node_untimed(node_id, constraints)?;
1251        log_node_measure_telemetry(kind, node_id, constraints, &measured, (threshold_ms, start));
1252        Ok(measured)
1253    }
1254
1255    /// The measurement of `node_id` and the kind of node it is.
1256    fn measure_node_untimed(
1257        self: &Rc<Self>,
1258        node_id: NodeId,
1259        constraints: Constraints,
1260    ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1261        if let Some(measured) = self.measure_layout_node(node_id, constraints)? {
1262            return Ok(("layout", measured));
1263        }
1264        self.measure_subcompose_node(node_id, constraints)
1265    }
1266
1267    fn try_measure_subcompose(
1268        self: &Rc<Self>,
1269        node_id: NodeId,
1270        constraints: Constraints,
1271    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1272        let (node_handle, resolved_modifiers, spare) = {
1273            let Ok(mut applier) = self.applier.try_borrow_typed() else {
1274                return Ok(None);
1275            };
1276            let node = match applier.get_mut(node_id) {
1277                Ok(node) => node,
1278                Err(NodeError::Missing { .. }) => return Ok(None),
1279                Err(err) => return Err(err),
1280            };
1281            let any = node.as_any_mut();
1282            if let Some(subcompose) = any.downcast_mut::<SubcomposeLayoutNode>() {
1283                let handle = subcompose.handle();
1284                let resolved_modifiers = handle
1285                    .resolved_modifiers()
1286                    .on_device_grid(subcompose.density().density());
1287                let spare = MeasuredSpare::take(subcompose.cache());
1288                (handle, resolved_modifiers, spare)
1289            } else {
1290                return Ok(None);
1291            }
1292        };
1293
1294        let props = resolved_modifiers.layout_properties();
1295        let padding = resolved_modifiers.padding();
1296        let offset = resolved_modifiers.offset();
1297        let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1298
1299        if let DimensionConstraint::Points(width) = props.width() {
1300            let constrained_width = width - padding.horizontal_sum();
1301            inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1302            inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1303        }
1304        if let DimensionConstraint::Points(height) = props.height() {
1305            let constrained_height = height - padding.vertical_sum();
1306            inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1307            inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1308        }
1309
1310        let measure_error = RefCell::new(None);
1311        let measured_children = node_handle.measured_children_scratch();
1312        let measure_result = self.subcompose_policy_result(
1313            &node_handle,
1314            node_id,
1315            inner_constraints,
1316            &measured_children,
1317            &measure_error,
1318        )?;
1319
1320        if let Some(err) = measure_error.borrow_mut().take() {
1321            return Err(err);
1322        }
1323
1324        let cranpose_ui_layout::MeasureResult {
1325            size: measured_size,
1326            alignment_lines: explicit_lines,
1327            placements,
1328        } = measure_result;
1329
1330        let mut width = measured_size.width + padding.horizontal_sum();
1331        let mut height = measured_size.height + padding.vertical_sum();
1332
1333        width = resolve_dimension(
1334            width,
1335            props.width(),
1336            props.min_width(),
1337            props.max_width(),
1338            constraints.min_width,
1339            constraints.max_width,
1340        );
1341        height = resolve_dimension(
1342            height,
1343            props.height(),
1344            props.min_height(),
1345            props.max_height(),
1346            constraints.min_height,
1347            constraints.max_height,
1348        );
1349
1350        let MeasuredSpare {
1351            node: spare,
1352            mut children,
1353        } = spare;
1354        children.reserve_exact(placements.len());
1355        let mut alignment_lines = AlignmentLines::default();
1356        let mut measured_children_by_id = measured_children.borrow_mut();
1357
1358        // One borrow of the applier places every child; measuring a child
1359        // the pass did not measure borrows it on its own.
1360        let mut applier = self.applier.try_borrow_typed().ok();
1361        if let Some(applier) = applier.as_mut() {
1362            let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1363                parent_node.set_measured_size(Size { width, height }, &self.context);
1364                parent_node
1365                    .layout_parts()
1366                    .note_child_placements(&placements);
1367                parent_node.clear_needs_measure();
1368                parent_node.clear_needs_layout();
1369            });
1370        }
1371
1372        // A lazy policy placed its items' roots with their measurements; the
1373        // other placements are looked up.
1374        let placed_roots = node_handle.take_placed_roots();
1375        let mut carried = PlacedRootMeasurements::new(&placed_roots);
1376        for placement in &placements {
1377            let looked_up;
1378            let child: &MeasuredNode = if let Some(measured) = carried.next_measurement() {
1379                measured
1380            } else {
1381                looked_up =
1382                    if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1383                        measured
1384                    } else {
1385                        drop(applier.take());
1386                        let measured = self.measure_node(placement.node_id, inner_constraints)?;
1387                        applier = self.applier.try_borrow_typed().ok();
1388                        measured
1389                    };
1390                &looked_up
1391            };
1392            let policy_position = Point {
1393                x: padding.left + placement.x,
1394                y: padding.top + placement.y,
1395            };
1396            let retained_position = Point {
1397                x: policy_position.x + child.offset.x,
1398                y: policy_position.y + child.offset.y,
1399            };
1400            let retained = ChildPlacement::new(retained_position, placement.z_index);
1401            alignment_lines.merge(
1402                child
1403                    .alignment_lines_for_parent()
1404                    .translated(policy_position.y),
1405            );
1406
1407            if let Some(applier) = applier.as_mut() {
1408                let _ = read_live_layout_node(applier, placement.node_id, |node| {
1409                    node.place(retained, node_id, &self.context);
1410                });
1411            }
1412
1413            children.push(MeasuredChild::new(child, policy_position));
1414        }
1415        drop(applier);
1416
1417        node_handle.recycle_placed_roots(placed_roots);
1418        node_handle.recycle_placement_scratch(placements);
1419
1420        Ok(Some(
1421            MeasuredNode::new(
1422                node_id,
1423                Size { width, height },
1424                offset,
1425                Point::default(),
1426                children,
1427            )
1428            .with_alignment_lines(
1429                alignment_lines
1430                    .with_overrides(explicit_lines.translated(padding.top))
1431                    .translated(offset.y),
1432            )
1433            .into_rc(spare),
1434        ))
1435    }
1436
1437    fn measure_layout_node(
1438        self: &Rc<Self>,
1439        node_id: NodeId,
1440        constraints: Constraints,
1441    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1442        let mut keep = true;
1443        let (mut applier, measure) = loop {
1444            let Ok(mut applier) = self.applier.try_borrow_typed() else {
1445                return Ok(None);
1446            };
1447            match applier.with_node::<LayoutNode, _>(node_id, |node| {
1448                self.visit_layout_node(node, constraints, keep)
1449            }) {
1450                Ok(LayoutNodeVisit::Measure(measure)) => break (applier, measure),
1451                Ok(LayoutNodeVisit::Cached(measured)) => return Ok(Some(measured)),
1452                Ok(LayoutNodeVisit::Keep(node)) => {
1453                    drop(applier);
1454                    if let Some(kept) = self.keep_measurement(constraints, node)? {
1455                        return Ok(Some(kept));
1456                    }
1457                    keep = false;
1458                }
1459                Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
1460                Err(err) => return Err(err),
1461            }
1462        };
1463        let LayoutNodeMeasure {
1464            parts,
1465            chain,
1466            mut pools,
1467            spare,
1468        } = measure;
1469
1470        let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
1471            state: parts.runtime_state(),
1472        };
1473        self.bind_layout_children(
1474            &mut applier,
1475            (node_id, &parts),
1476            &pools.child_ids,
1477            ChildPass::Measure,
1478        )?;
1479        drop(applier);
1480
1481        let runtime_state = parts.runtime_state().borrow();
1482        let measurement = self.measure_through_modifier_chain(
1483            node_id,
1484            &runtime_state,
1485            chain,
1486            constraints,
1487            &mut pools.placements,
1488            &mut pools.placement_indices,
1489        );
1490
1491        if let Some(err) = runtime_state.take_child_error() {
1492            for child_state in runtime_state.child_states() {
1493                child_state.measured.take();
1494            }
1495            if runtime_state.take_moved_coordinators() {
1496                let mut layout_state = parts.layout_state().borrow_mut();
1497                layout_state.note_geometry_change(node_id, &self.context);
1498            }
1499            return Err(err);
1500        }
1501
1502        let MeasuredSpare {
1503            node: spare,
1504            mut children,
1505        } = spare;
1506        runtime_state.measured_children(
1507            &pools.placements,
1508            &pools.placement_indices,
1509            measurement.content_offset,
1510            &mut children,
1511        );
1512        let measured = MeasuredNode::new(
1513            node_id,
1514            measurement.size,
1515            measurement.offset,
1516            measurement.content_offset,
1517            children,
1518        )
1519        .with_alignment_lines(measurement.alignment_lines)
1520        .with_window_root(measurement.window_root)
1521        .into_rc(spare);
1522
1523        // The node's parts hold all its measure writes: no lookup of it.
1524        let cache = parts.cache();
1525        cache.store_held_measurement(
1526            constraints,
1527            Rc::clone(&measured),
1528            measurement.hold,
1529            measurement.ignores_child_sizes,
1530        );
1531        let mut layout_state = parts.layout_state().borrow_mut();
1532        if runtime_state.take_moved_coordinators() {
1533            layout_state.note_geometry_change(node_id, &self.context);
1534        }
1535        cache.clear_needs_measure();
1536        cache.clear_layout_dirt();
1537        layout_state.set_size(measurement.size, &self.context);
1538        layout_state.set_content_offset(measurement.content_offset);
1539
1540        Ok(Some(measured))
1541    }
1542
1543    /// The intrinsic size `kind` names of a layout node, through its modifier
1544    /// chain and measure policy, as Compose computes it: no measure, no
1545    /// placement, and the node's measurement cache stays as it was. `None`
1546    /// when the node is not a layout node.
1547    fn layout_node_intrinsic(
1548        self: &Rc<Self>,
1549        node_id: NodeId,
1550        kind: IntrinsicKind,
1551    ) -> Result<Option<f32>, NodeError> {
1552        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1553            return Ok(None);
1554        };
1555        let mut pools = VecPools::acquire(&self.frame_arena);
1556        let bound = applier.with_node::<LayoutNode, _>(node_id, |node| {
1557            let parts = Rc::clone(node.layout_parts());
1558            let chain = parts.runtime_state().borrow_mut().bind_node(node);
1559            pools.child_ids.extend_from_slice(&node.children);
1560            (parts, chain)
1561        });
1562        let (parts, chain) = match bound {
1563            Ok(bound) => bound,
1564            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
1565            Err(err) => return Err(err),
1566        };
1567        let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
1568            state: parts.runtime_state(),
1569        };
1570        self.bind_layout_children(
1571            &mut applier,
1572            (node_id, &parts),
1573            &pools.child_ids,
1574            ChildPass::Intrinsics,
1575        )?;
1576        drop(applier);
1577
1578        let runtime_state = parts.runtime_state().borrow();
1579        let value = if chain.uses_chain {
1580            let scope = crate::density::DensityMeasureScope::new(chain.density);
1581            let frame = CoordinatorFrame::new(
1582                &runtime_state.measure_policy,
1583                &scope,
1584                runtime_state.child_measurables(),
1585                runtime_state.child_states(),
1586                &mut pools.placements,
1587                &mut pools.placement_indices,
1588            );
1589            runtime_state
1590                .coordinator_chain
1591                .intrinsic_from(0, &frame, kind)
1592        } else {
1593            policy_intrinsic(
1594                runtime_state.measure_policy.as_ref(),
1595                runtime_state.child_measurables(),
1596                kind,
1597            )
1598        };
1599        match runtime_state.take_child_error() {
1600            Some(err) => Err(err),
1601            None => Ok(Some(value)),
1602        }
1603    }
1604
1605    /// The measurement of a node whose own inputs did not change since it
1606    /// measured at `constraints`: only nodes below it are dirty.
1607    fn measurement_to_keep(
1608        &self,
1609        node: &LayoutNode,
1610        constraints: Constraints,
1611    ) -> Option<Rc<MeasuredNode>> {
1612        if node.needs_measure() || node.needs_layout() || node.is_virtual() {
1613            return None;
1614        }
1615        let cache = node.cache();
1616        if cache.epoch() < self.cache_floor {
1617            return None;
1618        }
1619        cache.get_measurement(constraints)
1620    }
1621
1622    // Measures again the children of `node_id` that changed, at their last
1623    // constraints, and keeps the node's measurement when each measures the
1624    // same for its parent; `None` when the node has to measure again. Out of
1625    // line: few frames keep a measurement, and the measure stays small.
1626    #[inline(never)]
1627    fn keep_measurement(
1628        self: &Rc<Self>,
1629        constraints: Constraints,
1630        node: KeptNode,
1631    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1632        let KeptNode {
1633            measured,
1634            parts,
1635            ignores_child_sizes,
1636        } = node;
1637        let mut next_state = 0;
1638        let kept = self.remeasure_changed_children(
1639            &measured,
1640            ignores_child_sizes,
1641            |child_id| self.layout_child_change(parts.runtime_state(), &mut next_state, child_id),
1642            |_| {},
1643        )?;
1644        let Some(KeptChildren {
1645            measured: kept,
1646            resized,
1647        }) = kept
1648        else {
1649            return Ok(None);
1650        };
1651        let cache = parts.cache();
1652        if resized {
1653            cache.store_resized_measurement(constraints, Rc::clone(&kept));
1654        } else {
1655            cache.store_measurement(constraints, Rc::clone(&kept));
1656        }
1657        cache.clear_layout_dirt();
1658        Ok(Some(kept))
1659    }
1660
1661    /// Walks the children of a kept measurement, measures again each one
1662    /// `change` finds changed, at the constraints it last had, and keeps the
1663    /// measurement when each of them reads the same for it, or, when the
1664    /// measurement `ignores_child_sizes`, the same but for its size. `None`
1665    /// as soon as `change` refuses a child or one measures differently for
1666    /// its parent.
1667    fn remeasure_changed_children(
1668        self: &Rc<Self>,
1669        cached: &Rc<MeasuredNode>,
1670        ignores_child_sizes: bool,
1671        mut change: impl FnMut(NodeId) -> Result<ChildChange, NodeError>,
1672        mut remeasured: impl FnMut(NodeId),
1673    ) -> Result<Option<KeptChildren>, NodeError> {
1674        let mut resized = false;
1675        for previous in &cached.children {
1676            let child = match change(previous.node_id)? {
1677                ChildChange::Unchanged => continue,
1678                ChildChange::Refuse => return Ok(None),
1679                ChildChange::Changed(child) => child,
1680            };
1681            let measured = self.measure_node(previous.node_id, child.constraints)?;
1682            remeasured(previous.node_id);
1683            let now = ForParent::of(&measured);
1684            if !previous.for_parent.same_as(&now) {
1685                if !(ignores_child_sizes && previous.for_parent.same_but_size(&now)) {
1686                    return Ok(None);
1687                }
1688                resized = true;
1689            }
1690            if child.placed {
1691                self.place_where_it_was(cached.node_id, previous.node_id);
1692            }
1693        }
1694        Ok(Some(KeptChildren {
1695            measured: Rc::clone(cached),
1696            resized,
1697        }))
1698    }
1699
1700    /// What the walk over a kept layout node finds of a child, from the
1701    /// record of the node's last measure, which `next_state` walks in order.
1702    /// A changed child refuses the keep when its parent data changed, the
1703    /// node read its intrinsic sizes, or the node did not measure it.
1704    fn layout_child_change(
1705        &self,
1706        runtime_state: &RefCell<LayoutRuntimeState>,
1707        next_state: &mut usize,
1708        child_id: NodeId,
1709    ) -> Result<ChildChange, NodeError> {
1710        let runtime_state = runtime_state.borrow();
1711        let Some((offset, state)) = runtime_state
1712            .child_states()
1713            .get(*next_state..)
1714            .unwrap_or_default()
1715            .iter()
1716            .enumerate()
1717            .find(|(_, state)| state.node_id == child_id)
1718        else {
1719            return Ok(ChildChange::Refuse);
1720        };
1721        *next_state += offset + 1;
1722        // A clean child's flags sit in the cache its record shares with it.
1723        if read_cell(&state.parts, |parts| {
1724            parts
1725                .as_ref()
1726                .is_some_and(|parts| !parts.cache().dirt().any())
1727        }) {
1728            return Ok(ChildChange::Unchanged);
1729        }
1730        self.child_change(child_id, |view| {
1731            let parent_data_changed = view.parent_data.as_ref().is_some_and(|data| {
1732                state.reads_parent_data_change(&*runtime_state.measure_policy, data)
1733            });
1734            match state.measured_constraints.get() {
1735                Some(constraints) if !state.read_intrinsics.get() && !parent_data_changed => {
1736                    ChildChange::Changed(ChangedChild {
1737                        constraints,
1738                        placed: view.placed,
1739                    })
1740                }
1741                _ => ChildChange::Refuse,
1742            }
1743        })
1744    }
1745
1746    /// Stores a subcompose node's new measurement in its cache, as a layout
1747    /// node's measure stores its own.
1748    fn store_subcompose_measurement(
1749        &self,
1750        node_id: NodeId,
1751        constraints: Constraints,
1752        measured: &Rc<MeasuredNode>,
1753    ) {
1754        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1755            return;
1756        };
1757        let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1758            node.cache()
1759                .store_measurement(constraints, Rc::clone(measured));
1760        });
1761    }
1762
1763    /// Places a child of the kept node `parent` where it was: the node's
1764    /// policy would place it there again.
1765    fn place_where_it_was(&self, parent: NodeId, child_id: NodeId) {
1766        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1767            return;
1768        };
1769        let _ = read_live_layout_node(&mut applier, child_id, |child| {
1770            child.stay_placed(parent, &self.context);
1771        });
1772    }
1773
1774    fn visit_layout_node(
1775        &self,
1776        node: &mut LayoutNode,
1777        constraints: Constraints,
1778        keep: bool,
1779    ) -> LayoutNodeVisit<'_> {
1780        node.clear_placed(&self.context);
1781        if keep && let Some(measured) = self.measurement_to_keep(node, constraints) {
1782            return LayoutNodeVisit::Keep(KeptNode {
1783                ignores_child_sizes: node.cache().ignores_child_sizes_of(&measured),
1784                measured,
1785                parts: Rc::clone(node.layout_parts()),
1786            });
1787        }
1788        let cache = node.cache();
1789        cache.activate(self.cache_epoch);
1790        if !Node::layout_dirty(node)
1791            && let Some(cached) = cache.get_measurement(constraints)
1792        {
1793            node.clear_needs_measure();
1794            node.clear_needs_layout();
1795            return LayoutNodeVisit::Cached(cached);
1796        }
1797
1798        let spare = MeasuredSpare::take(cache);
1799        let parts = Rc::clone(node.layout_parts());
1800        let chain = parts.runtime_state().borrow_mut().bind_node(node);
1801        let mut pools = VecPools::acquire(&self.frame_arena);
1802        pools.child_ids.extend_from_slice(&node.children);
1803        LayoutNodeVisit::Measure(LayoutNodeMeasure {
1804            parts,
1805            chain,
1806            pools,
1807            spare,
1808        })
1809    }
1810}
1811
1812#[derive(Debug, Clone)]
1813pub(crate) struct MeasuredNode {
1814    node_id: NodeId,
1815    size: Size,
1816    offset: Point,
1817    content_offset: Point,
1818    alignment_lines: AlignmentLines,
1819    children: Vec<MeasuredChild>,
1820    window_root: bool,
1821}
1822
1823impl MeasuredNode {
1824    fn new(
1825        node_id: NodeId,
1826        size: Size,
1827        offset: Point,
1828        content_offset: Point,
1829        children: Vec<MeasuredChild>,
1830    ) -> Self {
1831        Self {
1832            node_id,
1833            size,
1834            offset,
1835            content_offset,
1836            alignment_lines: AlignmentLines::default(),
1837            children,
1838            window_root: false,
1839        }
1840    }
1841
1842    fn with_window_root(mut self, window_root: bool) -> Self {
1843        self.window_root = window_root;
1844        self
1845    }
1846
1847    fn with_alignment_lines(mut self, alignment_lines: AlignmentLines) -> Self {
1848        self.alignment_lines = alignment_lines;
1849        self
1850    }
1851
1852    pub(crate) fn alignment_lines_for_parent(&self) -> AlignmentLines {
1853        if self.window_root {
1854            AlignmentLines::default()
1855        } else {
1856            self.alignment_lines
1857        }
1858    }
1859
1860    pub(crate) fn size_for_parent(&self) -> Size {
1861        if self.window_root {
1862            Size::new(0.0, 0.0)
1863        } else {
1864            self.size
1865        }
1866    }
1867
1868    pub(crate) fn size(&self) -> Size {
1869        self.size
1870    }
1871
1872    /// The measurement in `spare`'s memory, or in a new allocation when
1873    /// there is no spare.
1874    fn into_rc(self, spare: Option<Rc<Self>>) -> Rc<Self> {
1875        let Some(mut spare) = spare else {
1876            return Rc::new(self);
1877        };
1878        match Rc::get_mut(&mut spare) {
1879            Some(slot) => *slot = self,
1880            None => return Rc::new(self),
1881        }
1882        spare
1883    }
1884}
1885
1886/// What a parent's measurement keeps of a child: which child, where the
1887/// policy placed it, and what of the child's measurement the policy read.
1888/// The measurement itself stays with the child's cache, its one holder, so
1889/// the child's next measure can fill it in place.
1890#[derive(Debug, Clone, Copy)]
1891struct MeasuredChild {
1892    node_id: NodeId,
1893    offset: Point,
1894    for_parent: ForParent,
1895}
1896
1897impl MeasuredChild {
1898    fn new(measured: &MeasuredNode, offset: Point) -> Self {
1899        Self {
1900            node_id: measured.node_id,
1901            offset,
1902            for_parent: ForParent::of(measured),
1903        }
1904    }
1905}
1906
1907/// What a parent's policy reads of a child's measurement: the size and the
1908/// baselines, and the offset it places the child by.
1909#[derive(Debug, Clone, Copy)]
1910struct ForParent {
1911    size: Size,
1912    offset: Point,
1913    alignment_lines: AlignmentLines,
1914}
1915
1916impl ForParent {
1917    fn of(measured: &MeasuredNode) -> Self {
1918        Self {
1919            size: measured.size_for_parent(),
1920            offset: measured.offset,
1921            alignment_lines: measured.alignment_lines_for_parent(),
1922        }
1923    }
1924
1925    /// Whether a parent's policy, reading `other` in place of this, would
1926    /// lay its children out the same.
1927    fn same_as(&self, other: &Self) -> bool {
1928        self.size == other.size && self.same_but_size(other)
1929    }
1930
1931    /// Whether `other` reads the same as this but for its size.
1932    fn same_but_size(&self, other: &Self) -> bool {
1933        self.offset == other.offset
1934            && self.alignment_lines.first_baseline() == other.alignment_lines.first_baseline()
1935            && self.alignment_lines.last_baseline() == other.alignment_lines.last_baseline()
1936    }
1937}
1938
1939/// The intrinsic size `kind` names, of a measure policy over `measurables`.
1940fn policy_intrinsic(
1941    policy: &dyn MeasurePolicy,
1942    measurables: &[Box<dyn Measurable>],
1943    kind: IntrinsicKind,
1944) -> f32 {
1945    match kind {
1946        IntrinsicKind::MinWidth(height) => policy.min_intrinsic_width(measurables, height),
1947        IntrinsicKind::MaxWidth(height) => policy.max_intrinsic_width(measurables, height),
1948        IntrinsicKind::MinHeight(width) => policy.min_intrinsic_height(measurables, width),
1949        IntrinsicKind::MaxHeight(width) => policy.max_intrinsic_height(measurables, width),
1950    }
1951}
1952
1953pub(crate) struct LayoutRuntimeState {
1954    /// `None` until the node first binds a child: most nodes are leaves.
1955    children: Option<Box<LayoutChildren>>,
1956    coordinator_chain: CoordinatorChain,
1957    measure_policy: Rc<dyn MeasurePolicy>,
1958}
1959
1960impl LayoutRuntimeState {
1961    pub(crate) fn new(measure_policy: Rc<dyn MeasurePolicy>) -> Self {
1962        Self {
1963            children: None,
1964            coordinator_chain: CoordinatorChain::default(),
1965            measure_policy,
1966        }
1967    }
1968
1969    fn bind_node(&mut self, node: &LayoutNode) -> ModifierChainInputs {
1970        if !Rc::ptr_eq(&self.measure_policy, &node.measure_policy) {
1971            self.measure_policy = Rc::clone(&node.measure_policy);
1972        }
1973        self.coordinator_chain.sync(node)
1974    }
1975
1976    // Notes that a measure placed the node's coordinators, and says whether
1977    // one of them moved since the scene last heard of the node. A node
1978    // without layout modifiers moves only with its size, which the node
1979    // reports itself.
1980    fn take_moved_coordinators(&self) -> bool {
1981        self.coordinator_chain.take_moved()
1982    }
1983
1984    pub(crate) fn placed_coordinator_rect(
1985        &self,
1986        ordinal: usize,
1987        node_size: Size,
1988    ) -> Option<GeometryRect> {
1989        self.coordinator_chain.placed_rect(ordinal, node_size)
1990    }
1991
1992    #[cfg(test)]
1993    pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
1994        LayoutRuntimeDebugStats {
1995            child_ids: self
1996                .children
1997                .as_ref()
1998                .map(|children| children.ids.clone())
1999                .unwrap_or_default(),
2000            child_state_ptrs: self
2001                .child_states()
2002                .iter()
2003                .map(|state| Rc::as_ptr(state) as *const () as usize)
2004                .collect(),
2005            child_measurable_ptrs: self
2006                .child_measurables()
2007                .iter()
2008                .map(|measurable| {
2009                    measurable.as_ref() as *const dyn Measurable as *const () as usize
2010                })
2011                .collect(),
2012            child_measurable_count: self.child_measurables().len(),
2013            coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
2014            coordinator_node_count: self.coordinator_chain.debug_ptrs().len(),
2015        }
2016    }
2017}
2018
2019#[cfg(test)]
2020#[derive(Debug, Clone, PartialEq, Eq)]
2021pub(crate) struct LayoutRuntimeDebugStats {
2022    pub(crate) child_ids: Vec<NodeId>,
2023    pub(crate) child_state_ptrs: Vec<usize>,
2024    pub(crate) child_measurable_ptrs: Vec<usize>,
2025    pub(crate) child_measurable_count: usize,
2026    pub(crate) coordinator_node_ptrs: Vec<usize>,
2027    pub(crate) coordinator_node_count: usize,
2028}
2029
2030impl PlaceTarget for LayoutChildMeasureState {
2031    fn place(&self, x: f32, y: f32) {
2032        let internal_offset = read_cell(&self.measured, |measured| {
2033            measured
2034                .as_ref()
2035                .map(|measured| measured.offset)
2036                .unwrap_or_default()
2037        });
2038        self.place_retained(Point {
2039            x: x + internal_offset.x,
2040            y: y + internal_offset.y,
2041        });
2042    }
2043}
2044
2045/// Runs `visit` on the measurement `child` holds in its cache, or on a leaf
2046/// of no size for a child that holds none, as a node of another kind
2047/// measures.
2048fn visit_child<R>(
2049    applier: &mut MemoryApplier,
2050    child: &MeasuredChild,
2051    visit: impl FnOnce(&mut MemoryApplier, &MeasuredNode) -> R,
2052) -> R {
2053    let held = applier.get_mut(child.node_id).ok().and_then(|node| {
2054        let any = node.as_any_mut();
2055        if let Some(layout) = any.downcast_ref::<LayoutNode>() {
2056            return layout.cache().held_measurement();
2057        }
2058        any.downcast_ref::<SubcomposeLayoutNode>()
2059            .and_then(|subcompose| subcompose.cache().held_measurement())
2060    });
2061    match held {
2062        Some(held) => visit(applier, &held),
2063        None => visit(
2064            applier,
2065            &MeasuredNode::new(
2066                child.node_id,
2067                Size::default(),
2068                Point::default(),
2069                Point::default(),
2070                Vec::new(),
2071            ),
2072        ),
2073    }
2074}
2075
2076fn clear_semantics_dirty_flags(
2077    applier: &mut MemoryApplier,
2078    node: &MeasuredNode,
2079) -> Result<(), NodeError> {
2080    match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
2081        layout.clear_needs_semantics();
2082    }) {
2083        Ok(()) => {}
2084        Err(NodeError::Missing { .. }) => {}
2085        Err(NodeError::TypeMismatch { .. }) => {
2086            match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
2087                subcompose.clear_needs_semantics();
2088            }) {
2089                Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
2090                Err(err) => return Err(err),
2091            }
2092        }
2093        Err(err) => return Err(err),
2094    }
2095
2096    for child in &node.children {
2097        visit_child(applier, child, clear_semantics_dirty_flags)?;
2098    }
2099
2100    Ok(())
2101}
2102
2103fn build_semantics_tree_from_live_nodes(
2104    applier: &mut MemoryApplier,
2105    node: &MeasuredNode,
2106) -> Result<SemanticsTree, NodeError> {
2107    Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
2108        applier, node, None,
2109    )?))
2110}
2111
2112fn semantics_node_from_parts(
2113    node_id: NodeId,
2114    node_generation: u32,
2115    mut role: SemanticsRole,
2116    config: Option<SemanticsConfiguration>,
2117    children: Vec<SemanticsNode>,
2118    held_details: Option<Box<SemanticsDetails>>,
2119    bounds: GeometryRect,
2120) -> SemanticsNode {
2121    let focusable = crate::focus_dispatch::has_focus_target(node_id);
2122    let mut node = SemanticsNode {
2123        node_id,
2124        bounds,
2125        node_generation,
2126        children,
2127        focusable,
2128        focused: focusable && crate::focus_dispatch::active_focus_target() == Some(node_id),
2129        details: held_details,
2130        ..SemanticsNode::default()
2131    };
2132    let details = match config {
2133        Some(mut config) => {
2134            if config.role == Some(SemanticsWidgetRole::Button) {
2135                role = SemanticsRole::Button;
2136            }
2137            if config.is_activatable() {
2138                node.actions.push(SemanticsAction::Click {
2139                    handler: SemanticsCallback::new(node_id),
2140                });
2141            }
2142            let on_click_label = config.on_click_label.take().or_else(|| {
2143                config
2144                    .on_click
2145                    .as_ref()
2146                    .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
2147            });
2148            node.widget_role = config.role;
2149            node.description = config.content_description.take();
2150            node.on_click = config.on_click.take();
2151            node.selected = config.selected;
2152            node.toggled = config.toggled;
2153            node.enabled = config.enabled;
2154            node.hidden = config.hidden;
2155            node.merge_descendants = config.merge_descendants;
2156            node.traversal_index = config.traversal_index;
2157            node.text = config.text.take();
2158            let is_modal = config.is_modal
2159                && modal_takes_space(Size {
2160                    width: bounds.width,
2161                    height: bounds.height,
2162                });
2163            SemanticsDetails::from_configuration(config, on_click_label, is_modal)
2164        }
2165        None => SemanticsDetails::NONE,
2166    };
2167    node.set_details(details);
2168    node.role = role;
2169    node
2170}
2171
2172#[inline(never)]
2173fn build_semantics_node_from_live_nodes(
2174    applier: &mut MemoryApplier,
2175    node: &MeasuredNode,
2176    origin: Option<Point>,
2177) -> Result<SemanticsNode, NodeError> {
2178    let (role, config, (bounds, content), placement) =
2179        match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
2180            let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
2181            let config = layout.semantics_configuration();
2182            layout.clear_needs_semantics();
2183            let state = layout.layout_state();
2184            (
2185                role,
2186                config,
2187                semantics_placement(&state, origin),
2188                SemanticsPlacement::of(&state),
2189            )
2190        }) {
2191            Ok(data) => data,
2192            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
2193                match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
2194                    subcompose.clear_needs_semantics();
2195                    let state = subcompose.layout_state();
2196                    (
2197                        SemanticsRole::Subcompose,
2198                        subcompose.semantics_configuration(),
2199                        semantics_placement(&state, origin),
2200                        SemanticsPlacement::of(&state),
2201                    )
2202                }) {
2203                    Ok(data) => data,
2204                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
2205                        let top_left = origin.unwrap_or_default();
2206                        (
2207                            SemanticsRole::Unknown,
2208                            None,
2209                            (
2210                                GeometryRect::from_origin_size(top_left, node.size),
2211                                top_left,
2212                            ),
2213                            SemanticsPlacement::default(),
2214                        )
2215                    }
2216                    Err(err) => return Err(err),
2217                }
2218            }
2219            Err(err) => return Err(err),
2220        };
2221
2222    let mut children = Vec::with_capacity(node.children.len());
2223    for child in &node.children {
2224        children.push(visit_child(applier, child, |applier, child| {
2225            build_semantics_node_from_live_nodes(applier, child, Some(content))
2226        })?);
2227    }
2228
2229    Ok(SemanticsNode {
2230        placement,
2231        ..semantics_node_from_parts(
2232            node.node_id,
2233            applier.node_generation(node.node_id),
2234            role,
2235            config,
2236            children,
2237            None,
2238            bounds,
2239        )
2240    })
2241}
2242
2243fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
2244    stats.semantics_node_count += 1;
2245    stats.semantics_action_count += node.actions.len();
2246    stats.semantics_action_capacity += node.actions.capacity();
2247    stats.semantics_child_count += node.children.len();
2248    stats.semantics_child_capacity += node.children.capacity();
2249    stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
2250    stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
2251    if node.details.is_some() {
2252        stats.semantics_heap_bytes += size_of::<SemanticsDetails>();
2253    }
2254
2255    if let Some(description) = &node.description {
2256        stats.semantics_description_count += 1;
2257        stats.semantics_description_bytes += description.capacity();
2258        stats.semantics_heap_bytes += description.capacity();
2259    }
2260
2261    for child in &node.children {
2262        record_semantics_allocation_stats(child, stats);
2263    }
2264}
2265
2266fn record_layout_box_allocation_stats(
2267    layout_box: &LayoutBox,
2268    stats: &mut LayoutAllocationDebugStats,
2269) {
2270    stats.layout_box_count += 1;
2271    stats.layout_box_child_count += layout_box.children.len();
2272    stats.layout_box_child_capacity += layout_box.children.capacity();
2273    stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
2274    stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
2275
2276    for child in &layout_box.children {
2277        record_layout_box_allocation_stats(child, stats);
2278    }
2279}
2280
2281fn build_layout_tree(
2282    applier: &mut MemoryApplier,
2283    node: &MeasuredNode,
2284) -> Result<LayoutTree, NodeError> {
2285    fn place(
2286        applier: &mut MemoryApplier,
2287        node: &MeasuredNode,
2288        origin: Point,
2289        parent_transform: ProjectiveTransform,
2290    ) -> Result<LayoutBox, NodeError> {
2291        let top_left = Point {
2292            x: origin.x + node.offset.x,
2293            y: origin.y + node.offset.y,
2294        };
2295        let rect = GeometryRect {
2296            x: top_left.x,
2297            y: top_left.y,
2298            width: node.size.width,
2299            height: node.size.height,
2300        };
2301        let (data, window_transform) =
2302            snapshot_node_data(applier, node.node_id, top_left, node.size, parent_transform)?;
2303        let mut children = Vec::with_capacity(node.children.len());
2304        for child in &node.children {
2305            if crate::modifier::is_window_root(applier, child.node_id) {
2306                continue;
2307            }
2308            let child_origin = Point {
2309                x: top_left.x + child.offset.x,
2310                y: top_left.y + child.offset.y,
2311            };
2312            children.push(visit_child(applier, child, |applier, child| {
2313                place(applier, child, child_origin, window_transform)
2314            })?);
2315        }
2316        Ok(LayoutBox {
2317            node_generation: applier.node_generation(node.node_id),
2318            ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
2319        })
2320    }
2321
2322    Ok(LayoutTree::new(place(
2323        applier,
2324        node,
2325        Point { x: 0.0, y: 0.0 },
2326        ProjectiveTransform::identity(),
2327    )?))
2328}
2329
2330fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
2331    match &layout_box.node_data.kind {
2332        LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
2333        LayoutNodeKind::Spacer => SemanticsRole::Spacer,
2334        LayoutNodeKind::Unknown => SemanticsRole::Unknown,
2335        LayoutNodeKind::Button { .. } => SemanticsRole::Button,
2336        LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
2337    }
2338}
2339
2340#[inline(never)]
2341fn build_semantics_node_from_layout_box(layout_box: &LayoutBox, origin: Point) -> SemanticsNode {
2342    let rect = layout_box.rect;
2343    let content = Point {
2344        x: rect.x + layout_box.content_offset.x,
2345        y: rect.y + layout_box.content_offset.y,
2346    };
2347    let children = layout_box
2348        .children
2349        .iter()
2350        .map(|child| build_semantics_node_from_layout_box(child, content))
2351        .collect();
2352
2353    SemanticsNode {
2354        placement: SemanticsPlacement {
2355            position: Point {
2356                x: rect.x - origin.x,
2357                y: rect.y - origin.y,
2358            },
2359            content_offset: layout_box.content_offset,
2360        },
2361        ..semantics_node_from_parts(
2362            layout_box.node_id,
2363            layout_box.node_generation,
2364            semantics_role_from_layout_box(layout_box),
2365            layout_box.node_data.semantics().cloned(),
2366            children,
2367            None,
2368            rect,
2369        )
2370    }
2371}
2372
2373fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
2374    let horizontal = padding.horizontal_sum();
2375    let vertical = padding.vertical_sum();
2376    let min_width = (constraints.min_width - horizontal).max(0.0);
2377    let mut max_width = constraints.max_width;
2378    if max_width.is_finite() {
2379        max_width = (max_width - horizontal).max(0.0);
2380    }
2381    let min_height = (constraints.min_height - vertical).max(0.0);
2382    let mut max_height = constraints.max_height;
2383    if max_height.is_finite() {
2384        max_height = (max_height - vertical).max(0.0);
2385    }
2386    normalize_constraints(Constraints {
2387        min_width,
2388        max_width,
2389        min_height,
2390        max_height,
2391    })
2392}
2393
2394fn resolve_dimension(
2395    base: f32,
2396    explicit: DimensionConstraint,
2397    min_override: Option<f32>,
2398    max_override: Option<f32>,
2399    min_limit: f32,
2400    max_limit: f32,
2401) -> f32 {
2402    let mut min_bound = min_limit;
2403    if let Some(min_value) = min_override {
2404        min_bound = min_bound.max(min_value);
2405    }
2406
2407    let mut max_bound = if max_limit.is_finite() {
2408        max_limit
2409    } else {
2410        max_override.unwrap_or(max_limit)
2411    };
2412    if let Some(max_value) = max_override {
2413        if max_bound.is_finite() {
2414            max_bound = max_bound.min(max_value);
2415        } else {
2416            max_bound = max_value;
2417        }
2418    }
2419    if max_bound < min_bound {
2420        max_bound = min_bound;
2421    }
2422
2423    let mut size = match explicit {
2424        DimensionConstraint::Points(points) => points,
2425        DimensionConstraint::Fraction(fraction) => {
2426            if max_limit.is_finite() {
2427                max_limit * fraction.clamp(0.0, 1.0)
2428            } else {
2429                base
2430            }
2431        }
2432        DimensionConstraint::Unspecified => base,
2433        DimensionConstraint::Intrinsic(_) => base,
2434    };
2435
2436    size = clamp_dimension(size, min_bound, max_bound);
2437    size = clamp_dimension(size, min_limit, max_limit);
2438    size.max(0.0)
2439}
2440
2441fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
2442    let mut result = value.max(min);
2443    if max.is_finite() {
2444        result = result.min(max);
2445    }
2446    result
2447}
2448
2449fn normalize_constraints(mut constraints: Constraints) -> Constraints {
2450    if constraints.max_width < constraints.min_width {
2451        constraints.max_width = constraints.min_width;
2452    }
2453    if constraints.max_height < constraints.min_height {
2454        constraints.max_height = constraints.min_height;
2455    }
2456    constraints
2457}
2458
2459#[cfg(test)]
2460#[path = "tests/layout_tests.rs"]
2461mod tests;
2462
2463#[cfg(test)]
2464#[path = "tests/semantics_update_tests.rs"]
2465mod semantics_update_tests;
2466
2467#[cfg(test)]
2468#[path = "tests/coordinator_geometry_tests.rs"]
2469mod coordinator_geometry_tests;