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 live_layout_node;
9mod measure_telemetry;
10mod measured_spare;
11mod placed_root_measurements;
12/// Measure policies behind the built-in layout widgets such as `Box`, `Row` and `Column`.
13pub mod policies;
14mod reveal;
15mod semantics_details;
16mod semantics_labels;
17mod semantics_tree;
18mod semantics_update;
19
20use std::{
21    cell::{Cell, RefCell},
22    fmt,
23    mem::size_of,
24    rc::Rc,
25};
26
27use cranpose_core::{
28    Applier, ApplierHost, ConcreteApplierHost, MemoryApplier, Node, NodeError, NodeId, PackedU64,
29    RuntimeHandle,
30};
31use cranpose_foundation::{
32    InvalidationKind, ModifierNodeContext, SemanticsConfiguration, SemanticsWidgetRole,
33};
34use cranpose_ui_graphics::{ProjectiveTransform, layer_transform::layer_transform_to_window};
35use cranpose_ui_layout::{AlignmentLines, Constraints, MeasurePolicy, PlaceTarget, Placement};
36pub(crate) use frame_layout_arena::FrameLayoutArena;
37use frame_layout_arena::VecPools;
38use live_layout_node::{LiveLayoutNode, read_live_layout_node};
39use measured_spare::MeasuredSpare;
40use placed_root_measurements::PlacedRootMeasurements;
41pub use semantics_tree::{
42    SemanticsAction, SemanticsCallback, SemanticsNode, SemanticsPlacement, SemanticsRole,
43    SemanticsText, SemanticsTree,
44};
45use semantics_tree::{modal_takes_space, top_modal_path};
46use web_time::Instant;
47
48#[cfg(test)]
49use self::core::{HorizontalAlignment, VerticalAlignment};
50use self::{
51    coordinator_chain::{CoordinatorChain, CoordinatorFrame},
52    core::{Measurable, Placeable},
53    measure_telemetry::{
54        LayoutStages, layout_measure_telemetry_threshold_ms, log_layout_measure_telemetry,
55        log_node_measure_telemetry,
56    },
57    semantics_update::semantics_placement,
58};
59pub use self::{
60    reveal::can_skip_scroll_reveal_from_applier,
61    semantics_details::SemanticsDetails,
62    semantics_update::{build_semantics_tree_from_applier, update_semantics_tree_from_applier},
63};
64use crate::{
65    modifier::{
66        DimensionConstraint, EdgeInsets, Modifier, ModifierNodeSlices,
67        ModifierNodeSlicesDebugStats, Point, Rect as GeometryRect, ResolvedModifiers, Size,
68    },
69    render_state::AppContext,
70    subcompose_layout::{
71        MeasureCallbacks, MeasuredChildren, SubcomposeLayoutNode, SubcomposeLayoutNodeHandle,
72    },
73    widgets::nodes::{IntrinsicKind, LayoutNode, LayoutParts, LayoutState},
74};
75
76#[derive(Default)]
77pub(crate) struct LayoutNodeContext {
78    invalidations: Vec<InvalidationKind>,
79    update_requested: bool,
80    density: f32,
81}
82
83impl LayoutNodeContext {
84    pub(crate) fn new(density: f32) -> Self {
85        Self {
86            density,
87            ..Self::default()
88        }
89    }
90
91    pub(crate) fn take_invalidations(&mut self) -> Vec<InvalidationKind> {
92        std::mem::take(&mut self.invalidations)
93    }
94}
95
96impl ModifierNodeContext for LayoutNodeContext {
97    fn invalidate(&mut self, kind: InvalidationKind) {
98        if !self.invalidations.contains(&kind) {
99            self.invalidations.push(kind);
100        }
101    }
102
103    fn request_update(&mut self) {
104        self.update_requested = true;
105    }
106
107    fn density(&self) -> f32 {
108        self.density
109    }
110}
111
112#[doc(hidden)]
113pub fn invalidate_all_layout_caches() {
114    crate::render_state::invalidate_layout_cache_epoch();
115}
116
117/// The host a layout pass lends its applier to. Between passes it holds an
118/// empty applier and the app context keeps it, so a pass builds neither.
119pub(crate) struct LayoutHost {
120    host: Rc<ConcreteApplierHost<MemoryApplier>>,
121}
122
123impl Default for LayoutHost {
124    fn default() -> Self {
125        Self {
126            host: Rc::new(ConcreteApplierHost::new(MemoryApplier::new())),
127        }
128    }
129}
130
131/// Lends `target` to a [`LayoutHost`] of `context` for one pass and gives it
132/// back when dropped, also after a panic.
133struct ApplierSlotGuard<'a> {
134    target: &'a mut MemoryApplier,
135    lent: LayoutHost,
136    context: &'a AppContext,
137}
138
139impl<'a> ApplierSlotGuard<'a> {
140    fn new(target: &'a mut MemoryApplier, context: &'a AppContext) -> Self {
141        let lent = context.take_layout_host();
142        std::mem::swap(target, &mut *lent.host.borrow_typed());
143        Self {
144            target,
145            lent,
146            context,
147        }
148    }
149
150    fn host(&self) -> &Rc<ConcreteApplierHost<MemoryApplier>> {
151        &self.lent.host
152    }
153}
154
155impl Drop for ApplierSlotGuard<'_> {
156    fn drop(&mut self) {
157        let host = &self.lent.host;
158        std::mem::swap(self.target, &mut *host.borrow_typed());
159        // A host still shared past the pass stays with whoever holds it.
160        if Rc::strong_count(host) == 1 {
161            self.context.replace_layout_host(LayoutHost {
162                host: Rc::clone(host),
163            });
164        }
165    }
166}
167
168#[derive(Clone, Copy, PartialEq)]
169struct ModifierChainInputs {
170    density: crate::density::Density,
171    window_root: bool,
172    offset: Point,
173    uses_chain: bool,
174}
175
176struct ModifierChainMeasurement {
177    size: Size,
178    alignment_lines: AlignmentLines,
179    content_offset: Point,
180    offset: Point,
181    window_root: bool,
182    /// The incoming constraints this measurement holds for besides its own.
183    hold: Option<cranpose_ui_layout::ConstraintsHold>,
184    /// Whether no child's size entered it: see
185    /// [`MeasurePolicy::ignores_child_sizes`].
186    ignores_child_sizes: bool,
187}
188
189/// Counts and heap sizes of the layout and semantics data that one measure pass retains, for memory
190/// diagnostics.
191#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
192pub struct LayoutAllocationDebugStats {
193    /// Number of `LayoutBox` nodes in the tree.
194    pub layout_box_count: usize,
195    /// Total number of child entries across all layout boxes.
196    pub layout_box_child_count: usize,
197    /// Total allocated capacity of the child vectors across all layout boxes.
198    pub layout_box_child_capacity: usize,
199    /// Heap bytes allocated for the child vectors of all layout boxes.
200    pub layout_box_heap_bytes: usize,
201    /// Number of modifier slice snapshots, one per layout box.
202    pub modifier_slice_count: usize,
203    /// Heap bytes held by all modifier slice snapshots.
204    pub modifier_slice_heap_bytes: usize,
205    /// Number of draw commands collected from modifier nodes.
206    pub modifier_draw_command_count: usize,
207    /// Allocated capacity of the draw command lists.
208    pub modifier_draw_command_capacity: usize,
209    /// Number of pointer input handlers collected from modifier nodes.
210    pub modifier_pointer_input_count: usize,
211    /// Allocated capacity of the pointer input handler lists.
212    pub modifier_pointer_input_capacity: usize,
213    /// Number of layout boxes that carry text content.
214    pub modifier_text_content_count: usize,
215    /// Number of layout boxes that carry a text style.
216    pub modifier_text_style_count: usize,
217    /// Number of layout boxes that carry text layout options.
218    pub modifier_text_layout_options_count: usize,
219    /// Number of layout boxes that carry a prepared text layout.
220    pub modifier_prepared_text_layout_count: usize,
221    /// Number of layout boxes that carry a graphics layer.
222    pub modifier_graphics_layer_count: usize,
223    /// Number of layout boxes that carry a graphics layer resolver.
224    pub modifier_graphics_layer_resolver_count: usize,
225    /// Number of nodes in the semantics tree.
226    pub semantics_node_count: usize,
227    /// Total number of actions across all semantics nodes.
228    pub semantics_action_count: usize,
229    /// Total allocated capacity of the action vectors across all semantics nodes.
230    pub semantics_action_capacity: usize,
231    /// Total number of child entries across all semantics nodes.
232    pub semantics_child_count: usize,
233    /// Total allocated capacity of the child vectors across all semantics nodes.
234    pub semantics_child_capacity: usize,
235    /// Number of semantics nodes that have a content description.
236    pub semantics_description_count: usize,
237    /// Allocated bytes of all content description strings.
238    pub semantics_description_bytes: usize,
239    /// Heap bytes held by the semantics tree: actions, children, details and descriptions.
240    pub semantics_heap_bytes: usize,
241}
242
243impl LayoutAllocationDebugStats {
244    fn add_modifier_slice(&mut self, stats: ModifierNodeSlicesDebugStats) {
245        self.modifier_slice_count += 1;
246        self.modifier_slice_heap_bytes += stats.heap_bytes;
247        self.modifier_draw_command_count += stats.draw_command_count;
248        self.modifier_draw_command_capacity += stats.draw_command_capacity;
249        self.modifier_pointer_input_count += stats.pointer_input_count;
250        self.modifier_pointer_input_capacity += stats.pointer_input_capacity;
251        self.modifier_text_content_count += usize::from(stats.has_text_content);
252        self.modifier_text_style_count += usize::from(stats.has_text_style);
253        self.modifier_text_layout_options_count += usize::from(stats.has_text_layout_options);
254        self.modifier_prepared_text_layout_count += usize::from(stats.has_prepared_text_layout);
255        self.modifier_graphics_layer_count += usize::from(stats.has_graphics_layer);
256        self.modifier_graphics_layer_resolver_count +=
257            usize::from(stats.has_graphics_layer_resolver);
258    }
259}
260
261/// Result of running layout for a Compose tree.
262#[derive(Debug, Clone)]
263pub struct LayoutTree {
264    root: LayoutBox,
265}
266
267impl LayoutTree {
268    /// Wraps `root` as a layout tree.
269    pub fn new(root: LayoutBox) -> Self {
270        Self { root }
271    }
272
273    /// Returns the root layout box.
274    pub fn root(&self) -> &LayoutBox {
275        &self.root
276    }
277
278    /// Returns the root layout box for in-place changes.
279    pub fn root_mut(&mut self) -> &mut LayoutBox {
280        &mut self.root
281    }
282
283    /// Consumes the tree and returns its root layout box.
284    pub fn into_root(self) -> LayoutBox {
285        self.root
286    }
287
288    /// Counts the allocations that the layout boxes of this tree retain.
289    pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
290        let mut stats = LayoutAllocationDebugStats::default();
291        record_layout_box_allocation_stats(&self.root, &mut stats);
292        stats
293    }
294}
295
296/// Layout information for a single node.
297#[derive(Debug, Clone)]
298pub struct LayoutBox {
299    /// Id of the runtime node this box describes.
300    pub node_id: NodeId,
301    /// Incarnation of the runtime node captured with these layout bounds.
302    pub node_generation: u32,
303    /// Position and size of the node in root coordinates, in logical pixels, before graphics-layer
304    /// transforms.
305    pub rect: GeometryRect,
306    /// Content offset for scroll/inner transforms (applies to children, NOT this node's position)
307    pub content_offset: Point,
308    /// Modifier, semantics and kind data captured with the node.
309    pub node_data: LayoutNodeData,
310    /// Layout boxes of the node's placed children.
311    pub children: Vec<Self>,
312}
313
314impl LayoutBox {
315    /// Creates a layout box with node generation zero.
316    pub fn new(
317        node_id: NodeId,
318        rect: GeometryRect,
319        content_offset: Point,
320        node_data: LayoutNodeData,
321        children: Vec<Self>,
322    ) -> Self {
323        Self {
324            node_id,
325            node_generation: 0,
326            rect,
327            content_offset,
328            node_data,
329            children,
330        }
331    }
332}
333
334/// Snapshot of the data required to render a layout node.
335#[derive(Debug, Clone)]
336pub struct LayoutNodeData {
337    /// Composable origins retained by an inspection-enabled layout node.
338    #[cfg(feature = "inspection")]
339    pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
340    /// The modifier chain of the node.
341    pub modifier: Modifier,
342    /// Padding, layout properties and offset that the modifier chain resolved to.
343    pub resolved_modifiers: ResolvedModifiers,
344    /// Draw commands, pointer input handlers, text and graphics layer data collected from the
345    /// modifier nodes.
346    pub modifier_slices: Rc<ModifierNodeSlices>,
347    /// Semantics that the modifier chain reported, if any.
348    pub semantics: Option<Rc<SemanticsConfiguration>>,
349    /// What kind of node this is.
350    pub kind: LayoutNodeKind,
351}
352
353impl LayoutNodeData {
354    /// Creates node data from its parts.
355    pub fn new(
356        modifier: Modifier,
357        resolved_modifiers: ResolvedModifiers,
358        modifier_slices: Rc<ModifierNodeSlices>,
359        semantics: Option<Rc<SemanticsConfiguration>>,
360        kind: LayoutNodeKind,
361    ) -> Self {
362        Self {
363            #[cfg(feature = "inspection")]
364            source_trace: Rc::default(),
365            modifier,
366            resolved_modifiers,
367            modifier_slices,
368            semantics,
369            kind,
370        }
371    }
372
373    /// Semantics the node's live modifier chain reported when the snapshot
374    /// was taken.
375    pub fn semantics(&self) -> Option<&SemanticsConfiguration> {
376        self.semantics.as_deref()
377    }
378
379    /// Returns the padding, layout properties and offset that the modifier chain resolved to.
380    pub fn resolved_modifiers(&self) -> ResolvedModifiers {
381        self.resolved_modifiers
382    }
383
384    /// Returns the draw, pointer input, text and graphics layer data collected from the modifier
385    /// nodes.
386    pub fn modifier_slices(&self) -> &ModifierNodeSlices {
387        &self.modifier_slices
388    }
389}
390
391/// Classification of the node captured inside a [`LayoutBox`].
392///
393/// Note: Text content is no longer represented as a distinct LayoutNodeKind.
394/// Text nodes now use `LayoutNodeKind::Layout` with their content stored in
395/// `modifier_slices.text_content()` via TextModifierNode, following Jetpack
396/// Compose's pattern where text is a modifier node capability.
397#[derive(Clone)]
398pub enum LayoutNodeKind {
399    /// A node that measures and places its children.
400    Layout,
401    /// A subcomposition boundary, such as a lazy list or a `SubcomposeLayout`.
402    Subcompose,
403    /// A non-interactive spacer.
404    Spacer,
405    /// A button node.
406    Button {
407        /// Handler that runs when the button is clicked.
408        on_click: Rc<RefCell<dyn FnMut()>>,
409    },
410    /// A node that fits no other kind.
411    Unknown,
412}
413
414impl fmt::Debug for LayoutNodeKind {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        match self {
417            Self::Layout => f.write_str("Layout"),
418            Self::Subcompose => f.write_str("Subcompose"),
419            Self::Spacer => f.write_str("Spacer"),
420            Self::Button { .. } => f.write_str("Button"),
421            Self::Unknown => f.write_str("Unknown"),
422        }
423    }
424}
425
426/// Extension trait that equips `MemoryApplier` with layout computation.
427pub trait LayoutEngine {
428    /// Measures the tree under `root` within `max_size` and returns its layout tree.
429    fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError>;
430}
431
432impl LayoutEngine for MemoryApplier {
433    fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError> {
434        let measurements = measure_layout(self, root, max_size)?;
435        measurements
436            .into_layout_tree()
437            .ok_or(NodeError::MissingContext {
438                id: root,
439                reason: "layout tree was not requested",
440            })
441    }
442}
443
444/// Result of running the measure pass for a Compose layout tree.
445#[derive(Debug, Clone)]
446pub struct LayoutMeasurements {
447    root: Rc<MeasuredNode>,
448    // Boxed: a frame that builds neither tree moves a few words, not the
449    // hundreds of bytes the trees take inline.
450    semantics: Option<Box<SemanticsTree>>,
451    layout_tree: Option<Box<LayoutTree>>,
452}
453
454impl LayoutMeasurements {
455    fn new(
456        root: Rc<MeasuredNode>,
457        semantics: Option<SemanticsTree>,
458        layout_tree: Option<LayoutTree>,
459    ) -> Self {
460        Self {
461            root,
462            semantics: semantics.map(Box::new),
463            layout_tree: layout_tree.map(Box::new),
464        }
465    }
466
467    /// Returns the measured size of the root node.
468    pub fn root_size(&self) -> Size {
469        self.root.size
470    }
471
472    /// Returns the semantics tree, or `None` if the measure options left it out.
473    pub fn semantics_tree(&self) -> Option<&SemanticsTree> {
474        self.semantics.as_deref()
475    }
476
477    /// Counts the allocations that the layout tree and the semantics tree retain.
478    pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
479        let mut stats = self
480            .layout_tree
481            .as_deref()
482            .map(LayoutTree::debug_allocation_stats)
483            .unwrap_or_default();
484        if let Some(semantics) = &self.semantics {
485            record_semantics_allocation_stats(semantics.root(), &mut stats);
486        }
487        stats
488    }
489
490    /// Consumes the measurements and returns the built [`LayoutTree`], if requested.
491    pub fn into_layout_tree(self) -> Option<LayoutTree> {
492        self.layout_tree.map(|tree| *tree)
493    }
494
495    /// Returns a cloned [`LayoutTree`] for rendering/debug consumers, if requested.
496    pub fn layout_tree(&self) -> Option<LayoutTree> {
497        self.layout_tree.as_deref().cloned()
498    }
499}
500
501/// Builds a semantics tree from an existing [`LayoutTree`].
502///
503/// This is useful for consumers that need semantics on demand without forcing
504/// every layout pass to eagerly allocate a full [`SemanticsTree`].
505pub fn build_semantics_tree_from_layout_tree(layout_tree: &LayoutTree) -> SemanticsTree {
506    SemanticsTree::new(build_semantics_node_from_layout_box(
507        layout_tree.root(),
508        Point::default(),
509    ))
510}
511
512/// Builds a layout snapshot from retained layout state in the live applier tree.
513///
514/// Renderers use retained node state directly. This function exists for debug,
515/// robot, and tests that need an owned [`LayoutTree`] without forcing every
516/// layout pass to allocate one.
517pub fn build_layout_tree_from_applier(
518    applier: &mut MemoryApplier,
519    root: NodeId,
520) -> Result<Option<LayoutTree>, NodeError> {
521    let origin = layout_tree_origin(read_layout_node(applier, root, |state, _| state)?);
522    let mut child_stack = Vec::new();
523    place_layout_box(
524        applier,
525        root,
526        origin,
527        ProjectiveTransform::identity(),
528        &mut child_stack,
529    )
530    .map(|root| root.map(LayoutTree::new))
531}
532
533/// Whether any box placed under `root` has area, as
534/// [`build_layout_tree_from_applier`] would place it: the walk stops at the
535/// first such box and reads no node's modifiers or semantics.
536pub fn has_placed_content(applier: &mut MemoryApplier, root: NodeId) -> Result<bool, NodeError> {
537    walk_placed_boxes(applier, root, |_| std::ops::ControlFlow::Break(()))
538}
539
540/// The far right and bottom edges of the boxes with area placed under `root`,
541/// measured from the root's origin as [`build_layout_tree_from_applier`]
542/// places them, reading no node's modifiers or semantics. `None` while
543/// nothing under `root` has area.
544pub fn placed_content_extent(
545    applier: &mut MemoryApplier,
546    root: NodeId,
547) -> Result<Option<Size>, NodeError> {
548    let mut extent: Option<Size> = None;
549    walk_placed_boxes(applier, root, |rect| {
550        let (right, bottom) = (rect.x + rect.width, rect.y + rect.height);
551        extent = Some(extent.map_or_else(
552            || Size::new(right, bottom),
553            |extent| Size::new(extent.width.max(right), extent.height.max(bottom)),
554        ));
555        std::ops::ControlFlow::Continue(())
556    })?;
557    Ok(extent)
558}
559
560/// Hands `visit` every box with area placed under `root`, the root left out,
561/// at the rect [`build_layout_tree_from_applier`] gives it, skipping a window
562/// root's subtree and an unplaced node's, until `visit` breaks. Answers
563/// whether it did.
564fn walk_placed_boxes(
565    applier: &mut MemoryApplier,
566    root: NodeId,
567    mut visit: impl FnMut(GeometryRect) -> std::ops::ControlFlow<()>,
568) -> Result<bool, NodeError> {
569    let mut pending: Vec<(NodeId, Point)> = Vec::new();
570    let placed = read_layout_node(applier, root, |state, children| {
571        if state.is_placed() {
572            pending.extend(
573                children
574                    .iter()
575                    .map(|&child| (child, state.content_offset())),
576            );
577        }
578    })?;
579    if placed.is_none() {
580        return Ok(false);
581    }
582    while let Some((node_id, origin)) = pending.pop() {
583        if crate::modifier::is_window_root(applier, node_id) {
584            continue;
585        }
586        let rect = read_layout_node(applier, node_id, |state, children| {
587            if !state.is_placed() {
588                return None;
589            }
590            let (rect, content) = semantics_placement(&state, Some(origin));
591            pending.extend(children.iter().map(|&child| (child, content)));
592            Some(rect)
593        })?
594        .flatten();
595        if let Some(rect) = rect
596            && rect.width > 0.0
597            && rect.height > 0.0
598            && visit(rect).is_break()
599        {
600            return Ok(true);
601        }
602    }
603    Ok(false)
604}
605
606fn layout_tree_origin(root: Option<LayoutState>) -> Point {
607    let Some(state) = root else {
608        return Point::default();
609    };
610    let position = state.position();
611    Point {
612        x: -position.x,
613        y: -position.y,
614    }
615}
616
617/// Places the root at the origin, the one placement no parent makes, and
618/// names what the pass left unplaced.
619fn finish_placement(applier: &mut MemoryApplier, root: NodeId, context: &AppContext) {
620    if applier
621        .with_node::<LayoutNode, _>(root, |node| {
622            node.set_position(Point::default(), None, context);
623        })
624        .is_err()
625    {
626        let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
627            node.set_position(Point::default(), None, context);
628        });
629    }
630    if let Some(pass) = context.placement_pass().with_unplaced_nodes() {
631        record_unplaced_parents(applier, root, pass, context);
632    }
633}
634
635/// Names to the scene phase the parent of every node `pass` found placed and
636/// left unplaced: no geometry setter reported those, yet their layers leave
637/// the scene. Out of line: a pass rarely unplaces nodes.
638#[inline(never)]
639fn record_unplaced_parents(
640    applier: &mut MemoryApplier,
641    root: NodeId,
642    pass: u64,
643    context: &AppContext,
644) {
645    let mut parents = vec![root];
646    let mut children = Vec::new();
647    while let Some(parent) = parents.pop() {
648        children.clear();
649        if !matches!(
650            read_layout_node(applier, parent, |_, ids| children.extend_from_slice(ids)),
651            Ok(Some(()))
652        ) {
653            continue;
654        }
655        for &child in &children {
656            match read_layout_node(applier, child, |state, _| state.unplaced_in(pass)) {
657                Ok(Some(true)) => context.record_geometry_scene_node(parent),
658                Ok(Some(false)) => parents.push(child),
659                Ok(None) | Err(_) => {}
660            }
661        }
662    }
663}
664
665fn read_layout_node<R>(
666    applier: &mut MemoryApplier,
667    node_id: NodeId,
668    mut read: impl FnMut(LayoutState, &[NodeId]) -> R,
669) -> Result<Option<R>, NodeError> {
670    read_live_layout_node(applier, node_id, |node| {
671        node.with_children(|children| read(node.state(), children))
672    })
673}
674
675fn snapshot_node_data(
676    applier: &mut MemoryApplier,
677    node_id: NodeId,
678    top_left: Point,
679    size: Size,
680    parent_transform: ProjectiveTransform,
681) -> Result<(LayoutNodeData, ProjectiveTransform), NodeError> {
682    let info = runtime_metadata_for(applier, node_id)?;
683    let kind = layout_kind_from_metadata(node_id, &info);
684    let RuntimeNodeMetadata {
685        modifier,
686        resolved_modifiers,
687        modifier_slices,
688        semantics,
689        ..
690    } = info;
691
692    let window_transform = modifier_slices
693        .graphics_layer()
694        .map_or(parent_transform, |layer| {
695            layer_transform_to_window(
696                parent_transform,
697                top_left,
698                modifier_slices.layer_bounds(size),
699                &layer,
700            )
701        });
702    modifier_slices.publish_window_geometry(top_left, window_transform, size);
703
704    let data = LayoutNodeData {
705        #[cfg(feature = "inspection")]
706        source_trace: applier
707            .with_node::<LayoutNode, _>(node_id, |node| Rc::clone(&node.source_trace))
708            .unwrap_or_default(),
709        modifier,
710        resolved_modifiers,
711        modifier_slices,
712        semantics,
713        kind,
714    };
715    Ok((data, window_transform))
716}
717
718/// Places `node_id`'s box and its subtree. `child_stack` is shared by the
719/// whole walk: each node pushes its children above its parent's, reads them
720/// from there while its descendants push and pop above them, and pops them
721/// when done, so no node's child list is copied out of the applier.
722fn place_layout_box(
723    applier: &mut MemoryApplier,
724    node_id: NodeId,
725    parent_content_origin: Point,
726    parent_transform: ProjectiveTransform,
727    child_stack: &mut Vec<NodeId>,
728) -> Result<Option<LayoutBox>, NodeError> {
729    let first_child = child_stack.len();
730    let Some(state) = read_layout_node(applier, node_id, |state, children| {
731        if state.is_placed() {
732            child_stack.extend_from_slice(children);
733        }
734        state
735    })?
736    else {
737        return Ok(None);
738    };
739    if !state.is_placed() {
740        return Ok(None);
741    }
742
743    let top_left = Point {
744        x: parent_content_origin.x + state.position().x,
745        y: parent_content_origin.y + state.position().y,
746    };
747    let rect = GeometryRect {
748        x: top_left.x,
749        y: top_left.y,
750        width: state.size().width,
751        height: state.size().height,
752    };
753    let (data, window_transform) =
754        snapshot_node_data(applier, node_id, top_left, state.size(), parent_transform)?;
755    let child_origin = Point {
756        x: top_left.x + state.content_offset().x,
757        y: top_left.y + state.content_offset().y,
758    };
759    let end = child_stack.len();
760    let mut children = Vec::with_capacity(end - first_child);
761    for index in first_child..end {
762        let child_id = child_stack[index];
763        if crate::modifier::is_window_root(applier, child_id) {
764            continue;
765        }
766        if let Some(child) = place_layout_box(
767            applier,
768            child_id,
769            child_origin,
770            window_transform,
771            child_stack,
772        )? {
773            children.push(child);
774        }
775    }
776    child_stack.truncate(first_child);
777
778    Ok(Some(LayoutBox {
779        node_generation: applier.node_generation(node_id),
780        ..LayoutBox::new(node_id, rect, state.content_offset(), data, children)
781    }))
782}
783
784/// The modal the semantics tree of `root` would be rooted at, found without
785/// building the tree: the topmost placed, visible modal that takes space,
786/// or `None` when no modal is open. Unlike a tree update, it leaves the
787/// nodes' semantics dirty flags alone.
788pub fn top_modal_from_applier(
789    applier: &mut MemoryApplier,
790    root: NodeId,
791) -> Result<Option<NodeId>, NodeError> {
792    // One depth-first walk on a shared stack, last child first, with a
793    // modal's own step below its children's: the first modal step popped is
794    // the modal drawn on top. It runs every frame, so no node's children are
795    // copied and each node is looked up once.
796    enum Step {
797        Enter { node: NodeId, child: bool },
798        Modal(NodeId),
799    }
800
801    fn push_placed(
802        steps: &mut Vec<Step>,
803        node: NodeId,
804        reach: cranpose_foundation::SemanticsReach,
805        size: Size,
806        children: impl IntoIterator<Item = NodeId>,
807    ) {
808        if reach.hidden {
809            return;
810        }
811        if reach.is_modal && modal_takes_space(size) {
812            steps.push(Step::Modal(node));
813        }
814        steps.extend(
815            children
816                .into_iter()
817                .map(|node| Step::Enter { node, child: true }),
818        );
819    }
820
821    let mut steps = vec![Step::Enter {
822        node: root,
823        child: false,
824    }];
825    while let Some(step) = steps.pop() {
826        let (node_id, child) = match step {
827            Step::Modal(node) => return Ok(Some(node)),
828            Step::Enter { node, child } => (node, child),
829        };
830        let node = match applier.get_mut(node_id) {
831            Ok(node) => node.as_any_mut(),
832            Err(NodeError::Missing { .. }) => continue,
833            Err(error) => return Err(error),
834        };
835        if let Some(layout) = node.downcast_mut::<LayoutNode>() {
836            let state = layout.layout_state();
837            if state.is_placed() && !(child && layout.is_window_root()) {
838                let children = layout.children.iter().copied();
839                push_placed(
840                    &mut steps,
841                    node_id,
842                    layout.semantics_reach(),
843                    state.size(),
844                    children,
845                );
846            }
847        } else if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
848            let state = subcompose.layout_state();
849            if state.is_placed() {
850                let reach = subcompose.semantics_reach();
851                subcompose.with_active_children(|children| {
852                    push_placed(
853                        &mut steps,
854                        node_id,
855                        reach,
856                        state.size(),
857                        children.iter().copied(),
858                    );
859                });
860            }
861        }
862    }
863    Ok(None)
864}
865
866/// Selects which trees [`measure_layout_with_options`] builds.
867#[derive(Clone, Copy, Debug, PartialEq, Eq)]
868pub struct MeasureLayoutOptions {
869    /// Build the semantics tree.
870    pub collect_semantics: bool,
871    /// Build the layout tree.
872    pub build_layout_tree: bool,
873}
874
875impl Default for MeasureLayoutOptions {
876    fn default() -> Self {
877        Self {
878            collect_semantics: true,
879            build_layout_tree: true,
880        }
881    }
882}
883
884/// Check if a node or any of its descendants needs measure (selective measure optimization).
885/// This can be used by the app shell to skip layout when the tree is clean.
886///
887/// O(1) check - just looks at root's dirty flag.
888/// Works because all mutation paths bubble dirty flags to root via composer commands.
889///
890/// Returns Result to force caller to handle errors explicitly. No more unwrap_or(true) safety net.
891pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
892    Ok(applier.get_mut(root)?.layout_dirty())
893}
894
895/// Check if the root semantics snapshot is dirty.
896///
897/// Semantics invalidations bubble to the root the same way layout invalidations do,
898/// so a root check is sufficient to determine whether the next layout pass needs to
899/// rebuild semantic data even when geometry is otherwise unchanged.
900pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
901    Ok(applier.get_mut(root)?.needs_semantics())
902}
903
904#[cfg(test)]
905pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
906    cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
907}
908
909/// Runs the measure phase for the subtree rooted at `root`.
910pub fn measure_layout(
911    applier: &mut MemoryApplier,
912    root: NodeId,
913    max_size: Size,
914) -> Result<LayoutMeasurements, NodeError> {
915    measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
916}
917
918/// Runs the measure phase for the subtree rooted at `root` and builds only the trees that `options`
919/// select.
920pub fn measure_layout_with_options(
921    applier: &mut MemoryApplier,
922    root: NodeId,
923    max_size: Size,
924    options: MeasureLayoutOptions,
925) -> Result<LayoutMeasurements, NodeError> {
926    let mut stages = LayoutStages::start(layout_measure_telemetry_threshold_ms().is_some());
927    let context = crate::render_state::require_current_app_context("a layout pass");
928    context.begin_text_layout_pass();
929    context.placement_pass().begin();
930    process_pending_layout_repasses(&context, applier, root)?;
931    stages.reached();
932
933    let constraints = Constraints {
934        min_width: 0.0,
935        max_width: max_size.width,
936        min_height: 0.0,
937        max_height: max_size.height,
938    };
939
940    let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
941        .with_node::<LayoutNode, _>(root, |node| {
942            (
943                node.needs_measure() || Node::descendant_needs_measure(node),
944                node.needs_semantics(),
945                node.cache().epoch(),
946            )
947        }) {
948        Ok(tuple) => tuple,
949        Err(NodeError::TypeMismatch { .. }) => {
950            let node = applier.get_mut(root)?;
951            let measure_dirty = node.needs_measure() || node.descendant_needs_measure();
952            let semantics_dirty = node.needs_semantics();
953            (measure_dirty, semantics_dirty, 0)
954        }
955        Err(err) => return Err(err),
956    };
957
958    let epoch = if needs_remeasure {
959        context.next_layout_cache_epoch()
960    } else if cached_epoch != 0 {
961        cached_epoch
962    } else {
963        context.current_layout_cache_epoch()
964    };
965
966    let guard = ApplierSlotGuard::new(applier, &context);
967    let applier_host = guard.host();
968    stages.reached();
969
970    let builder = LayoutBuilder::new_with_epoch(Rc::clone(applier_host), epoch, &context);
971    stages.reached();
972
973    let measured = builder
974        .state
975        .measure_root(root, normalize_constraints(constraints))?;
976    stages.reached();
977
978    if let Ok(mut applier) = applier_host.try_borrow_typed() {
979        finish_placement(&mut applier, root, &context);
980    }
981    stages.reached();
982
983    let (layout_tree, semantics) = {
984        let mut applier_ref = applier_host.borrow_typed();
985        let layout_tree = if options.build_layout_tree {
986            Some(build_layout_tree(&mut applier_ref, &measured)?)
987        } else {
988            None
989        };
990        let semantics = if options.collect_semantics {
991            let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
992                clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
993                build_semantics_tree_from_layout_tree(layout_tree)
994            } else {
995                build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
996            };
997            Some(semantics_tree)
998        } else {
999            None
1000        };
1001        (layout_tree, semantics)
1002    };
1003    stages.reached();
1004
1005    drop(builder);
1006    stages.reached();
1007
1008    drop(guard);
1009    stages.reached();
1010
1011    log_layout_measure_telemetry(root, &stages);
1012
1013    Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
1014}
1015
1016fn process_pending_layout_repasses(
1017    context: &AppContext,
1018    applier: &mut MemoryApplier,
1019    root: NodeId,
1020) -> Result<(), NodeError> {
1021    for node_id in context.take_modifier_slice_repass_nodes() {
1022        if let Ok(node) = applier.get_mut(node_id) {
1023            let any = node.as_any_mut();
1024            if let Some(layout) = any.downcast_mut::<LayoutNode>() {
1025                layout.mark_modifier_slices_dirty();
1026            } else if let Some(subcompose) = any.downcast_mut::<SubcomposeLayoutNode>() {
1027                subcompose.mark_modifier_slices_dirty();
1028            }
1029        }
1030    }
1031    let measure_repass_nodes = context.take_measure_repass_nodes();
1032    let repass_nodes = context.take_layout_repass_nodes();
1033    if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
1034        return Ok(());
1035    }
1036    for node_id in measure_repass_nodes {
1037        cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
1038    }
1039    for node_id in repass_nodes {
1040        cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1041    }
1042    applier.get_mut(root)?.mark_descendant_needs_layout(false);
1043    Ok(())
1044}
1045
1046/// A layout pass's builder, with the frame arena it took from `context` and
1047/// gives back when dropped.
1048struct LayoutBuilder<'a> {
1049    state: Rc<LayoutBuilderState>,
1050    context: &'a AppContext,
1051}
1052
1053impl<'a> LayoutBuilder<'a> {
1054    fn new_with_epoch(
1055        applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1056        epoch: u64,
1057        context: &'a Rc<AppContext>,
1058    ) -> Self {
1059        Self {
1060            state: Rc::new(LayoutBuilderState::new_with_epoch(
1061                applier,
1062                epoch,
1063                Rc::clone(context),
1064            )),
1065            context,
1066        }
1067    }
1068}
1069
1070impl Drop for LayoutBuilder<'_> {
1071    fn drop(&mut self) {
1072        if Rc::strong_count(&self.state) != 1 {
1073            return;
1074        }
1075        let Ok(mut frame_arena) = self.state.frame_arena.try_borrow_mut() else {
1076            return;
1077        };
1078        self.context
1079            .replace_layout_frame_arena(std::mem::take(&mut *frame_arena));
1080    }
1081}
1082
1083struct LayoutBuilderState {
1084    applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1085    runtime_handle: RefCell<Option<RuntimeHandle>>,
1086    cache_epoch: u64,
1087    cache_floor: u64,
1088    frame_arena: RefCell<FrameLayoutArena>,
1089    /// The context the pass lays out in, which takes the nodes whose
1090    /// geometry it changed.
1091    context: Rc<AppContext>,
1092    /// The threshold of the measure telemetry, read once a pass: `None`
1093    /// while the telemetry is off.
1094    measure_telemetry_ms: Option<f64>,
1095}
1096
1097struct LayoutRuntimeFrameBindingCleanup<'a> {
1098    state: &'a RefCell<LayoutRuntimeState>,
1099}
1100
1101impl Drop for LayoutRuntimeFrameBindingCleanup<'_> {
1102    fn drop(&mut self) {
1103        self.state.borrow().unbind_children();
1104    }
1105}
1106
1107enum LayoutNodeVisit<'a> {
1108    Cached(Rc<MeasuredNode>),
1109    /// Only nodes below this one changed: its measurement holds unless a
1110    /// changed child measures differently.
1111    Keep(KeptNode),
1112    Measure(LayoutNodeMeasure<'a>),
1113}
1114
1115/// A node whose measurement may hold, with the parts of it the keep reads
1116/// and writes, so the keep needs no lookup of the node.
1117struct KeptNode {
1118    measured: Rc<MeasuredNode>,
1119    parts: Rc<LayoutParts>,
1120    /// Whether a child that measures to another size leaves the node as it
1121    /// is.
1122    ignores_child_sizes: bool,
1123}
1124
1125/// A kept measurement after the walk over its changed children: `resized`
1126/// when one of them measured to another size that the node ignores.
1127struct KeptChildren {
1128    measured: Rc<MeasuredNode>,
1129    resized: bool,
1130}
1131
1132/// What measuring one changed child of a kept node again needs.
1133#[derive(Clone, Copy)]
1134struct ChangedChild {
1135    constraints: Constraints,
1136    placed: bool,
1137}
1138
1139/// What the walk over a kept measurement finds of one child.
1140enum ChildChange {
1141    Unchanged,
1142    Changed(ChangedChild),
1143    /// The node has to run its policy.
1144    Refuse,
1145}
1146
1147/// A changed child of a kept node as a layout pass finds it, from a layout
1148/// node or a subcompose node.
1149struct ChildView {
1150    self_dirty: bool,
1151    /// The child's parent data, read only when the child changed itself:
1152    /// only its own modifiers give it.
1153    parent_data: Option<cranpose_ui_layout::ParentData>,
1154    /// The constraints the child's own cache holds.
1155    cached_constraints: Option<Constraints>,
1156    read_intrinsics: bool,
1157    placed: bool,
1158}
1159
1160impl ChildView {
1161    /// The child, when it or a node below it changed.
1162    fn of(node: &LiveLayoutNode<'_>) -> Option<Self> {
1163        let cache = node.cache();
1164        let dirt = cache.dirt();
1165        let self_dirty = dirt.measure || dirt.layout;
1166        if !self_dirty && !dirt.descendant.layout {
1167            return None;
1168        }
1169        Some(Self {
1170            self_dirty,
1171            parent_data: self_dirty.then(|| node.parent_data()),
1172            cached_constraints: cache.measured_constraints(),
1173            read_intrinsics: cache.has_intrinsics(),
1174            placed: node.is_placed(),
1175        })
1176    }
1177}
1178
1179struct LayoutNodeMeasure<'a> {
1180    parts: Rc<LayoutParts>,
1181    chain: ModifierChainInputs,
1182    pools: VecPools<'a>,
1183    spare: MeasuredSpare,
1184}
1185
1186#[expect(clippy::multiple_inherent_impl, reason = "bottom-up pass in its file")]
1187impl LayoutBuilderState {
1188    fn new_with_epoch(
1189        applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1190        epoch: u64,
1191        context: Rc<AppContext>,
1192    ) -> Self {
1193        let runtime_handle = applier.borrow_typed().runtime_handle();
1194
1195        Self {
1196            applier,
1197            runtime_handle: RefCell::new(runtime_handle),
1198            cache_epoch: epoch,
1199            cache_floor: context.layout_cache_floor(),
1200            frame_arena: RefCell::new(context.take_layout_frame_arena()),
1201            context,
1202            measure_telemetry_ms: layout_measure_telemetry_threshold_ms(),
1203        }
1204    }
1205
1206    fn with_applier_result<R>(
1207        &self,
1208        f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1209    ) -> Result<R, NodeError> {
1210        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1211            return Err(NodeError::MissingContext {
1212                id: NodeId::default(),
1213                reason: "applier already borrowed",
1214            });
1215        };
1216        f(&mut applier)
1217    }
1218
1219    fn measure_subcompose_node(
1220        self: &Rc<Self>,
1221        node_id: NodeId,
1222        constraints: Constraints,
1223    ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1224        self.clear_subcompose_placed(node_id);
1225        if let Some(cached) = self.subcompose_measurement_to_keep(node_id, constraints)
1226            && let Some(kept) = self.keep_subcompose_measurement(node_id, constraints, &cached)?
1227        {
1228            return Ok(("subcompose kept", kept));
1229        }
1230        Ok(match self.try_measure_subcompose(node_id, constraints)? {
1231            Some(measured) => {
1232                self.store_subcompose_measurement(node_id, constraints, &measured);
1233                ("subcompose", measured)
1234            }
1235            None => (
1236                "fallback",
1237                Rc::new(MeasuredNode::new(
1238                    node_id,
1239                    Size::default(),
1240                    Point { x: 0.0, y: 0.0 },
1241                    Point::default(),
1242                    Vec::new(),
1243                )),
1244            ),
1245        })
1246    }
1247
1248    /// The measurement of a subcompose node whose own inputs did not change
1249    /// since it measured at `constraints`, as for a layout node: its policy
1250    /// does not run, so its content does not compose again.
1251    fn subcompose_measurement_to_keep(
1252        &self,
1253        node_id: NodeId,
1254        constraints: Constraints,
1255    ) -> Option<Rc<MeasuredNode>> {
1256        let mut applier = self.applier.try_borrow_typed().ok()?;
1257        applier
1258            .with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1259                if node.needs_measure() || Node::needs_layout(node) {
1260                    return None;
1261                }
1262                let cache = node.cache();
1263                if cache.epoch() < self.cache_floor {
1264                    return None;
1265                }
1266                cache.get_measurement(constraints)
1267            })
1268            .ok()
1269            .flatten()
1270    }
1271
1272    /// Keeps a subcompose node's measurement as [`Self::keep_measurement`]
1273    /// keeps a layout node's. A subcompose node holds no record of what its
1274    /// policy gave each child, so a changed child must have only changed
1275    /// nodes below it, and measures again at the constraints its own cache
1276    /// holds.
1277    fn keep_subcompose_measurement(
1278        self: &Rc<Self>,
1279        node_id: NodeId,
1280        constraints: Constraints,
1281        cached: &Rc<MeasuredNode>,
1282    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1283        let kept = self.remeasure_changed_children(
1284            cached,
1285            false,
1286            |child_id| self.subcompose_child_change(child_id),
1287            |child_id| self.mark_measured_without_policy(child_id),
1288        )?;
1289        let Some(KeptChildren { measured: kept, .. }) = kept else {
1290            return Ok(None);
1291        };
1292        self.with_applier_result(|applier| {
1293            applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1294                node.cache()
1295                    .store_measurement(constraints, Rc::clone(&kept));
1296                node.clear_needs_layout();
1297            })
1298        })?;
1299        Ok(Some(kept))
1300    }
1301
1302    /// What the walk over a kept subcompose node finds of a child. A changed
1303    /// child refuses the keep when it changed itself, a parent read its
1304    /// intrinsic sizes, or its cache holds no constraints.
1305    fn subcompose_child_change(&self, child_id: NodeId) -> Result<ChildChange, NodeError> {
1306        self.child_change(child_id, |view| {
1307            match view.cached_constraints.filter(|_| !view.self_dirty) {
1308                Some(constraints) if !view.read_intrinsics => ChildChange::Changed(ChangedChild {
1309                    constraints,
1310                    placed: view.placed,
1311                }),
1312                _ => ChildChange::Refuse,
1313            }
1314        })
1315    }
1316
1317    /// What the walk finds of a child: unchanged when neither it nor a node
1318    /// below it changed, otherwise what `decide` makes of it.
1319    fn child_change(
1320        &self,
1321        child_id: NodeId,
1322        decide: impl FnOnce(ChildView) -> ChildChange,
1323    ) -> Result<ChildChange, NodeError> {
1324        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1325            return Ok(ChildChange::Refuse);
1326        };
1327        Ok(
1328            match read_live_layout_node(&mut applier, child_id, |node| ChildView::of(&node))? {
1329                None => ChildChange::Refuse,
1330                Some(None) => ChildChange::Unchanged,
1331                Some(Some(view)) => decide(view),
1332            },
1333        )
1334    }
1335
1336    /// Marks a child a subcompose node measured again without its policy, so
1337    /// the policy checks it when it next runs: it may keep measurements of
1338    /// its own, as a lazy list keeps its items'.
1339    fn mark_measured_without_policy(&self, child_id: NodeId) {
1340        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1341            return;
1342        };
1343        if let Ok(node) = applier.get_mut(child_id) {
1344            node.mark_descendant_needs_layout(true);
1345        }
1346    }
1347
1348    fn clear_subcompose_placed(&self, node_id: NodeId) {
1349        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1350            return;
1351        };
1352        let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1353            node.clear_placed(&self.context);
1354        });
1355    }
1356
1357    fn measure_node(
1358        self: &Rc<Self>,
1359        node_id: NodeId,
1360        constraints: Constraints,
1361    ) -> Result<Rc<MeasuredNode>, NodeError> {
1362        let Some(threshold_ms) = self.measure_telemetry_ms else {
1363            return self
1364                .measure_node_untimed(node_id, constraints)
1365                .map(|(_, measured)| measured);
1366        };
1367        self.measure_node_timed(node_id, constraints, threshold_ms)
1368    }
1369
1370    // Out of line, so a measure without telemetry keeps no room for its
1371    // timing and log on the stack at each level of the tree.
1372    #[inline(never)]
1373    fn measure_node_timed(
1374        self: &Rc<Self>,
1375        node_id: NodeId,
1376        constraints: Constraints,
1377        threshold_ms: f64,
1378    ) -> Result<Rc<MeasuredNode>, NodeError> {
1379        let start = Instant::now();
1380        let (kind, measured) = self.measure_node_untimed(node_id, constraints)?;
1381        log_node_measure_telemetry(kind, node_id, constraints, &measured, (threshold_ms, start));
1382        Ok(measured)
1383    }
1384
1385    /// The measurement of `node_id` and the kind of node it is.
1386    fn measure_node_untimed(
1387        self: &Rc<Self>,
1388        node_id: NodeId,
1389        constraints: Constraints,
1390    ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1391        if let Some(measured) = self.measure_layout_node(node_id, constraints)? {
1392            return Ok(("layout", measured));
1393        }
1394        self.measure_subcompose_node(node_id, constraints)
1395    }
1396
1397    fn try_measure_subcompose(
1398        self: &Rc<Self>,
1399        node_id: NodeId,
1400        constraints: Constraints,
1401    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1402        let (node_handle, resolved_modifiers, spare) = {
1403            let Ok(mut applier) = self.applier.try_borrow_typed() else {
1404                return Ok(None);
1405            };
1406            let node = match applier.get_mut(node_id) {
1407                Ok(node) => node,
1408                Err(NodeError::Missing { .. }) => return Ok(None),
1409                Err(err) => return Err(err),
1410            };
1411            let any = node.as_any_mut();
1412            if let Some(subcompose) = any.downcast_mut::<SubcomposeLayoutNode>() {
1413                let handle = subcompose.handle();
1414                let resolved_modifiers = handle
1415                    .resolved_modifiers()
1416                    .on_device_grid(subcompose.density().density());
1417                let spare = MeasuredSpare::take(subcompose.cache());
1418                (handle, resolved_modifiers, spare)
1419            } else {
1420                return Ok(None);
1421            }
1422        };
1423
1424        let props = resolved_modifiers.layout_properties();
1425        let padding = resolved_modifiers.padding();
1426        let offset = resolved_modifiers.offset();
1427        let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1428
1429        if let DimensionConstraint::Points(width) = props.width() {
1430            let constrained_width = width - padding.horizontal_sum();
1431            inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1432            inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1433        }
1434        if let DimensionConstraint::Points(height) = props.height() {
1435            let constrained_height = height - padding.vertical_sum();
1436            inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1437            inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1438        }
1439
1440        let measure_error = RefCell::new(None);
1441        let measured_children = node_handle.measured_children_scratch();
1442        let measure_result = self.subcompose_policy_result(
1443            &node_handle,
1444            node_id,
1445            inner_constraints,
1446            &measured_children,
1447            &measure_error,
1448        )?;
1449
1450        if let Some(err) = measure_error.borrow_mut().take() {
1451            return Err(err);
1452        }
1453
1454        let cranpose_ui_layout::MeasureResult {
1455            size: measured_size,
1456            alignment_lines: explicit_lines,
1457            placements,
1458        } = measure_result;
1459
1460        let mut width = measured_size.width + padding.horizontal_sum();
1461        let mut height = measured_size.height + padding.vertical_sum();
1462
1463        width = resolve_dimension(
1464            width,
1465            props.width(),
1466            props.min_width(),
1467            props.max_width(),
1468            constraints.min_width,
1469            constraints.max_width,
1470        );
1471        height = resolve_dimension(
1472            height,
1473            props.height(),
1474            props.min_height(),
1475            props.max_height(),
1476            constraints.min_height,
1477            constraints.max_height,
1478        );
1479
1480        let MeasuredSpare {
1481            node: spare,
1482            mut children,
1483        } = spare;
1484        children.reserve_exact(placements.len());
1485        let mut alignment_lines = AlignmentLines::default();
1486        let mut measured_children_by_id = measured_children.borrow_mut();
1487
1488        // One borrow of the applier places every child; measuring a child
1489        // the pass did not measure borrows it on its own.
1490        let mut applier = self.applier.try_borrow_typed().ok();
1491        if let Some(applier) = applier.as_mut() {
1492            let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1493                parent_node.set_measured_size(Size { width, height }, &self.context);
1494                parent_node.clear_needs_measure();
1495                parent_node.clear_needs_layout();
1496            });
1497        }
1498
1499        // A lazy policy placed its items' roots with their measurements; the
1500        // other placements are looked up.
1501        let placed_roots = node_handle.take_placed_roots();
1502        let mut carried = PlacedRootMeasurements::new(&placed_roots);
1503        for placement in &placements {
1504            let looked_up;
1505            let child: &MeasuredNode = if let Some(measured) = carried.next_measurement() {
1506                measured
1507            } else {
1508                looked_up =
1509                    if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1510                        measured
1511                    } else {
1512                        drop(applier.take());
1513                        let measured = self.measure_node(placement.node_id, inner_constraints)?;
1514                        applier = self.applier.try_borrow_typed().ok();
1515                        measured
1516                    };
1517                &looked_up
1518            };
1519            let policy_position = Point {
1520                x: padding.left + placement.x,
1521                y: padding.top + placement.y,
1522            };
1523            let retained_position = Point {
1524                x: policy_position.x + child.offset.x,
1525                y: policy_position.y + child.offset.y,
1526            };
1527            alignment_lines.merge(
1528                child
1529                    .alignment_lines_for_parent()
1530                    .translated(policy_position.y),
1531            );
1532
1533            if let Some(applier) = applier.as_mut()
1534                && applier
1535                    .with_node::<LayoutNode, _>(placement.node_id, |node| {
1536                        node.set_position(retained_position, Some(node_id), &self.context);
1537                    })
1538                    .is_err()
1539            {
1540                let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1541                    node.set_position(retained_position, Some(node_id), &self.context);
1542                });
1543            }
1544
1545            children.push(MeasuredChild::new(child, policy_position));
1546        }
1547        drop(applier);
1548
1549        node_handle.recycle_placed_roots(placed_roots);
1550        node_handle.recycle_placement_scratch(placements);
1551
1552        Ok(Some(
1553            MeasuredNode::new(
1554                node_id,
1555                Size { width, height },
1556                offset,
1557                Point::default(),
1558                children,
1559            )
1560            .with_alignment_lines(
1561                alignment_lines
1562                    .with_overrides(explicit_lines.translated(padding.top))
1563                    .translated(offset.y),
1564            )
1565            .into_rc(spare),
1566        ))
1567    }
1568
1569    // The result of `node_handle`'s policy at `constraints`: from the slots
1570    // of its last pass where its retained measure policy keeps them, else
1571    // from a pass that composes, which puts the children it measures in
1572    // `measured_children` and its first failure in `measure_error`.
1573    fn subcompose_policy_result(
1574        self: &Rc<Self>,
1575        node_handle: &SubcomposeLayoutNodeHandle,
1576        node_id: NodeId,
1577        constraints: Constraints,
1578        measured_children: &MeasuredChildren,
1579        measure_error: &RefCell<Option<NodeError>>,
1580    ) -> Result<cranpose_ui_layout::MeasureResult, NodeError> {
1581        let applier_host: Rc<dyn ApplierHost> = Rc::clone(&self.applier) as Rc<dyn ApplierHost>;
1582        if let Some(result) = node_handle.measure_retained(&*applier_host, node_id, constraints) {
1583            return Ok(result);
1584        }
1585        let runtime_handle = {
1586            let mut runtime_handle = self.runtime_handle.borrow_mut();
1587            if runtime_handle.is_none()
1588                && let Ok(applier) = self.applier.try_borrow_typed()
1589            {
1590                *runtime_handle = applier.runtime_handle();
1591            }
1592            runtime_handle.clone().ok_or(NodeError::MissingContext {
1593                id: node_id,
1594                reason: "runtime handle required for subcomposition",
1595            })?
1596        };
1597        node_handle.measure_with_callbacks(
1598            &applier_host,
1599            &runtime_handle,
1600            node_id,
1601            constraints,
1602            MeasureCallbacks {
1603                measurer: Box::new(|child_id: NodeId, child_constraints: Constraints| {
1604                    match self.measure_node(child_id, child_constraints) {
1605                        Ok(measured) => {
1606                            measured_children
1607                                .borrow_mut()
1608                                .insert(child_id, Rc::clone(&measured));
1609                            let size = measured.size_for_parent();
1610                            Placeable::value(size.width, size.height, child_id)
1611                                .with_alignment_lines(measured.alignment_lines_for_parent())
1612                        }
1613                        Err(err) => {
1614                            let mut slot = measure_error.borrow_mut();
1615                            if slot.is_none() {
1616                                *slot = Some(err);
1617                            }
1618                            Placeable::value(0.0, 0.0, child_id)
1619                        }
1620                    }
1621                }),
1622                retained_measure_lookup: Box::new(|child_id| {
1623                    measured_children.borrow().get(&child_id).cloned()
1624                }),
1625                error: measure_error,
1626            },
1627        )
1628    }
1629
1630    fn measure_layout_node(
1631        self: &Rc<Self>,
1632        node_id: NodeId,
1633        constraints: Constraints,
1634    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1635        let mut keep = true;
1636        let (mut applier, measure) = loop {
1637            let Ok(mut applier) = self.applier.try_borrow_typed() else {
1638                return Ok(None);
1639            };
1640            match applier.with_node::<LayoutNode, _>(node_id, |node| {
1641                self.visit_layout_node(node, constraints, keep)
1642            }) {
1643                Ok(LayoutNodeVisit::Measure(measure)) => break (applier, measure),
1644                Ok(LayoutNodeVisit::Cached(measured)) => return Ok(Some(measured)),
1645                Ok(LayoutNodeVisit::Keep(node)) => {
1646                    drop(applier);
1647                    if let Some(kept) = self.keep_measurement(constraints, node)? {
1648                        return Ok(Some(kept));
1649                    }
1650                    keep = false;
1651                }
1652                Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
1653                Err(err) => return Err(err),
1654            }
1655        };
1656        let LayoutNodeMeasure {
1657            parts,
1658            chain,
1659            mut pools,
1660            spare,
1661        } = measure;
1662
1663        let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
1664            state: parts.runtime_state(),
1665        };
1666        self.bind_layout_children(
1667            &mut applier,
1668            (node_id, parts.runtime_state()),
1669            &pools.child_ids,
1670            ChildPass::Measure,
1671        )?;
1672        drop(applier);
1673
1674        let runtime_state = parts.runtime_state().borrow();
1675        let measurement = self.measure_through_modifier_chain(
1676            node_id,
1677            &runtime_state,
1678            chain,
1679            constraints,
1680            &mut pools.placements,
1681            &mut pools.placement_indices,
1682        );
1683
1684        if let Some(err) = runtime_state.take_child_error() {
1685            for child_state in runtime_state.child_states() {
1686                child_state.measured.take();
1687            }
1688            self.with_applier_result(|applier| {
1689                applier.with_node::<LayoutNode, _>(node_id, |node| {
1690                    if runtime_state.take_moved_coordinators() {
1691                        node.note_geometry_change(node_id, &self.context);
1692                    }
1693                })
1694            })
1695            .ok();
1696            return Err(err);
1697        }
1698
1699        let MeasuredSpare {
1700            node: spare,
1701            mut children,
1702        } = spare;
1703        runtime_state.measured_children(
1704            &pools.placements,
1705            &pools.placement_indices,
1706            measurement.content_offset,
1707            &mut children,
1708        );
1709        let measured = MeasuredNode::new(
1710            node_id,
1711            measurement.size,
1712            measurement.offset,
1713            measurement.content_offset,
1714            children,
1715        )
1716        .with_alignment_lines(measurement.alignment_lines)
1717        .with_window_root(measurement.window_root)
1718        .into_rc(spare);
1719
1720        self.with_applier_result(|applier| {
1721            applier.with_node::<LayoutNode, _>(node_id, |node| {
1722                node.cache().store_held_measurement(
1723                    constraints,
1724                    Rc::clone(&measured),
1725                    measurement.hold,
1726                    measurement.ignores_child_sizes,
1727                );
1728                if runtime_state.take_moved_coordinators() {
1729                    node.note_geometry_change(node_id, &self.context);
1730                }
1731                node.clear_needs_measure();
1732                node.clear_needs_layout();
1733                node.set_measured_size(measurement.size, &self.context);
1734                node.set_content_offset(measurement.content_offset);
1735            })
1736        })
1737        .ok();
1738
1739        Ok(Some(measured))
1740    }
1741
1742    /// The intrinsic size `kind` names of a layout node, through its modifier
1743    /// chain and measure policy, as Compose computes it: no measure, no
1744    /// placement, and the node's measurement cache stays as it was. `None`
1745    /// when the node is not a layout node.
1746    fn layout_node_intrinsic(
1747        self: &Rc<Self>,
1748        node_id: NodeId,
1749        kind: IntrinsicKind,
1750    ) -> Result<Option<f32>, NodeError> {
1751        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1752            return Ok(None);
1753        };
1754        let mut pools = VecPools::acquire(&self.frame_arena);
1755        let bound = applier.with_node::<LayoutNode, _>(node_id, |node| {
1756            let parts = Rc::clone(node.layout_parts());
1757            let chain = parts.runtime_state().borrow_mut().bind_node(node);
1758            pools.child_ids.extend_from_slice(&node.children);
1759            (parts, chain)
1760        });
1761        let (parts, chain) = match bound {
1762            Ok(bound) => bound,
1763            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
1764            Err(err) => return Err(err),
1765        };
1766        let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
1767            state: parts.runtime_state(),
1768        };
1769        self.bind_layout_children(
1770            &mut applier,
1771            (node_id, parts.runtime_state()),
1772            &pools.child_ids,
1773            ChildPass::Intrinsics,
1774        )?;
1775        drop(applier);
1776
1777        let runtime_state = parts.runtime_state().borrow();
1778        let value = if chain.uses_chain {
1779            let scope = crate::density::DensityMeasureScope::new(chain.density);
1780            let frame = CoordinatorFrame::new(
1781                &runtime_state.measure_policy,
1782                &scope,
1783                runtime_state.child_measurables(),
1784                runtime_state.child_states(),
1785                &mut pools.placements,
1786                &mut pools.placement_indices,
1787            );
1788            runtime_state
1789                .coordinator_chain
1790                .intrinsic_from(0, &frame, kind)
1791        } else {
1792            policy_intrinsic(
1793                runtime_state.measure_policy.as_ref(),
1794                runtime_state.child_measurables(),
1795                kind,
1796            )
1797        };
1798        match runtime_state.take_child_error() {
1799            Some(err) => Err(err),
1800            None => Ok(Some(value)),
1801        }
1802    }
1803
1804    /// The measurement of a node whose own inputs did not change since it
1805    /// measured at `constraints`: only nodes below it are dirty.
1806    fn measurement_to_keep(
1807        &self,
1808        node: &LayoutNode,
1809        constraints: Constraints,
1810    ) -> Option<Rc<MeasuredNode>> {
1811        if node.needs_measure() || node.needs_layout() || node.is_virtual() {
1812            return None;
1813        }
1814        let cache = node.cache();
1815        if cache.epoch() < self.cache_floor {
1816            return None;
1817        }
1818        cache.get_measurement(constraints)
1819    }
1820
1821    // Measures again the children of `node_id` that changed, at their last
1822    // constraints, and keeps the node's measurement when each measures the
1823    // same for its parent; `None` when the node has to measure again. Out of
1824    // line: few frames keep a measurement, and the measure stays small.
1825    #[inline(never)]
1826    fn keep_measurement(
1827        self: &Rc<Self>,
1828        constraints: Constraints,
1829        node: KeptNode,
1830    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1831        let KeptNode {
1832            measured,
1833            parts,
1834            ignores_child_sizes,
1835        } = node;
1836        let mut next_state = 0;
1837        let kept = self.remeasure_changed_children(
1838            &measured,
1839            ignores_child_sizes,
1840            |child_id| self.layout_child_change(parts.runtime_state(), &mut next_state, child_id),
1841            |_| {},
1842        )?;
1843        let Some(KeptChildren {
1844            measured: kept,
1845            resized,
1846        }) = kept
1847        else {
1848            return Ok(None);
1849        };
1850        let cache = parts.cache();
1851        if resized {
1852            cache.store_resized_measurement(constraints, Rc::clone(&kept));
1853        } else {
1854            cache.store_measurement(constraints, Rc::clone(&kept));
1855        }
1856        cache.clear_layout_dirt();
1857        Ok(Some(kept))
1858    }
1859
1860    /// Walks the children of a kept measurement, measures again each one
1861    /// `change` finds changed, at the constraints it last had, and keeps the
1862    /// measurement when each of them reads the same for it, or, when the
1863    /// measurement `ignores_child_sizes`, the same but for its size. `None`
1864    /// as soon as `change` refuses a child or one measures differently for
1865    /// its parent.
1866    fn remeasure_changed_children(
1867        self: &Rc<Self>,
1868        cached: &Rc<MeasuredNode>,
1869        ignores_child_sizes: bool,
1870        mut change: impl FnMut(NodeId) -> Result<ChildChange, NodeError>,
1871        mut remeasured: impl FnMut(NodeId),
1872    ) -> Result<Option<KeptChildren>, NodeError> {
1873        let mut resized = false;
1874        for previous in &cached.children {
1875            let child = match change(previous.node_id)? {
1876                ChildChange::Unchanged => continue,
1877                ChildChange::Refuse => return Ok(None),
1878                ChildChange::Changed(child) => child,
1879            };
1880            let measured = self.measure_node(previous.node_id, child.constraints)?;
1881            remeasured(previous.node_id);
1882            let now = ForParent::of(&measured);
1883            if !previous.for_parent.same_as(&now) {
1884                if !(ignores_child_sizes && previous.for_parent.same_but_size(&now)) {
1885                    return Ok(None);
1886                }
1887                resized = true;
1888            }
1889            if child.placed {
1890                self.place_where_it_was(cached.node_id, previous.node_id);
1891            }
1892        }
1893        Ok(Some(KeptChildren {
1894            measured: Rc::clone(cached),
1895            resized,
1896        }))
1897    }
1898
1899    /// What the walk over a kept layout node finds of a child, from the
1900    /// record of the node's last measure, which `next_state` walks in order.
1901    /// A changed child refuses the keep when its parent data changed, the
1902    /// node read its intrinsic sizes, or the node did not measure it.
1903    fn layout_child_change(
1904        &self,
1905        runtime_state: &RefCell<LayoutRuntimeState>,
1906        next_state: &mut usize,
1907        child_id: NodeId,
1908    ) -> Result<ChildChange, NodeError> {
1909        let runtime_state = runtime_state.borrow();
1910        let Some((offset, state)) = runtime_state
1911            .child_states()
1912            .get(*next_state..)
1913            .unwrap_or_default()
1914            .iter()
1915            .enumerate()
1916            .find(|(_, state)| state.node_id == child_id)
1917        else {
1918            return Ok(ChildChange::Refuse);
1919        };
1920        *next_state += offset + 1;
1921        // A clean child's flags sit in the cache its record shares with it.
1922        if read_cell(&state.parts, |parts| {
1923            parts
1924                .as_ref()
1925                .is_some_and(|parts| !parts.cache().dirt().any())
1926        }) {
1927            return Ok(ChildChange::Unchanged);
1928        }
1929        self.child_change(child_id, |view| {
1930            let parent_data_changed = view.parent_data.as_ref().is_some_and(|data| {
1931                read_cell(&state.parent_data, |bound| {
1932                    bound.as_ref().is_none_or(|bound| {
1933                        runtime_state
1934                            .measure_policy
1935                            .reads_parent_data_change(bound, data)
1936                    })
1937                })
1938            });
1939            match state.measured_constraints.get() {
1940                Some(constraints) if !state.read_intrinsics.get() && !parent_data_changed => {
1941                    ChildChange::Changed(ChangedChild {
1942                        constraints,
1943                        placed: view.placed,
1944                    })
1945                }
1946                _ => ChildChange::Refuse,
1947            }
1948        })
1949    }
1950
1951    /// Stores a subcompose node's new measurement in its cache, as a layout
1952    /// node's measure stores its own.
1953    fn store_subcompose_measurement(
1954        &self,
1955        node_id: NodeId,
1956        constraints: Constraints,
1957        measured: &Rc<MeasuredNode>,
1958    ) {
1959        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1960            return;
1961        };
1962        let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1963            node.cache()
1964                .store_measurement(constraints, Rc::clone(measured));
1965        });
1966    }
1967
1968    /// Places a child of the kept node `parent` where it was: the node's
1969    /// policy would place it there again.
1970    fn place_where_it_was(&self, parent: NodeId, child_id: NodeId) {
1971        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1972            return;
1973        };
1974        let _ = read_live_layout_node(&mut applier, child_id, |child| {
1975            child.stay_placed(parent, &self.context);
1976        });
1977    }
1978
1979    fn visit_layout_node(
1980        &self,
1981        node: &mut LayoutNode,
1982        constraints: Constraints,
1983        keep: bool,
1984    ) -> LayoutNodeVisit<'_> {
1985        node.clear_placed(&self.context);
1986        if keep && let Some(measured) = self.measurement_to_keep(node, constraints) {
1987            return LayoutNodeVisit::Keep(KeptNode {
1988                ignores_child_sizes: node.cache().ignores_child_sizes_of(&measured),
1989                measured,
1990                parts: Rc::clone(node.layout_parts()),
1991            });
1992        }
1993        let cache = node.cache();
1994        cache.activate(self.cache_epoch);
1995        if !Node::layout_dirty(node)
1996            && let Some(cached) = cache.get_measurement(constraints)
1997        {
1998            node.clear_needs_measure();
1999            node.clear_needs_layout();
2000            return LayoutNodeVisit::Cached(cached);
2001        }
2002
2003        let spare = MeasuredSpare::take(cache);
2004        let parts = Rc::clone(node.layout_parts());
2005        let chain = parts.runtime_state().borrow_mut().bind_node(node);
2006        let mut pools = VecPools::acquire(&self.frame_arena);
2007        pools.child_ids.extend_from_slice(&node.children);
2008        LayoutNodeVisit::Measure(LayoutNodeMeasure {
2009            parts,
2010            chain,
2011            pools,
2012            spare,
2013        })
2014    }
2015
2016    fn bind_layout_children(
2017        self: &Rc<Self>,
2018        applier: &mut MemoryApplier,
2019        (node_id, runtime_state): (NodeId, &RefCell<LayoutRuntimeState>),
2020        child_ids: &[NodeId],
2021        pass: ChildPass,
2022    ) -> Result<(), NodeError> {
2023        let mut runtime_state = runtime_state.borrow_mut();
2024        runtime_state.bind_children(self, node_id, pass, child_ids.len());
2025        let mut bound = 0;
2026        for &child_id in child_ids {
2027            if self.bind_layout_child(applier, &mut runtime_state, bound, child_id)? {
2028                bound += 1;
2029            }
2030        }
2031        runtime_state.truncate_children(bound);
2032        Ok(())
2033    }
2034
2035    fn bind_layout_child(
2036        &self,
2037        applier: &mut MemoryApplier,
2038        runtime_state: &mut LayoutRuntimeState,
2039        position: usize,
2040        child_id: NodeId,
2041    ) -> Result<bool, NodeError> {
2042        let bound = applier.with_node::<LayoutNode, _>(child_id, |child| {
2043            runtime_state.child_state_at(position, child_id).bind(
2044                LayoutChildBinding {
2045                    parts: child.layout_parts(),
2046                    layout_node: true,
2047                    parent_data: Some(child.parent_data()),
2048                    dirty: child.needs_layout() || child.needs_measure(),
2049                    descendant_dirty: Node::descendant_needs_layout(child),
2050                },
2051                self,
2052            );
2053        });
2054        match bound {
2055            Ok(()) => Ok(true),
2056            Err(NodeError::TypeMismatch { .. }) => {
2057                match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2058                    runtime_state.child_state_at(position, child_id).bind(
2059                        LayoutChildBinding {
2060                            parts: child.layout_parts(),
2061                            layout_node: false,
2062                            parent_data: Some(child.parent_data()),
2063                            dirty: child.needs_layout() || child.needs_measure(),
2064                            descendant_dirty: Node::descendant_needs_layout(child),
2065                        },
2066                        self,
2067                    );
2068                }) {
2069                    Ok(()) => Ok(true),
2070                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(false),
2071                    Err(err) => Err(err),
2072                }
2073            }
2074            Err(NodeError::Missing { .. }) => Ok(false),
2075            Err(err) => Err(err),
2076        }
2077    }
2078
2079    fn measure_through_modifier_chain(
2080        &self,
2081        node_id: NodeId,
2082        runtime_state: &LayoutRuntimeState,
2083        chain: ModifierChainInputs,
2084        constraints: Constraints,
2085        placements: &mut Vec<Placement>,
2086        placement_indices: &mut Vec<usize>,
2087    ) -> ModifierChainMeasurement {
2088        let scope = crate::density::DensityMeasureScope::new(chain.density);
2089
2090        if !chain.uses_chain {
2091            let measurement = runtime_state.measure_policy.measure_into(
2092                &scope,
2093                runtime_state.child_measurables(),
2094                constraints,
2095                placements,
2096            );
2097            return ModifierChainMeasurement {
2098                size: measurement.size,
2099                alignment_lines: inherited_alignment_lines(
2100                    runtime_state.child_states(),
2101                    placements,
2102                    placement_indices,
2103                )
2104                .with_overrides(measurement.alignment_lines),
2105                content_offset: Point::default(),
2106                offset: chain.offset,
2107                window_root: chain.window_root,
2108                hold: if chain.window_root {
2109                    None
2110                } else {
2111                    runtime_state.measure_policy.measure_hold(
2112                        runtime_state.child_measurables(),
2113                        constraints,
2114                        measurement.size,
2115                    )
2116                },
2117                ignores_child_sizes: !chain.window_root
2118                    && runtime_state
2119                        .measure_policy
2120                        .ignores_child_sizes(runtime_state.child_measurables(), constraints),
2121            };
2122        }
2123
2124        let frame = CoordinatorFrame::new(
2125            &runtime_state.measure_policy,
2126            &scope,
2127            runtime_state.child_measurables(),
2128            runtime_state.child_states(),
2129            placements,
2130            placement_indices,
2131        );
2132        let placeable = runtime_state
2133            .coordinator_chain
2134            .measure_from(0, &frame, constraints);
2135        let (content_x, content_y) = placeable.content_offset();
2136
2137        let invalidations = frame.take_invalidations();
2138        if !invalidations.is_empty() {
2139            self.with_applier_result(|applier| {
2140                applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2141                    for kind in invalidations {
2142                        match kind {
2143                            InvalidationKind::Layout => layout_node.mark_needs_measure(),
2144                            InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2145                            InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2146                            InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2147                            InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2148                        }
2149                    }
2150                })
2151            })
2152            .ok();
2153        }
2154
2155        ModifierChainMeasurement {
2156            alignment_lines: placeable.alignment_lines(),
2157            size: Size {
2158                width: placeable.width(),
2159                height: placeable.height(),
2160            },
2161            content_offset: Point {
2162                x: content_x - chain.offset.x,
2163                y: content_y - chain.offset.y,
2164            },
2165            offset: chain.offset,
2166            window_root: chain.window_root,
2167            hold: if chain.window_root {
2168                None
2169            } else {
2170                runtime_state.coordinator_chain.measured_hold(
2171                    cranpose_ui_layout::MeasureScope::density(&scope),
2172                    runtime_state.measure_policy.as_ref(),
2173                    runtime_state.child_measurables(),
2174                )
2175            },
2176            ignores_child_sizes: !chain.window_root
2177                && runtime_state.coordinator_chain.ignores_child_sizes(
2178                    runtime_state.measure_policy.as_ref(),
2179                    runtime_state.child_measurables(),
2180                ),
2181        }
2182    }
2183}
2184
2185#[derive(Debug, Clone)]
2186pub(crate) struct MeasuredNode {
2187    node_id: NodeId,
2188    size: Size,
2189    offset: Point,
2190    content_offset: Point,
2191    alignment_lines: AlignmentLines,
2192    children: Vec<MeasuredChild>,
2193    window_root: bool,
2194}
2195
2196impl MeasuredNode {
2197    fn new(
2198        node_id: NodeId,
2199        size: Size,
2200        offset: Point,
2201        content_offset: Point,
2202        children: Vec<MeasuredChild>,
2203    ) -> Self {
2204        Self {
2205            node_id,
2206            size,
2207            offset,
2208            content_offset,
2209            alignment_lines: AlignmentLines::default(),
2210            children,
2211            window_root: false,
2212        }
2213    }
2214
2215    fn with_window_root(mut self, window_root: bool) -> Self {
2216        self.window_root = window_root;
2217        self
2218    }
2219
2220    fn with_alignment_lines(mut self, alignment_lines: AlignmentLines) -> Self {
2221        self.alignment_lines = alignment_lines;
2222        self
2223    }
2224
2225    pub(crate) fn alignment_lines_for_parent(&self) -> AlignmentLines {
2226        if self.window_root {
2227            AlignmentLines::default()
2228        } else {
2229            self.alignment_lines
2230        }
2231    }
2232
2233    pub(crate) fn size_for_parent(&self) -> Size {
2234        if self.window_root {
2235            Size::new(0.0, 0.0)
2236        } else {
2237            self.size
2238        }
2239    }
2240
2241    pub(crate) fn size(&self) -> Size {
2242        self.size
2243    }
2244
2245    /// The measurement in `spare`'s memory, or in a new allocation when
2246    /// there is no spare.
2247    fn into_rc(self, spare: Option<Rc<Self>>) -> Rc<Self> {
2248        let Some(mut spare) = spare else {
2249            return Rc::new(self);
2250        };
2251        match Rc::get_mut(&mut spare) {
2252            Some(slot) => *slot = self,
2253            None => return Rc::new(self),
2254        }
2255        spare
2256    }
2257}
2258
2259/// What a parent's measurement keeps of a child: which child, where the
2260/// policy placed it, and what of the child's measurement the policy read.
2261/// The measurement itself stays with the child's cache, its one holder, so
2262/// the child's next measure can fill it in place.
2263#[derive(Debug, Clone, Copy)]
2264struct MeasuredChild {
2265    node_id: NodeId,
2266    offset: Point,
2267    for_parent: ForParent,
2268}
2269
2270impl MeasuredChild {
2271    fn new(measured: &MeasuredNode, offset: Point) -> Self {
2272        Self {
2273            node_id: measured.node_id,
2274            offset,
2275            for_parent: ForParent::of(measured),
2276        }
2277    }
2278}
2279
2280/// What a parent's policy reads of a child's measurement: the size and the
2281/// baselines, and the offset it places the child by.
2282#[derive(Debug, Clone, Copy)]
2283struct ForParent {
2284    size: Size,
2285    offset: Point,
2286    alignment_lines: AlignmentLines,
2287}
2288
2289impl ForParent {
2290    fn of(measured: &MeasuredNode) -> Self {
2291        Self {
2292            size: measured.size_for_parent(),
2293            offset: measured.offset,
2294            alignment_lines: measured.alignment_lines_for_parent(),
2295        }
2296    }
2297
2298    /// Whether a parent's policy, reading `other` in place of this, would
2299    /// lay its children out the same.
2300    fn same_as(&self, other: &Self) -> bool {
2301        self.size == other.size && self.same_but_size(other)
2302    }
2303
2304    /// Whether `other` reads the same as this but for its size.
2305    fn same_but_size(&self, other: &Self) -> bool {
2306        self.offset == other.offset
2307            && self.alignment_lines.first_baseline() == other.alignment_lines.first_baseline()
2308            && self.alignment_lines.last_baseline() == other.alignment_lines.last_baseline()
2309    }
2310}
2311
2312/// The intrinsic size `kind` names, of a measure policy over `measurables`.
2313fn policy_intrinsic(
2314    policy: &dyn MeasurePolicy,
2315    measurables: &[Box<dyn Measurable>],
2316    kind: IntrinsicKind,
2317) -> f32 {
2318    match kind {
2319        IntrinsicKind::MinWidth(height) => policy.min_intrinsic_width(measurables, height),
2320        IntrinsicKind::MaxWidth(height) => policy.max_intrinsic_width(measurables, height),
2321        IntrinsicKind::MinHeight(width) => policy.min_intrinsic_height(measurables, width),
2322        IntrinsicKind::MaxHeight(width) => policy.max_intrinsic_height(measurables, width),
2323    }
2324}
2325
2326fn inherited_alignment_lines(
2327    child_states: &[Rc<LayoutChildMeasureState>],
2328    placements: &[Placement],
2329    placement_indices: &mut Vec<usize>,
2330) -> AlignmentLines {
2331    placement_indices.clear();
2332    let in_child_order = placements.len() == child_states.len()
2333        && placements
2334            .iter()
2335            .zip(child_states)
2336            .all(|(placement, child)| placement.node_id == child.node_id);
2337    if !in_child_order {
2338        placement_indices.extend(0..placements.len());
2339        placement_indices.sort_unstable_by_key(|&index| (placements[index].node_id, index));
2340    }
2341    let mut lines = AlignmentLines::default();
2342    for (index, child) in child_states.iter().enumerate() {
2343        let placement = placement_for_child(placements, placement_indices, index, child.node_id);
2344        read_cell(&child.measured, |measured| {
2345            if let Some(measured) = measured {
2346                lines.merge(
2347                    measured
2348                        .alignment_lines_for_parent()
2349                        .translated(child.placement_position(placement).y),
2350                );
2351            }
2352        });
2353    }
2354    lines
2355}
2356
2357fn placement_for_child<'a>(
2358    placements: &'a [Placement],
2359    indices: &[usize],
2360    child_index: usize,
2361    node_id: NodeId,
2362) -> Option<&'a Placement> {
2363    let index = if indices.is_empty() {
2364        child_index
2365    } else {
2366        let first = indices.partition_point(|&index| placements[index].node_id < node_id);
2367        *indices.get(first)?
2368    };
2369    placements
2370        .get(index)
2371        .filter(|placement| placement.node_id == node_id)
2372}
2373
2374pub(crate) struct LayoutRuntimeState {
2375    /// `None` until the node first binds a child: most nodes are leaves.
2376    children: Option<Box<LayoutChildren>>,
2377    coordinator_chain: CoordinatorChain,
2378    measure_policy: Rc<dyn MeasurePolicy>,
2379}
2380
2381/// What a node keeps for measuring its children, in their order.
2382struct LayoutChildren {
2383    ids: Vec<NodeId>,
2384    states: Vec<Rc<LayoutChildMeasureState>>,
2385    measurables: Vec<Box<dyn Measurable>>,
2386    frame: Rc<LayoutChildFrame>,
2387}
2388
2389impl LayoutChildren {
2390    /// Room for exactly `count` children, where growth would make room for
2391    /// four in each of the three lists. Children added later grow the lists
2392    /// as usual.
2393    fn with_capacity(count: usize) -> Self {
2394        Self {
2395            ids: Vec::with_capacity(count),
2396            states: Vec::with_capacity(count),
2397            measurables: Vec::with_capacity(count),
2398            frame: Rc::default(),
2399        }
2400    }
2401
2402    /// Puts the state of `child_id` at `position`: the one it had further
2403    /// on, or a new one.
2404    #[inline(never)]
2405    fn bind_at(&mut self, position: usize, child_id: NodeId) {
2406        let from = match self.ids[position..].iter().position(|&id| id == child_id) {
2407            Some(offset) => position + offset,
2408            None => {
2409                let state = LayoutChildMeasureState::new(child_id, Rc::clone(&self.frame));
2410                self.ids.push(child_id);
2411                self.states.push(Rc::clone(&state));
2412                self.measurables
2413                    .push(Box::new(LayoutChildMeasurable::new(state)));
2414                self.ids.len() - 1
2415            }
2416        };
2417        self.ids.swap(position, from);
2418        self.states.swap(position, from);
2419        self.measurables.swap(position, from);
2420    }
2421}
2422
2423impl LayoutRuntimeState {
2424    pub(crate) fn new(measure_policy: Rc<dyn MeasurePolicy>) -> Self {
2425        Self {
2426            children: None,
2427            coordinator_chain: CoordinatorChain::default(),
2428            measure_policy,
2429        }
2430    }
2431
2432    fn bind_node(&mut self, node: &LayoutNode) -> ModifierChainInputs {
2433        if !Rc::ptr_eq(&self.measure_policy, &node.measure_policy) {
2434            self.measure_policy = Rc::clone(&node.measure_policy);
2435        }
2436        self.coordinator_chain.sync(node)
2437    }
2438
2439    fn child_states(&self) -> &[Rc<LayoutChildMeasureState>] {
2440        self.children
2441            .as_deref()
2442            .map_or(&[], |children| children.states.as_slice())
2443    }
2444
2445    fn child_measurables(&self) -> &[Box<dyn Measurable>] {
2446        self.children
2447            .as_deref()
2448            .map_or(&[], |children| children.measurables.as_slice())
2449    }
2450
2451    /// Binds the children's frame of the node `parent` to `builder` for a
2452    /// pass over `count` children. A node that never had one binds nothing.
2453    fn bind_children(
2454        &mut self,
2455        builder: &Rc<LayoutBuilderState>,
2456        parent: NodeId,
2457        pass: ChildPass,
2458        count: usize,
2459    ) {
2460        if count == 0 && self.children.is_none() {
2461            return;
2462        }
2463        self.children
2464            .get_or_insert_with(|| Box::new(LayoutChildren::with_capacity(count)))
2465            .frame
2466            .bind(builder, parent, pass);
2467    }
2468
2469    fn unbind_children(&self) {
2470        if let Some(children) = &self.children {
2471            children.frame.unbind();
2472        }
2473    }
2474
2475    /// The first error a child recorded in the pass, taken.
2476    fn take_child_error(&self) -> Option<NodeError> {
2477        self.children
2478            .as_ref()
2479            .and_then(|children| children.frame.error.borrow_mut().take())
2480    }
2481
2482    /// The state of the child at `position`, for a pass `bind_children`
2483    /// bound. Inlined: the child is nearly always where it was.
2484    #[inline]
2485    fn child_state_at(&mut self, position: usize, child_id: NodeId) -> &LayoutChildMeasureState {
2486        let children = self
2487            .children
2488            .get_or_insert_with(|| Box::new(LayoutChildren::with_capacity(position + 1)));
2489        if children.ids.get(position) != Some(&child_id) {
2490            children.bind_at(position, child_id);
2491        }
2492        &children.states[position]
2493    }
2494
2495    fn truncate_children(&mut self, len: usize) {
2496        if let Some(children) = &mut self.children {
2497            children.ids.truncate(len);
2498            children.states.truncate(len);
2499            children.measurables.truncate(len);
2500        }
2501    }
2502
2503    /// Puts the children the last measure measured into `measured_children`.
2504    fn measured_children(
2505        &self,
2506        placements: &[Placement],
2507        placement_indices: &[usize],
2508        content_offset: Point,
2509        measured_children: &mut Vec<MeasuredChild>,
2510    ) {
2511        let child_states = self.child_states();
2512        measured_children.reserve_exact(child_states.len());
2513        for (index, child_state) in child_states.iter().enumerate() {
2514            let Some(measured) = child_state.measured.take() else {
2515                continue;
2516            };
2517            let placement =
2518                placement_for_child(placements, placement_indices, index, child_state.node_id);
2519            let base_position = child_state.placement_position(placement);
2520            let position = Point {
2521                x: base_position.x + measured.offset.x,
2522                y: base_position.y + measured.offset.y,
2523            };
2524            // A policy that placed the child there in this pass, as one
2525            // that places its placeables does, left nothing to place.
2526            if placement.is_some() && child_state.last_position.get() != Some(position) {
2527                child_state.place_retained(position);
2528            }
2529            measured_children.push(MeasuredChild::new(
2530                &measured,
2531                Point {
2532                    x: content_offset.x + base_position.x,
2533                    y: content_offset.y + base_position.y,
2534                },
2535            ));
2536        }
2537    }
2538
2539    // Notes that a measure placed the node's coordinators, and says whether
2540    // one of them moved since the scene last heard of the node. A node
2541    // without layout modifiers moves only with its size, which the node
2542    // reports itself.
2543    fn take_moved_coordinators(&self) -> bool {
2544        self.coordinator_chain.take_moved()
2545    }
2546
2547    pub(crate) fn placed_coordinator_rect(
2548        &self,
2549        ordinal: usize,
2550        node_size: Size,
2551    ) -> Option<GeometryRect> {
2552        self.coordinator_chain.placed_rect(ordinal, node_size)
2553    }
2554
2555    #[cfg(test)]
2556    pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
2557        LayoutRuntimeDebugStats {
2558            child_ids: self
2559                .children
2560                .as_ref()
2561                .map(|children| children.ids.clone())
2562                .unwrap_or_default(),
2563            child_state_ptrs: self
2564                .child_states()
2565                .iter()
2566                .map(|state| Rc::as_ptr(state) as *const () as usize)
2567                .collect(),
2568            child_measurable_ptrs: self
2569                .child_measurables()
2570                .iter()
2571                .map(|measurable| {
2572                    measurable.as_ref() as *const dyn Measurable as *const () as usize
2573                })
2574                .collect(),
2575            child_measurable_count: self.child_measurables().len(),
2576            coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
2577            coordinator_node_count: self.coordinator_chain.debug_ptrs().len(),
2578        }
2579    }
2580}
2581
2582#[cfg(test)]
2583#[derive(Debug, Clone, PartialEq, Eq)]
2584pub(crate) struct LayoutRuntimeDebugStats {
2585    pub(crate) child_ids: Vec<NodeId>,
2586    pub(crate) child_state_ptrs: Vec<usize>,
2587    pub(crate) child_measurable_ptrs: Vec<usize>,
2588    pub(crate) child_measurable_count: usize,
2589    pub(crate) coordinator_node_ptrs: Vec<usize>,
2590    pub(crate) coordinator_node_count: usize,
2591}
2592
2593#[derive(Default)]
2594struct LayoutChildFrame {
2595    builder: RefCell<Option<Rc<LayoutBuilderState>>>,
2596    error: RefCell<Option<NodeError>>,
2597    pass: Cell<ChildPass>,
2598    /// The node whose children the frame binds, which places them.
2599    parent: Cell<Option<NodeId>>,
2600}
2601
2602/// What a node binds its children for.
2603#[derive(Clone, Copy, Default, PartialEq, Eq)]
2604enum ChildPass {
2605    /// A measure of the node, which records how it read each child, for a
2606    /// later keep of its measurement.
2607    #[default]
2608    Measure,
2609    /// An intrinsic size of the node, which its parent asks: the record of
2610    /// the node's last measure stays as it was.
2611    Intrinsics,
2612}
2613
2614impl LayoutChildFrame {
2615    fn bind(&self, builder: &Rc<LayoutBuilderState>, parent: NodeId, pass: ChildPass) {
2616        self.error.borrow_mut().take();
2617        *self.builder.borrow_mut() = Some(Rc::clone(builder));
2618        self.pass.set(pass);
2619        self.parent.set(Some(parent));
2620    }
2621
2622    fn measures(&self) -> bool {
2623        self.pass.get() == ChildPass::Measure
2624    }
2625
2626    fn unbind(&self) {
2627        self.builder.borrow_mut().take();
2628    }
2629
2630    fn record_error(&self, err: NodeError) {
2631        if self.builder.borrow().is_none() {
2632            return;
2633        }
2634        let mut slot = self.error.borrow_mut();
2635        if slot.is_none() {
2636            *slot = Some(err);
2637        }
2638    }
2639}
2640
2641struct LayoutChildBinding<'a> {
2642    parts: &'a Rc<LayoutParts>,
2643    // Whether the child is a layout node, whose size and placed flag its
2644    // parent's measure writes: a subcompose node writes its own.
2645    layout_node: bool,
2646    parent_data: Option<cranpose_ui_layout::ParentData>,
2647    /// The child's own layout or measure is dirty.
2648    dirty: bool,
2649    /// Only nodes below the child are dirty.
2650    descendant_dirty: bool,
2651}
2652
2653/// A parent's record of one child, for each child of every parent: its
2654/// shared parts sit in `Cell`s, read by take and put back, where a
2655/// `RefCell`'s borrow flag would take a word of every record.
2656struct LayoutChildMeasureState {
2657    node_id: NodeId,
2658    frame: Rc<LayoutChildFrame>,
2659    // The child's shared parts, `None` until the record is first bound.
2660    parts: Cell<Option<Rc<LayoutParts>>>,
2661    // Whether the record writes the child's size and placed flag: see
2662    // `LayoutChildBinding::layout_node`.
2663    layout_node: Cell<bool>,
2664    cache_epoch: Cell<PackedU64>,
2665    force_remeasure: Cell<bool>,
2666    parent_data: Cell<Option<cranpose_ui_layout::ParentData>>,
2667    /// The constraints the parent's last measure gave the child.
2668    measured_constraints: Cell<Option<Constraints>>,
2669    /// Whether the parent's last measure read the child's intrinsic sizes.
2670    read_intrinsics: Cell<bool>,
2671    measured: Cell<Option<Rc<MeasuredNode>>>,
2672    last_position: Cell<Option<Point>>,
2673}
2674
2675/// Runs `read` on what `cell` holds and puts it back. `read` must not reach
2676/// the same cell again.
2677pub(crate) fn read_cell<T: Default, R>(cell: &Cell<T>, read: impl FnOnce(&T) -> R) -> R {
2678    let value = cell.take();
2679    let result = read(&value);
2680    cell.set(value);
2681    result
2682}
2683
2684impl LayoutChildMeasureState {
2685    fn placement_position(&self, placement: Option<&Placement>) -> Point {
2686        placement
2687            .map(|placement| Point {
2688                x: placement.x,
2689                y: placement.y,
2690            })
2691            .or_else(|| self.last_position.get())
2692            .unwrap_or_default()
2693    }
2694
2695    fn new(node_id: NodeId, frame: Rc<LayoutChildFrame>) -> Rc<Self> {
2696        Rc::new(Self {
2697            node_id,
2698            frame,
2699            parts: Cell::new(None),
2700            layout_node: Cell::new(false),
2701            cache_epoch: Cell::default(),
2702            force_remeasure: Cell::new(true),
2703            parent_data: Cell::new(None),
2704            measured_constraints: Cell::new(None),
2705            read_intrinsics: Cell::new(false),
2706            measured: Cell::new(None),
2707            last_position: Cell::new(None),
2708        })
2709    }
2710
2711    /// Binds the child for a measure of its parent. A child with only dirty
2712    /// nodes below it keeps its cache: it measures again, but may keep its
2713    /// measurement.
2714    fn bind(&self, binding: LayoutChildBinding<'_>, pass: &LayoutBuilderState) {
2715        let cache = binding.parts.cache();
2716        let child_epoch = cache.epoch();
2717        let stale = binding.dirty || child_epoch < pass.cache_floor;
2718        let cache_epoch = if stale { pass.cache_epoch } else { child_epoch };
2719        cache.activate(cache_epoch);
2720        let parts = self.parts.take();
2721        let shared = parts
2722            .as_ref()
2723            .is_some_and(|current| Rc::ptr_eq(current, binding.parts));
2724        self.parts.set(if shared {
2725            parts
2726        } else {
2727            Some(Rc::clone(binding.parts))
2728        });
2729        self.layout_node.set(binding.layout_node);
2730        self.cache_epoch.set(PackedU64::new(cache_epoch));
2731        self.force_remeasure.set(stale || binding.descendant_dirty);
2732        self.parent_data.set(binding.parent_data);
2733        if self.frame.measures() {
2734            self.measured.take();
2735            self.last_position.set(None);
2736            self.measured_constraints.set(None);
2737            self.read_intrinsics.set(false);
2738        }
2739    }
2740
2741    /// Runs `f` on the child's layout state with the context of the pass it
2742    /// measures in; nothing while it is bound to no pass.
2743    fn with_pass_layout_state(&self, f: impl FnOnce(&mut LayoutState, &AppContext)) {
2744        if !self.layout_node.get() {
2745            return;
2746        }
2747        let builder = self.frame.builder.borrow();
2748        let Some(builder) = builder.as_ref() else {
2749            return;
2750        };
2751        read_cell(&self.parts, |parts| {
2752            if let Some(parts) = parts {
2753                f(&mut parts.layout_state().borrow_mut(), &builder.context);
2754            }
2755        });
2756    }
2757
2758    fn place_retained(&self, position: Point) {
2759        self.last_position.set(Some(position));
2760        let builder = self.frame.builder.borrow();
2761        let Some(builder) = builder.as_ref() else {
2762            return;
2763        };
2764        read_cell(&self.parts, |parts| {
2765            if let Some(parts) = parts {
2766                parts.layout_state().borrow_mut().place(
2767                    position,
2768                    self.frame.parent.get(),
2769                    &builder.context,
2770                );
2771            }
2772        });
2773    }
2774
2775    /// The intrinsic size of a child that is a layout node, from its
2776    /// modifier chain and measure policy, without measuring it. `None` for
2777    /// another node type, which measures for it.
2778    fn layout_intrinsic(&self, kind: IntrinsicKind) -> Option<f32> {
2779        let builder = self.frame.builder.borrow();
2780        let builder = builder.as_ref()?;
2781        match builder.layout_node_intrinsic(self.node_id, kind) {
2782            Ok(value) => value,
2783            Err(err) => {
2784                self.frame.record_error(err);
2785                Some(0.0)
2786            }
2787        }
2788    }
2789
2790    fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
2791        let builder = self.frame.builder.borrow();
2792        let builder = builder.as_ref().ok_or(NodeError::MissingContext {
2793            id: self.node_id,
2794            reason: "layout child applier not configured",
2795        })?;
2796        builder.measure_node(self.node_id, constraints)
2797    }
2798
2799    fn measure_cached(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
2800        if self.frame.measures() {
2801            self.measured_constraints.set(Some(constraints));
2802        }
2803        let parts = read_cell(&self.parts, Clone::clone);
2804        let cache = parts.as_deref().map(LayoutParts::cache);
2805        if let Some(cache) = cache {
2806            cache.activate(self.cache_epoch.get().get());
2807        }
2808        if !self.force_remeasure.get()
2809            && let Some(cached) = cache.and_then(|cache| cache.get_measurement(constraints))
2810        {
2811            // A measure clears the placed flag the parent's placement sets
2812            // again; a child it no longer places then leaves the scene.
2813            if self.frame.measures() {
2814                self.with_pass_layout_state(LayoutState::clear_placed);
2815            }
2816            return Some(cached);
2817        }
2818
2819        match self.perform_measure(constraints) {
2820            Ok(measured) => {
2821                self.force_remeasure.set(false);
2822                if let Some(cache) = cache {
2823                    cache.store_measurement(constraints, Rc::clone(&measured));
2824                }
2825                Some(measured)
2826            }
2827            Err(err) => {
2828                self.frame.record_error(err);
2829                None
2830            }
2831        }
2832    }
2833}
2834
2835struct LayoutChildMeasurable {
2836    state: Rc<LayoutChildMeasureState>,
2837}
2838
2839impl LayoutChildMeasurable {
2840    fn new(state: Rc<LayoutChildMeasureState>) -> Self {
2841        Self { state }
2842    }
2843
2844    fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
2845        if self.state.frame.builder.borrow().is_none() {
2846            return None;
2847        }
2848        read_cell(&self.state.parent_data, Clone::clone)
2849    }
2850
2851    fn intrinsic(
2852        &self,
2853        kind: IntrinsicKind,
2854        constraints: Constraints,
2855        extent: fn(Size) -> f32,
2856    ) -> f32 {
2857        let state = &self.state;
2858        if state.frame.measures() {
2859            state.read_intrinsics.set(true);
2860        }
2861        let parts = read_cell(&state.parts, Clone::clone);
2862        let cache = parts.as_deref().map(LayoutParts::cache);
2863        if let Some(cache) = cache {
2864            cache.activate(state.cache_epoch.get().get());
2865        }
2866        if !state.force_remeasure.get()
2867            && let Some(value) = cache.and_then(|cache| cache.get_intrinsic(&kind))
2868        {
2869            return value;
2870        }
2871        let value = match state.layout_intrinsic(kind) {
2872            Some(value) => value,
2873            None => state
2874                .measure_cached(constraints)
2875                .map_or(0.0, |node| extent(node.size_for_parent())),
2876        };
2877        if let Some(cache) = cache {
2878            cache.store_intrinsic(kind, value);
2879        }
2880        value
2881    }
2882}
2883
2884impl PlaceTarget for LayoutChildMeasureState {
2885    fn place(&self, x: f32, y: f32) {
2886        let internal_offset = read_cell(&self.measured, |measured| {
2887            measured
2888                .as_ref()
2889                .map(|measured| measured.offset)
2890                .unwrap_or_default()
2891        });
2892        self.place_retained(Point {
2893            x: x + internal_offset.x,
2894            y: y + internal_offset.y,
2895        });
2896    }
2897}
2898
2899impl Measurable for LayoutChildMeasurable {
2900    fn measured_hold(&self) -> Option<(Size, cranpose_ui_layout::ConstraintsHold)> {
2901        read_cell(&self.state.measured, |measured| {
2902            let measured = measured.as_ref()?;
2903            let hold = read_cell(&self.state.parts, |parts| {
2904                parts.as_ref()?.cache().hold_of(measured)
2905            })?;
2906            Some((measured.size_for_parent(), hold))
2907        })
2908    }
2909
2910    fn measure(&self, constraints: Constraints) -> Placeable {
2911        let state = &self.state;
2912        let measured = state.measure_cached(constraints);
2913        let (measured_size, size_for_parent) = measured.as_ref().map_or(
2914            (
2915                Size {
2916                    width: 0.0,
2917                    height: 0.0,
2918                },
2919                Size {
2920                    width: 0.0,
2921                    height: 0.0,
2922                },
2923            ),
2924            |measured| (measured.size, measured.size_for_parent()),
2925        );
2926        state.with_pass_layout_state(|layout_state, context| {
2927            layout_state.set_size(measured_size, context);
2928        });
2929        let alignment_lines = measured
2930            .as_ref()
2931            .map_or_else(AlignmentLines::default, |node| {
2932                node.alignment_lines_for_parent()
2933            });
2934        state.measured.set(measured);
2935
2936        Placeable::with_place_target(
2937            size_for_parent.width,
2938            size_for_parent.height,
2939            state.node_id,
2940            Rc::clone(&self.state) as Rc<dyn PlaceTarget>,
2941        )
2942        .with_alignment_lines(alignment_lines)
2943    }
2944
2945    fn min_intrinsic_width(&self, height: f32) -> f32 {
2946        self.intrinsic(
2947            IntrinsicKind::MinWidth(height),
2948            Constraints {
2949                min_width: 0.0,
2950                max_width: f32::INFINITY,
2951                min_height: height,
2952                max_height: height,
2953            },
2954            |size| size.width,
2955        )
2956    }
2957
2958    fn max_intrinsic_width(&self, height: f32) -> f32 {
2959        self.intrinsic(
2960            IntrinsicKind::MaxWidth(height),
2961            Constraints {
2962                min_width: 0.0,
2963                max_width: f32::INFINITY,
2964                min_height: 0.0,
2965                max_height: height,
2966            },
2967            |size| size.width,
2968        )
2969    }
2970
2971    fn min_intrinsic_height(&self, width: f32) -> f32 {
2972        self.intrinsic(
2973            IntrinsicKind::MinHeight(width),
2974            Constraints {
2975                min_width: width,
2976                max_width: width,
2977                min_height: 0.0,
2978                max_height: f32::INFINITY,
2979            },
2980            |size| size.height,
2981        )
2982    }
2983
2984    fn max_intrinsic_height(&self, width: f32) -> f32 {
2985        self.intrinsic(
2986            IntrinsicKind::MaxHeight(width),
2987            Constraints {
2988                min_width: 0.0,
2989                max_width: width,
2990                min_height: 0.0,
2991                max_height: f32::INFINITY,
2992            },
2993            |size| size.height,
2994        )
2995    }
2996
2997    fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
2998        let parent_data = self.resolved_parent_data()?;
2999        if !parent_data.has_weight() {
3000            return None;
3001        }
3002        Some(cranpose_ui_layout::FlexParentData::new(
3003            parent_data.weight,
3004            parent_data.fill,
3005        ))
3006    }
3007
3008    fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3009        self.resolved_parent_data().unwrap_or_default()
3010    }
3011}
3012
3013#[derive(Clone)]
3014struct RuntimeNodeMetadata {
3015    modifier: Modifier,
3016    resolved_modifiers: ResolvedModifiers,
3017    modifier_slices: Rc<ModifierNodeSlices>,
3018    semantics: Option<Rc<SemanticsConfiguration>>,
3019    role: SemanticsRole,
3020    button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3021}
3022
3023impl Default for RuntimeNodeMetadata {
3024    fn default() -> Self {
3025        Self {
3026            modifier: Modifier::empty(),
3027            resolved_modifiers: ResolvedModifiers::default(),
3028            modifier_slices: Rc::default(),
3029            semantics: None,
3030            role: SemanticsRole::Unknown,
3031            button_handler: None,
3032        }
3033    }
3034}
3035
3036fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3037    modifier_slices
3038        .annotated_text()
3039        .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3040            value: SemanticsText(Rc::clone(&text)),
3041        })
3042}
3043
3044fn runtime_metadata_for(
3045    applier: &mut MemoryApplier,
3046    node_id: NodeId,
3047) -> Result<RuntimeNodeMetadata, NodeError> {
3048    if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3049        let modifier = layout.modifier.clone();
3050        let resolved_modifiers = layout.resolved_modifiers();
3051        let modifier_slices = layout.modifier_slices_snapshot();
3052        let role = role_from_modifier_slices(&modifier_slices);
3053
3054        RuntimeNodeMetadata {
3055            modifier,
3056            resolved_modifiers,
3057            modifier_slices,
3058            semantics: layout.semantics_configuration().map(Rc::new),
3059            role,
3060            button_handler: None,
3061        }
3062    }) {
3063        return Ok(meta);
3064    }
3065
3066    if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
3067        applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3068            (
3069                node.modifier(),
3070                node.resolved_modifiers(),
3071                node.modifier_slices_snapshot(),
3072                node.semantics_configuration().map(Rc::new),
3073            )
3074        })
3075    {
3076        return Ok(RuntimeNodeMetadata {
3077            modifier,
3078            resolved_modifiers,
3079            modifier_slices,
3080            semantics,
3081            role: SemanticsRole::Subcompose,
3082            button_handler: None,
3083        });
3084    }
3085    Ok(RuntimeNodeMetadata::default())
3086}
3087
3088/// Runs `visit` on the measurement `child` holds in its cache, or on a leaf
3089/// of no size for a child that holds none, as a node of another kind
3090/// measures.
3091fn visit_child<R>(
3092    applier: &mut MemoryApplier,
3093    child: &MeasuredChild,
3094    visit: impl FnOnce(&mut MemoryApplier, &MeasuredNode) -> R,
3095) -> R {
3096    let held = applier.get_mut(child.node_id).ok().and_then(|node| {
3097        let any = node.as_any_mut();
3098        if let Some(layout) = any.downcast_ref::<LayoutNode>() {
3099            return layout.cache().held_measurement();
3100        }
3101        any.downcast_ref::<SubcomposeLayoutNode>()
3102            .and_then(|subcompose| subcompose.cache().held_measurement())
3103    });
3104    match held {
3105        Some(held) => visit(applier, &held),
3106        None => visit(
3107            applier,
3108            &MeasuredNode::new(
3109                child.node_id,
3110                Size::default(),
3111                Point::default(),
3112                Point::default(),
3113                Vec::new(),
3114            ),
3115        ),
3116    }
3117}
3118
3119fn clear_semantics_dirty_flags(
3120    applier: &mut MemoryApplier,
3121    node: &MeasuredNode,
3122) -> Result<(), NodeError> {
3123    match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3124        layout.clear_needs_semantics();
3125    }) {
3126        Ok(()) => {}
3127        Err(NodeError::Missing { .. }) => {}
3128        Err(NodeError::TypeMismatch { .. }) => {
3129            match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3130                subcompose.clear_needs_semantics();
3131            }) {
3132                Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
3133                Err(err) => return Err(err),
3134            }
3135        }
3136        Err(err) => return Err(err),
3137    }
3138
3139    for child in &node.children {
3140        visit_child(applier, child, clear_semantics_dirty_flags)?;
3141    }
3142
3143    Ok(())
3144}
3145
3146fn build_semantics_tree_from_live_nodes(
3147    applier: &mut MemoryApplier,
3148    node: &MeasuredNode,
3149) -> Result<SemanticsTree, NodeError> {
3150    Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3151        applier, node, None,
3152    )?))
3153}
3154
3155fn semantics_node_from_parts(
3156    node_id: NodeId,
3157    node_generation: u32,
3158    mut role: SemanticsRole,
3159    config: Option<SemanticsConfiguration>,
3160    children: Vec<SemanticsNode>,
3161    held_details: Option<Box<SemanticsDetails>>,
3162    bounds: GeometryRect,
3163) -> SemanticsNode {
3164    let focusable = crate::focus_dispatch::has_focus_target(node_id);
3165    let mut node = SemanticsNode {
3166        node_id,
3167        bounds,
3168        node_generation,
3169        children,
3170        focusable,
3171        focused: focusable && crate::focus_dispatch::active_focus_target() == Some(node_id),
3172        details: held_details,
3173        ..SemanticsNode::default()
3174    };
3175    let details = match config {
3176        Some(mut config) => {
3177            if config.role == Some(SemanticsWidgetRole::Button) {
3178                role = SemanticsRole::Button;
3179            }
3180            if config.is_activatable() {
3181                node.actions.push(SemanticsAction::Click {
3182                    handler: SemanticsCallback::new(node_id),
3183                });
3184            }
3185            let on_click_label = config.on_click_label.take().or_else(|| {
3186                config
3187                    .on_click
3188                    .as_ref()
3189                    .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
3190            });
3191            node.widget_role = config.role;
3192            node.description = config.content_description.take();
3193            node.on_click = config.on_click.take();
3194            node.selected = config.selected;
3195            node.toggled = config.toggled;
3196            node.enabled = config.enabled;
3197            node.hidden = config.hidden;
3198            node.merge_descendants = config.merge_descendants;
3199            node.traversal_index = config.traversal_index;
3200            node.text = config.text.take();
3201            let is_modal = config.is_modal
3202                && modal_takes_space(Size {
3203                    width: bounds.width,
3204                    height: bounds.height,
3205                });
3206            SemanticsDetails::from_configuration(config, on_click_label, is_modal)
3207        }
3208        None => SemanticsDetails::NONE,
3209    };
3210    node.set_details(details);
3211    node.role = role;
3212    node
3213}
3214
3215#[inline(never)]
3216fn build_semantics_node_from_live_nodes(
3217    applier: &mut MemoryApplier,
3218    node: &MeasuredNode,
3219    origin: Option<Point>,
3220) -> Result<SemanticsNode, NodeError> {
3221    let (role, config, (bounds, content), placement) =
3222        match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3223            let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
3224            let config = layout.semantics_configuration();
3225            layout.clear_needs_semantics();
3226            let state = layout.layout_state();
3227            (
3228                role,
3229                config,
3230                semantics_placement(&state, origin),
3231                SemanticsPlacement::of(&state),
3232            )
3233        }) {
3234            Ok(data) => data,
3235            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3236                match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3237                    subcompose.clear_needs_semantics();
3238                    let state = subcompose.layout_state();
3239                    (
3240                        SemanticsRole::Subcompose,
3241                        subcompose.semantics_configuration(),
3242                        semantics_placement(&state, origin),
3243                        SemanticsPlacement::of(&state),
3244                    )
3245                }) {
3246                    Ok(data) => data,
3247                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3248                        let top_left = origin.unwrap_or_default();
3249                        (
3250                            SemanticsRole::Unknown,
3251                            None,
3252                            (
3253                                GeometryRect::from_origin_size(top_left, node.size),
3254                                top_left,
3255                            ),
3256                            SemanticsPlacement::default(),
3257                        )
3258                    }
3259                    Err(err) => return Err(err),
3260                }
3261            }
3262            Err(err) => return Err(err),
3263        };
3264
3265    let mut children = Vec::with_capacity(node.children.len());
3266    for child in &node.children {
3267        children.push(visit_child(applier, child, |applier, child| {
3268            build_semantics_node_from_live_nodes(applier, child, Some(content))
3269        })?);
3270    }
3271
3272    Ok(SemanticsNode {
3273        placement,
3274        ..semantics_node_from_parts(
3275            node.node_id,
3276            applier.node_generation(node.node_id),
3277            role,
3278            config,
3279            children,
3280            None,
3281            bounds,
3282        )
3283    })
3284}
3285
3286fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
3287    stats.semantics_node_count += 1;
3288    stats.semantics_action_count += node.actions.len();
3289    stats.semantics_action_capacity += node.actions.capacity();
3290    stats.semantics_child_count += node.children.len();
3291    stats.semantics_child_capacity += node.children.capacity();
3292    stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
3293    stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
3294    if node.details.is_some() {
3295        stats.semantics_heap_bytes += size_of::<SemanticsDetails>();
3296    }
3297
3298    if let Some(description) = &node.description {
3299        stats.semantics_description_count += 1;
3300        stats.semantics_description_bytes += description.capacity();
3301        stats.semantics_heap_bytes += description.capacity();
3302    }
3303
3304    for child in &node.children {
3305        record_semantics_allocation_stats(child, stats);
3306    }
3307}
3308
3309fn record_layout_box_allocation_stats(
3310    layout_box: &LayoutBox,
3311    stats: &mut LayoutAllocationDebugStats,
3312) {
3313    stats.layout_box_count += 1;
3314    stats.layout_box_child_count += layout_box.children.len();
3315    stats.layout_box_child_capacity += layout_box.children.capacity();
3316    stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
3317    stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
3318
3319    for child in &layout_box.children {
3320        record_layout_box_allocation_stats(child, stats);
3321    }
3322}
3323
3324fn build_layout_tree(
3325    applier: &mut MemoryApplier,
3326    node: &MeasuredNode,
3327) -> Result<LayoutTree, NodeError> {
3328    fn place(
3329        applier: &mut MemoryApplier,
3330        node: &MeasuredNode,
3331        origin: Point,
3332        parent_transform: ProjectiveTransform,
3333    ) -> Result<LayoutBox, NodeError> {
3334        let top_left = Point {
3335            x: origin.x + node.offset.x,
3336            y: origin.y + node.offset.y,
3337        };
3338        let rect = GeometryRect {
3339            x: top_left.x,
3340            y: top_left.y,
3341            width: node.size.width,
3342            height: node.size.height,
3343        };
3344        let (data, window_transform) =
3345            snapshot_node_data(applier, node.node_id, top_left, node.size, parent_transform)?;
3346        let mut children = Vec::with_capacity(node.children.len());
3347        for child in &node.children {
3348            if crate::modifier::is_window_root(applier, child.node_id) {
3349                continue;
3350            }
3351            let child_origin = Point {
3352                x: top_left.x + child.offset.x,
3353                y: top_left.y + child.offset.y,
3354            };
3355            children.push(visit_child(applier, child, |applier, child| {
3356                place(applier, child, child_origin, window_transform)
3357            })?);
3358        }
3359        Ok(LayoutBox {
3360            node_generation: applier.node_generation(node.node_id),
3361            ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
3362        })
3363    }
3364
3365    Ok(LayoutTree::new(place(
3366        applier,
3367        node,
3368        Point { x: 0.0, y: 0.0 },
3369        ProjectiveTransform::identity(),
3370    )?))
3371}
3372
3373fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
3374    match &layout_box.node_data.kind {
3375        LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
3376        LayoutNodeKind::Spacer => SemanticsRole::Spacer,
3377        LayoutNodeKind::Unknown => SemanticsRole::Unknown,
3378        LayoutNodeKind::Button { .. } => SemanticsRole::Button,
3379        LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
3380    }
3381}
3382
3383#[inline(never)]
3384fn build_semantics_node_from_layout_box(layout_box: &LayoutBox, origin: Point) -> SemanticsNode {
3385    let rect = layout_box.rect;
3386    let content = Point {
3387        x: rect.x + layout_box.content_offset.x,
3388        y: rect.y + layout_box.content_offset.y,
3389    };
3390    let children = layout_box
3391        .children
3392        .iter()
3393        .map(|child| build_semantics_node_from_layout_box(child, content))
3394        .collect();
3395
3396    SemanticsNode {
3397        placement: SemanticsPlacement {
3398            position: Point {
3399                x: rect.x - origin.x,
3400                y: rect.y - origin.y,
3401            },
3402            content_offset: layout_box.content_offset,
3403        },
3404        ..semantics_node_from_parts(
3405            layout_box.node_id,
3406            layout_box.node_generation,
3407            semantics_role_from_layout_box(layout_box),
3408            layout_box.node_data.semantics().cloned(),
3409            children,
3410            None,
3411            rect,
3412        )
3413    }
3414}
3415
3416fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
3417    match &info.role {
3418        SemanticsRole::Layout => LayoutNodeKind::Layout,
3419        SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
3420        SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
3421        SemanticsRole::Spacer => LayoutNodeKind::Spacer,
3422        SemanticsRole::Button => {
3423            let handler = info
3424                .button_handler
3425                .as_ref()
3426                .cloned()
3427                .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
3428            LayoutNodeKind::Button { on_click: handler }
3429        }
3430        SemanticsRole::Unknown => LayoutNodeKind::Unknown,
3431    }
3432}
3433
3434fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
3435    let horizontal = padding.horizontal_sum();
3436    let vertical = padding.vertical_sum();
3437    let min_width = (constraints.min_width - horizontal).max(0.0);
3438    let mut max_width = constraints.max_width;
3439    if max_width.is_finite() {
3440        max_width = (max_width - horizontal).max(0.0);
3441    }
3442    let min_height = (constraints.min_height - vertical).max(0.0);
3443    let mut max_height = constraints.max_height;
3444    if max_height.is_finite() {
3445        max_height = (max_height - vertical).max(0.0);
3446    }
3447    normalize_constraints(Constraints {
3448        min_width,
3449        max_width,
3450        min_height,
3451        max_height,
3452    })
3453}
3454
3455fn resolve_dimension(
3456    base: f32,
3457    explicit: DimensionConstraint,
3458    min_override: Option<f32>,
3459    max_override: Option<f32>,
3460    min_limit: f32,
3461    max_limit: f32,
3462) -> f32 {
3463    let mut min_bound = min_limit;
3464    if let Some(min_value) = min_override {
3465        min_bound = min_bound.max(min_value);
3466    }
3467
3468    let mut max_bound = if max_limit.is_finite() {
3469        max_limit
3470    } else {
3471        max_override.unwrap_or(max_limit)
3472    };
3473    if let Some(max_value) = max_override {
3474        if max_bound.is_finite() {
3475            max_bound = max_bound.min(max_value);
3476        } else {
3477            max_bound = max_value;
3478        }
3479    }
3480    if max_bound < min_bound {
3481        max_bound = min_bound;
3482    }
3483
3484    let mut size = match explicit {
3485        DimensionConstraint::Points(points) => points,
3486        DimensionConstraint::Fraction(fraction) => {
3487            if max_limit.is_finite() {
3488                max_limit * fraction.clamp(0.0, 1.0)
3489            } else {
3490                base
3491            }
3492        }
3493        DimensionConstraint::Unspecified => base,
3494        DimensionConstraint::Intrinsic(_) => base,
3495    };
3496
3497    size = clamp_dimension(size, min_bound, max_bound);
3498    size = clamp_dimension(size, min_limit, max_limit);
3499    size.max(0.0)
3500}
3501
3502fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
3503    let mut result = value.max(min);
3504    if max.is_finite() {
3505        result = result.min(max);
3506    }
3507    result
3508}
3509
3510fn normalize_constraints(mut constraints: Constraints) -> Constraints {
3511    if constraints.max_width < constraints.min_width {
3512        constraints.max_width = constraints.min_width;
3513    }
3514    if constraints.max_height < constraints.min_height {
3515        constraints.max_height = constraints.min_height;
3516    }
3517    constraints
3518}
3519
3520#[cfg(test)]
3521#[path = "tests/layout_tests.rs"]
3522mod tests;
3523
3524#[cfg(test)]
3525#[path = "tests/semantics_update_tests.rs"]
3526mod semantics_update_tests;
3527
3528#[cfg(test)]
3529#[path = "tests/coordinator_geometry_tests.rs"]
3530mod coordinator_geometry_tests;