Skip to main content

cranpose_ui/layout/
mod.rs

1/// Re-exports of the alignment, arrangement and measurable types that layout policies use.
2pub mod core;
3pub(crate) mod flex_box;
4pub(crate) mod grid;
5/// Measure policies behind the built-in layout widgets such as `Box`, `Row` and `Column`.
6pub mod policies;
7mod reveal;
8mod semantics_details;
9mod semantics_labels;
10mod semantics_update;
11
12use std::{
13    cell::{Cell, RefCell},
14    fmt,
15    mem::size_of,
16    rc::Rc,
17    sync::OnceLock,
18};
19
20use cranpose_core::{
21    Applier, ApplierHost, Composer, ConcreteApplierHost, MemoryApplier, Node, NodeError, NodeId,
22    Phase, RuntimeHandle, SlotTable, SlotsHost,
23};
24use cranpose_foundation::{
25    InvalidationKind, ModifierNodeContext, NodeCapabilities, SemanticsConfiguration,
26    SemanticsCustomAction, SemanticsWidgetRole,
27};
28use cranpose_ui_graphics::{ProjectiveTransform, layer_transform::layer_transform_to_window};
29use cranpose_ui_layout::{AlignmentLines, Constraints, MeasurePolicy, PlaceTarget, Placement};
30use web_time::Instant;
31
32#[cfg(test)]
33use self::core::{HorizontalAlignment, VerticalAlignment};
34use self::{
35    core::{Measurable, Placeable},
36    semantics_update::semantics_placement,
37};
38pub use self::{
39    reveal::can_skip_scroll_reveal_from_applier,
40    semantics_details::SemanticsDetails,
41    semantics_update::{build_semantics_tree_from_applier, update_semantics_tree_from_applier},
42};
43use crate::{
44    modifier::{
45        DimensionConstraint, EdgeInsets, Modifier, ModifierNodeSlices,
46        ModifierNodeSlicesDebugStats, Point, Rect as GeometryRect, ResolvedModifiers, Size,
47    },
48    render_state::AppContext,
49    subcompose_layout::{CachedBatchMeasureInputs, SubcomposeLayoutNode},
50    widgets::nodes::{
51        IntrinsicKind, LayoutNode, LayoutNodeCacheHandles, LayoutState, begin_placement_pass,
52        placement_pass_with_unplaced_nodes,
53    },
54};
55
56#[derive(Default)]
57pub(crate) struct LayoutNodeContext {
58    invalidations: Vec<InvalidationKind>,
59    update_requested: bool,
60    density: f32,
61}
62
63impl LayoutNodeContext {
64    pub(crate) fn new(density: f32) -> Self {
65        Self {
66            density,
67            ..Self::default()
68        }
69    }
70
71    pub(crate) fn take_invalidations(&mut self) -> Vec<InvalidationKind> {
72        std::mem::take(&mut self.invalidations)
73    }
74}
75
76impl ModifierNodeContext for LayoutNodeContext {
77    fn invalidate(&mut self, kind: InvalidationKind) {
78        if !self.invalidations.contains(&kind) {
79            self.invalidations.push(kind);
80        }
81    }
82
83    fn request_update(&mut self) {
84        self.update_requested = true;
85    }
86
87    fn density(&self) -> f32 {
88        self.density
89    }
90}
91
92#[doc(hidden)]
93pub fn invalidate_all_layout_caches() {
94    crate::render_state::invalidate_layout_cache_epoch();
95}
96
97fn layout_measure_telemetry_threshold_ms() -> Option<f64> {
98    static THRESHOLD_MS: OnceLock<Option<f64>> = OnceLock::new();
99    *THRESHOLD_MS.get_or_init(|| {
100        std::env::var("CRANPOSE_LAYOUT_MEASURE_TELEMETRY_MS")
101            .ok()
102            .and_then(|value| value.parse::<f64>().ok())
103            .filter(|value| value.is_finite() && *value >= 0.0)
104            .or_else(|| {
105                std::env::var_os("CRANPOSE_LAYOUT_MEASURE_TELEMETRY")
106                    .is_some()
107                    .then_some(4.0)
108            })
109    })
110}
111
112/// The stages of a layout pass that its telemetry times: start, repasses,
113/// applier guard, builder, measure, root placement, layout and semantics
114/// trees, builder drop and guard drop.
115type LayoutStages = cranpose_core::StageTimes<9>;
116
117fn log_layout_measure_telemetry(root: NodeId, stages: &LayoutStages) {
118    let Some(threshold_ms) = layout_measure_telemetry_threshold_ms() else {
119        return;
120    };
121    let Some(
122        [
123            start,
124            after_repasses,
125            after_guard,
126            after_builder,
127            after_measure,
128            after_root_place,
129            after_aux,
130            after_builder_drop,
131            after_guard_drop,
132        ],
133    ) = stages.times()
134    else {
135        return;
136    };
137    let ms = cranpose_core::stage_ms;
138    let total_ms = ms(start, after_guard_drop);
139    if total_ms < threshold_ms {
140        return;
141    }
142    log::warn!(
143        "[layout-measure-telemetry] root={root} total_ms={total_ms:.2} repass_ms={:.2} guard_ms={:.2} builder_ms={:.2} measure_ms={:.2} root_place_ms={:.2} aux_ms={:.2} builder_drop_ms={:.2} guard_drop_ms={:.2}",
144        ms(start, after_repasses),
145        ms(after_repasses, after_guard),
146        ms(after_guard, after_builder),
147        ms(after_builder, after_measure),
148        ms(after_measure, after_root_place),
149        ms(after_root_place, after_aux),
150        ms(after_aux, after_builder_drop),
151        ms(after_builder_drop, after_guard_drop),
152    );
153}
154
155fn log_node_measure_telemetry(
156    kind: &'static str,
157    node_id: NodeId,
158    constraints: Constraints,
159    measured: &MeasuredNode,
160    (threshold_ms, start): (f64, Instant),
161) {
162    let size = measured.size;
163    let children = measured.children.len();
164    let total_ms = start.elapsed().as_secs_f64() * 1000.0;
165    if total_ms < threshold_ms {
166        return;
167    }
168
169    log::warn!(
170        "[layout-node-telemetry] kind={kind} node={} total_ms={total_ms:.2} constraints=({:.1},{:.1},{:.1},{:.1}) size=({:.1},{:.1}) children={children}",
171        node_id,
172        constraints.min_width,
173        constraints.max_width,
174        constraints.min_height,
175        constraints.max_height,
176        size.width,
177        size.height,
178    );
179}
180
181/// The host a layout pass lends its applier to. Between passes it holds an
182/// empty applier and the app context keeps it, so a pass builds neither.
183pub(crate) struct LayoutHost {
184    host: Rc<ConcreteApplierHost<MemoryApplier>>,
185}
186
187impl Default for LayoutHost {
188    fn default() -> Self {
189        Self {
190            host: Rc::new(ConcreteApplierHost::new(MemoryApplier::new())),
191        }
192    }
193}
194
195/// Lends `target` to a [`LayoutHost`] of `context` for one pass and gives it
196/// back when dropped, also after a panic.
197struct ApplierSlotGuard<'a> {
198    target: &'a mut MemoryApplier,
199    lent: LayoutHost,
200    slots: Rc<RefCell<SlotTable>>,
201    context: &'a AppContext,
202}
203
204impl<'a> ApplierSlotGuard<'a> {
205    fn new(target: &'a mut MemoryApplier, context: &'a AppContext) -> Self {
206        let lent = context.take_layout_host();
207        let slots = {
208            let mut applier_ref = lent.host.borrow_typed();
209            std::mem::swap(target, &mut *applier_ref);
210            applier_ref.slots_handle()
211        };
212        Self {
213            target,
214            lent,
215            slots,
216            context,
217        }
218    }
219
220    fn host(&self) -> &Rc<ConcreteApplierHost<MemoryApplier>> {
221        &self.lent.host
222    }
223
224    fn slots_handle(&self) -> &Rc<RefCell<SlotTable>> {
225        &self.slots
226    }
227}
228
229impl Drop for ApplierSlotGuard<'_> {
230    fn drop(&mut self) {
231        let host = &self.lent.host;
232        std::mem::swap(self.target, &mut *host.borrow_typed());
233        // A host still shared past the pass stays with whoever holds it.
234        if Rc::strong_count(host) == 1 {
235            self.context.replace_layout_host(LayoutHost {
236                host: Rc::clone(host),
237            });
238        }
239    }
240}
241
242#[derive(Clone, Copy, PartialEq)]
243struct ModifierChainInputs {
244    density: crate::density::Density,
245    window_root: bool,
246    offset: Point,
247    uses_chain: bool,
248}
249
250struct ModifierChainMeasurement {
251    size: Size,
252    alignment_lines: AlignmentLines,
253    content_offset: Point,
254    offset: Point,
255    window_root: bool,
256    uses_chain: bool,
257    /// The incoming constraints this measurement holds for besides its own.
258    hold: Option<cranpose_ui_layout::ConstraintsHold>,
259}
260
261struct ScratchVecPool<T> {
262    available: Vec<Vec<T>>,
263}
264
265impl<T> ScratchVecPool<T> {
266    fn acquire(&mut self) -> Vec<T> {
267        self.available.pop().unwrap_or_default()
268    }
269
270    fn release(&mut self, mut values: Vec<T>) {
271        values.clear();
272        self.available.push(values);
273    }
274
275    #[cfg(test)]
276    fn available_count(&self) -> usize {
277        self.available.len()
278    }
279}
280
281impl<T> Default for ScratchVecPool<T> {
282    fn default() -> Self {
283        Self {
284            available: Vec::new(),
285        }
286    }
287}
288
289#[derive(Default)]
290pub(crate) struct FrameLayoutArena {
291    tmp_child_ids: ScratchVecPool<NodeId>,
292    tmp_placements: ScratchVecPool<Placement>,
293}
294
295#[cfg(test)]
296impl FrameLayoutArena {
297    pub(crate) fn available_placement_scratch_count(&self) -> usize {
298        self.tmp_placements.available_count()
299    }
300
301    pub(crate) fn seed_placement_scratch_for_test(&mut self) {
302        self.tmp_placements.release(Vec::with_capacity(1));
303    }
304}
305
306/// Discrete event callback reference produced during semantics extraction.
307#[derive(Clone, Debug, PartialEq, Eq)]
308pub struct SemanticsCallback {
309    node_id: NodeId,
310}
311
312impl SemanticsCallback {
313    /// Creates a callback that targets the node with `node_id`.
314    pub fn new(node_id: NodeId) -> Self {
315        Self { node_id }
316    }
317
318    /// Returns the id of the node whose action this callback triggers.
319    pub fn node_id(&self) -> NodeId {
320        self.node_id
321    }
322}
323
324/// Semantics action exposed to the input system.
325#[derive(Clone, Debug, PartialEq, Eq)]
326pub enum SemanticsAction {
327    /// Activates the node, as a tap or a keyboard or assistive technology click does.
328    Click {
329        /// Callback that identifies the node to activate.
330        handler: SemanticsCallback,
331    },
332}
333
334/// A text node's text in the semantics tree, shared with the node's modifier
335/// slices rather than copied out of them.
336#[derive(Clone, Debug)]
337pub struct SemanticsText(Rc<crate::text::AnnotatedString>);
338
339impl SemanticsText {
340    /// The text the node shows.
341    pub fn as_str(&self) -> &str {
342        &self.0.text
343    }
344}
345
346impl PartialEq for SemanticsText {
347    fn eq(&self, other: &Self) -> bool {
348        self.as_str() == other.as_str()
349    }
350}
351
352impl Eq for SemanticsText {}
353
354impl From<&str> for SemanticsText {
355    fn from(text: &str) -> Self {
356        Self(Rc::new(crate::text::AnnotatedString::from(text)))
357    }
358}
359
360/// Semantic role describing how a node should participate in accessibility and hit testing.
361/// Roles are now derived from SemanticsConfiguration rather than widget types.
362#[derive(Clone, Debug, PartialEq, Eq)]
363pub enum SemanticsRole {
364    /// Generic container or layout node
365    Layout,
366    /// Subcomposition boundary
367    Subcompose,
368    /// Text content derived from the text node semantics payload.
369    Text {
370        /// The text the node shows.
371        value: SemanticsText,
372    },
373    /// Spacer (non-interactive)
374    Spacer,
375    /// Button (derived from the semantics `role`)
376    Button,
377    /// Unknown or unspecified role
378    Unknown,
379}
380
381/// A single node within the semantics tree.
382///
383/// Not `Eq`: a node now carries drawn-control bounds, which are floats. The
384/// tree is compared for "did anything a screen reader can see change", and
385/// that comparison is `PartialEq` everywhere else on the accessibility path
386/// (`AccessibilityRect`, `AccessibilityElement`) for the same reason.
387#[derive(Clone, Debug, PartialEq)]
388pub struct SemanticsNode {
389    /// Id of the runtime node this semantics node describes.
390    pub node_id: NodeId,
391    /// Where the node lies in the root's coordinates: its layout rect,
392    /// before graphics-layer transforms, as [`LayoutBox::rect`] holds it.
393    pub bounds: GeometryRect,
394    /// Where layout put the node inside its parent's content, from which a
395    /// tree update moves a node that did not change along with its parent.
396    pub placement: SemanticsPlacement,
397    /// Incarnation of the runtime node, incremented when its storage is recycled.
398    pub node_generation: u32,
399    /// Where this node sits in the tree (layout, text, subcomposition …).
400    pub role: SemanticsRole,
401    /// What kind of control this node is, as a screen reader announces it —
402    /// Compose's `Role`. Separate from [`SemanticsNode::role`] because a
403    /// clickable `Row` is structurally a layout node and semantically a button.
404    pub widget_role: Option<SemanticsWidgetRole>,
405    /// Actions that the input system and assistive technology can invoke on this node.
406    pub actions: Vec<SemanticsAction>,
407    /// Child semantics nodes, in layout order.
408    pub children: Vec<SemanticsNode>,
409    /// Content description that a screen reader announces for the node, if one was set.
410    pub description: Option<String>,
411    /// Direct activation callback for keyboard and assistive technology.
412    pub on_click: Option<SemanticsCustomAction>,
413    /// Selection state, or `None` if the node is not selectable.
414    pub selected: Option<bool>,
415    /// Checked state, or `None` if the node is not a toggleable control.
416    pub toggled: Option<cranpose_foundation::ToggleableState>,
417    /// Whether the node accepts user interaction. `true` unless the node was disabled.
418    pub enabled: bool,
419    /// Whether a screen reader skips this node and everything under it.
420    pub hidden: bool,
421    /// Whether a screen reader takes this node and the text under it as one
422    /// stop.
423    pub merge_descendants: bool,
424    /// Where a screen reader visits this node among the ones beside it.
425    pub traversal_index: f32,
426    /// The text an editable field holds.
427    pub text: Option<String>,
428    /// Whether this node registered a focus target, so focus can land on it.
429    /// Compose's `SemanticsProperties.Focused` companion.
430    pub focusable: bool,
431    /// Whether focus sits on this node right now.
432    pub focused: bool,
433    /// The properties few nodes set, held only when the node sets one: read
434    /// them through [`SemanticsNode::details`] and write them through
435    /// [`SemanticsNode::set_details`].
436    pub details: Option<Box<SemanticsDetails>>,
437}
438
439impl Default for SemanticsNode {
440    fn default() -> Self {
441        Self {
442            node_id: 0,
443            bounds: GeometryRect::EMPTY,
444            placement: SemanticsPlacement::default(),
445            node_generation: 0,
446            role: SemanticsRole::Unknown,
447            widget_role: None,
448            actions: Vec::new(),
449            children: Vec::new(),
450            description: None,
451            on_click: None,
452            selected: None,
453            toggled: None,
454            enabled: true,
455            hidden: false,
456            merge_descendants: false,
457            traversal_index: 0.0,
458            text: None,
459            focusable: false,
460            focused: false,
461            details: None,
462        }
463    }
464}
465
466/// Where layout put a semantics node: its offset inside its parent's
467/// content and where its own content starts inside it.
468#[derive(Clone, Copy, Debug, Default, PartialEq)]
469pub struct SemanticsPlacement {
470    position: Point,
471    content_offset: Point,
472}
473
474impl SemanticsPlacement {
475    fn of(state: &LayoutState) -> Self {
476        Self {
477            position: state.position(),
478            content_offset: state.content_offset(),
479        }
480    }
481
482    fn place(self, origin: Point) -> (Point, Point) {
483        let top_left = Point {
484            x: origin.x + self.position.x,
485            y: origin.y + self.position.y,
486        };
487        let content = Point {
488            x: top_left.x + self.content_offset.x,
489            y: top_left.y + self.content_offset.y,
490        };
491        (top_left, content)
492    }
493}
494
495/// Rooted semantics tree extracted after layout.
496///
497/// Two trees are equal when they hold the same nodes; what a tree keeps to
498/// bring itself up to date is not compared.
499#[derive(Clone, Debug)]
500pub struct SemanticsTree {
501    full: SemanticsNode,
502    modal: Option<Vec<usize>>,
503    tracking: semantics_update::SemanticsTracking,
504}
505
506impl PartialEq for SemanticsTree {
507    fn eq(&self, other: &Self) -> bool {
508        self.full == other.full && self.modal == other.modal
509    }
510}
511
512impl SemanticsTree {
513    fn new(full: SemanticsNode) -> Self {
514        let modal = top_modal_path(&full);
515        Self {
516            full,
517            modal,
518            tracking: semantics_update::SemanticsTracking::default(),
519        }
520    }
521
522    /// Returns the top visible modal subtree, or the full root when no modal is open.
523    pub fn root(&self) -> &SemanticsNode {
524        self.modal.as_deref().map_or(&self.full, |path| {
525            path.iter()
526                .try_fold(&self.full, |node, &index| node.children.get(index))
527                .unwrap_or(&self.full)
528        })
529    }
530}
531
532/// Whether a modal of `size` takes space; a zero-sized modal hides nothing.
533fn modal_takes_space(size: Size) -> bool {
534    size.width > 0.0 && size.height > 0.0 && size.width.is_finite() && size.height.is_finite()
535}
536
537/// The path to the modal drawn on top: the last visible modal in document
538/// order, a modal inside another preferred to it.
539fn top_modal_path(root: &SemanticsNode) -> Option<Vec<usize>> {
540    fn search(node: &SemanticsNode, path: &mut Vec<usize>) -> bool {
541        if node.hidden {
542            return false;
543        }
544        for (index, child) in node.children.iter().enumerate().rev() {
545            path.push(index);
546            if search(child, path) {
547                return true;
548            }
549            path.pop();
550        }
551        node.details().is_modal
552    }
553    let mut path = Vec::new();
554    search(root, &mut path).then_some(path)
555}
556
557/// Counts and heap sizes of the layout and semantics data that one measure pass retains, for memory
558/// diagnostics.
559#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
560pub struct LayoutAllocationDebugStats {
561    /// Number of `LayoutBox` nodes in the tree.
562    pub layout_box_count: usize,
563    /// Total number of child entries across all layout boxes.
564    pub layout_box_child_count: usize,
565    /// Total allocated capacity of the child vectors across all layout boxes.
566    pub layout_box_child_capacity: usize,
567    /// Heap bytes allocated for the child vectors of all layout boxes.
568    pub layout_box_heap_bytes: usize,
569    /// Number of modifier slice snapshots, one per layout box.
570    pub modifier_slice_count: usize,
571    /// Heap bytes held by all modifier slice snapshots.
572    pub modifier_slice_heap_bytes: usize,
573    /// Number of draw commands collected from modifier nodes.
574    pub modifier_draw_command_count: usize,
575    /// Allocated capacity of the draw command lists.
576    pub modifier_draw_command_capacity: usize,
577    /// Number of pointer input handlers collected from modifier nodes.
578    pub modifier_pointer_input_count: usize,
579    /// Allocated capacity of the pointer input handler lists.
580    pub modifier_pointer_input_capacity: usize,
581    /// Number of layout boxes that carry text content.
582    pub modifier_text_content_count: usize,
583    /// Number of layout boxes that carry a text style.
584    pub modifier_text_style_count: usize,
585    /// Number of layout boxes that carry text layout options.
586    pub modifier_text_layout_options_count: usize,
587    /// Number of layout boxes that carry a prepared text layout.
588    pub modifier_prepared_text_layout_count: usize,
589    /// Number of layout boxes that carry a graphics layer.
590    pub modifier_graphics_layer_count: usize,
591    /// Number of layout boxes that carry a graphics layer resolver.
592    pub modifier_graphics_layer_resolver_count: usize,
593    /// Number of nodes in the semantics tree.
594    pub semantics_node_count: usize,
595    /// Total number of actions across all semantics nodes.
596    pub semantics_action_count: usize,
597    /// Total allocated capacity of the action vectors across all semantics nodes.
598    pub semantics_action_capacity: usize,
599    /// Total number of child entries across all semantics nodes.
600    pub semantics_child_count: usize,
601    /// Total allocated capacity of the child vectors across all semantics nodes.
602    pub semantics_child_capacity: usize,
603    /// Number of semantics nodes that have a content description.
604    pub semantics_description_count: usize,
605    /// Allocated bytes of all content description strings.
606    pub semantics_description_bytes: usize,
607    /// Heap bytes held by the semantics tree: actions, children, details and descriptions.
608    pub semantics_heap_bytes: usize,
609}
610
611impl LayoutAllocationDebugStats {
612    fn add_modifier_slice(&mut self, stats: ModifierNodeSlicesDebugStats) {
613        self.modifier_slice_count += 1;
614        self.modifier_slice_heap_bytes += stats.heap_bytes;
615        self.modifier_draw_command_count += stats.draw_command_count;
616        self.modifier_draw_command_capacity += stats.draw_command_capacity;
617        self.modifier_pointer_input_count += stats.pointer_input_count;
618        self.modifier_pointer_input_capacity += stats.pointer_input_capacity;
619        self.modifier_text_content_count += usize::from(stats.has_text_content);
620        self.modifier_text_style_count += usize::from(stats.has_text_style);
621        self.modifier_text_layout_options_count += usize::from(stats.has_text_layout_options);
622        self.modifier_prepared_text_layout_count += usize::from(stats.has_prepared_text_layout);
623        self.modifier_graphics_layer_count += usize::from(stats.has_graphics_layer);
624        self.modifier_graphics_layer_resolver_count +=
625            usize::from(stats.has_graphics_layer_resolver);
626    }
627}
628
629/// Result of running layout for a Compose tree.
630#[derive(Debug, Clone)]
631pub struct LayoutTree {
632    root: LayoutBox,
633}
634
635impl LayoutTree {
636    /// Wraps `root` as a layout tree.
637    pub fn new(root: LayoutBox) -> Self {
638        Self { root }
639    }
640
641    /// Returns the root layout box.
642    pub fn root(&self) -> &LayoutBox {
643        &self.root
644    }
645
646    /// Returns the root layout box for in-place changes.
647    pub fn root_mut(&mut self) -> &mut LayoutBox {
648        &mut self.root
649    }
650
651    /// Consumes the tree and returns its root layout box.
652    pub fn into_root(self) -> LayoutBox {
653        self.root
654    }
655
656    /// Counts the allocations that the layout boxes of this tree retain.
657    pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
658        let mut stats = LayoutAllocationDebugStats::default();
659        record_layout_box_allocation_stats(&self.root, &mut stats);
660        stats
661    }
662}
663
664/// Layout information for a single node.
665#[derive(Debug, Clone)]
666pub struct LayoutBox {
667    /// Id of the runtime node this box describes.
668    pub node_id: NodeId,
669    /// Incarnation of the runtime node captured with these layout bounds.
670    pub node_generation: u32,
671    /// Position and size of the node in root coordinates, in logical pixels, before graphics-layer
672    /// transforms.
673    pub rect: GeometryRect,
674    /// Content offset for scroll/inner transforms (applies to children, NOT this node's position)
675    pub content_offset: Point,
676    /// Modifier, semantics and kind data captured with the node.
677    pub node_data: LayoutNodeData,
678    /// Layout boxes of the node's placed children.
679    pub children: Vec<LayoutBox>,
680}
681
682impl LayoutBox {
683    /// Creates a layout box with node generation zero.
684    pub fn new(
685        node_id: NodeId,
686        rect: GeometryRect,
687        content_offset: Point,
688        node_data: LayoutNodeData,
689        children: Vec<LayoutBox>,
690    ) -> Self {
691        Self {
692            node_id,
693            node_generation: 0,
694            rect,
695            content_offset,
696            node_data,
697            children,
698        }
699    }
700}
701
702/// Snapshot of the data required to render a layout node.
703#[derive(Debug, Clone)]
704pub struct LayoutNodeData {
705    /// Composable origins retained by an inspection-enabled layout node.
706    #[cfg(feature = "inspection")]
707    pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
708    /// The modifier chain of the node.
709    pub modifier: Modifier,
710    /// Padding, layout properties and offset that the modifier chain resolved to.
711    pub resolved_modifiers: ResolvedModifiers,
712    /// Draw commands, pointer input handlers, text and graphics layer data collected from the
713    /// modifier nodes.
714    pub modifier_slices: Rc<ModifierNodeSlices>,
715    /// Semantics that the modifier chain reported, if any.
716    pub semantics: Option<Rc<SemanticsConfiguration>>,
717    /// What kind of node this is.
718    pub kind: LayoutNodeKind,
719}
720
721impl LayoutNodeData {
722    /// Creates node data from its parts.
723    pub fn new(
724        modifier: Modifier,
725        resolved_modifiers: ResolvedModifiers,
726        modifier_slices: Rc<ModifierNodeSlices>,
727        semantics: Option<Rc<SemanticsConfiguration>>,
728        kind: LayoutNodeKind,
729    ) -> Self {
730        Self {
731            #[cfg(feature = "inspection")]
732            source_trace: Rc::default(),
733            modifier,
734            resolved_modifiers,
735            modifier_slices,
736            semantics,
737            kind,
738        }
739    }
740
741    /// Semantics the node's live modifier chain reported when the snapshot
742    /// was taken.
743    pub fn semantics(&self) -> Option<&SemanticsConfiguration> {
744        self.semantics.as_deref()
745    }
746
747    /// Returns the padding, layout properties and offset that the modifier chain resolved to.
748    pub fn resolved_modifiers(&self) -> ResolvedModifiers {
749        self.resolved_modifiers
750    }
751
752    /// Returns the draw, pointer input, text and graphics layer data collected from the modifier
753    /// nodes.
754    pub fn modifier_slices(&self) -> &ModifierNodeSlices {
755        &self.modifier_slices
756    }
757}
758
759/// Classification of the node captured inside a [`LayoutBox`].
760///
761/// Note: Text content is no longer represented as a distinct LayoutNodeKind.
762/// Text nodes now use `LayoutNodeKind::Layout` with their content stored in
763/// `modifier_slices.text_content()` via TextModifierNode, following Jetpack
764/// Compose's pattern where text is a modifier node capability.
765#[derive(Clone)]
766pub enum LayoutNodeKind {
767    /// A node that measures and places its children.
768    Layout,
769    /// A subcomposition boundary, such as a lazy list or a `SubcomposeLayout`.
770    Subcompose,
771    /// A non-interactive spacer.
772    Spacer,
773    /// A button node.
774    Button {
775        /// Handler that runs when the button is clicked.
776        on_click: Rc<RefCell<dyn FnMut()>>,
777    },
778    /// A node that fits no other kind.
779    Unknown,
780}
781
782impl fmt::Debug for LayoutNodeKind {
783    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
784        match self {
785            LayoutNodeKind::Layout => f.write_str("Layout"),
786            LayoutNodeKind::Subcompose => f.write_str("Subcompose"),
787            LayoutNodeKind::Spacer => f.write_str("Spacer"),
788            LayoutNodeKind::Button { .. } => f.write_str("Button"),
789            LayoutNodeKind::Unknown => f.write_str("Unknown"),
790        }
791    }
792}
793
794/// Extension trait that equips `MemoryApplier` with layout computation.
795pub trait LayoutEngine {
796    /// Measures the tree under `root` within `max_size` and returns its layout tree.
797    fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError>;
798}
799
800impl LayoutEngine for MemoryApplier {
801    fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError> {
802        let measurements = measure_layout(self, root, max_size)?;
803        measurements
804            .into_layout_tree()
805            .ok_or(NodeError::MissingContext {
806                id: root,
807                reason: "layout tree was not requested",
808            })
809    }
810}
811
812/// Result of running the measure pass for a Compose layout tree.
813#[derive(Debug, Clone)]
814pub struct LayoutMeasurements {
815    root: Rc<MeasuredNode>,
816    // Boxed: a frame that builds neither tree moves a few words, not the
817    // hundreds of bytes the trees take inline.
818    semantics: Option<Box<SemanticsTree>>,
819    layout_tree: Option<Box<LayoutTree>>,
820}
821
822impl LayoutMeasurements {
823    fn new(
824        root: Rc<MeasuredNode>,
825        semantics: Option<SemanticsTree>,
826        layout_tree: Option<LayoutTree>,
827    ) -> Self {
828        Self {
829            root,
830            semantics: semantics.map(Box::new),
831            layout_tree: layout_tree.map(Box::new),
832        }
833    }
834
835    /// Returns the measured size of the root node.
836    pub fn root_size(&self) -> Size {
837        self.root.size
838    }
839
840    /// Returns the semantics tree, or `None` if the measure options left it out.
841    pub fn semantics_tree(&self) -> Option<&SemanticsTree> {
842        self.semantics.as_deref()
843    }
844
845    /// Counts the allocations that the layout tree and the semantics tree retain.
846    pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
847        let mut stats = self
848            .layout_tree
849            .as_deref()
850            .map(LayoutTree::debug_allocation_stats)
851            .unwrap_or_default();
852        if let Some(semantics) = &self.semantics {
853            record_semantics_allocation_stats(semantics.root(), &mut stats);
854        }
855        stats
856    }
857
858    /// Consumes the measurements and returns the built [`LayoutTree`], if requested.
859    pub fn into_layout_tree(self) -> Option<LayoutTree> {
860        self.layout_tree.map(|tree| *tree)
861    }
862
863    /// Returns a cloned [`LayoutTree`] for rendering/debug consumers, if requested.
864    pub fn layout_tree(&self) -> Option<LayoutTree> {
865        self.layout_tree.as_deref().cloned()
866    }
867}
868
869/// Builds a semantics tree from an existing [`LayoutTree`].
870///
871/// This is useful for consumers that need semantics on demand without forcing
872/// every layout pass to eagerly allocate a full [`SemanticsTree`].
873pub fn build_semantics_tree_from_layout_tree(layout_tree: &LayoutTree) -> SemanticsTree {
874    SemanticsTree::new(build_semantics_node_from_layout_box(
875        layout_tree.root(),
876        Point::default(),
877    ))
878}
879
880/// Builds a layout snapshot from retained layout state in the live applier tree.
881///
882/// Renderers use retained node state directly. This function exists for debug,
883/// robot, and tests that need an owned [`LayoutTree`] without forcing every
884/// layout pass to allocate one.
885pub fn build_layout_tree_from_applier(
886    applier: &mut MemoryApplier,
887    root: NodeId,
888) -> Result<Option<LayoutTree>, NodeError> {
889    let origin = layout_tree_origin(read_layout_node(applier, root, |state, _| state)?);
890    let mut child_stack = Vec::new();
891    place_layout_box(
892        applier,
893        root,
894        origin,
895        ProjectiveTransform::identity(),
896        &mut child_stack,
897    )
898    .map(|root| root.map(LayoutTree::new))
899}
900
901/// Whether any box placed under `root` has area, as
902/// [`build_layout_tree_from_applier`] would place it: the walk stops at the
903/// first such box and reads no node's modifiers or semantics.
904pub fn has_placed_content(applier: &mut MemoryApplier, root: NodeId) -> Result<bool, NodeError> {
905    walk_placed_boxes(applier, root, |_| std::ops::ControlFlow::Break(()))
906}
907
908/// The far right and bottom edges of the boxes with area placed under `root`,
909/// measured from the root's origin as [`build_layout_tree_from_applier`]
910/// places them, reading no node's modifiers or semantics. `None` while
911/// nothing under `root` has area.
912pub fn placed_content_extent(
913    applier: &mut MemoryApplier,
914    root: NodeId,
915) -> Result<Option<Size>, NodeError> {
916    let mut extent: Option<Size> = None;
917    walk_placed_boxes(applier, root, |rect| {
918        let (right, bottom) = (rect.x + rect.width, rect.y + rect.height);
919        extent = Some(extent.map_or_else(
920            || Size::new(right, bottom),
921            |extent| Size::new(extent.width.max(right), extent.height.max(bottom)),
922        ));
923        std::ops::ControlFlow::Continue(())
924    })?;
925    Ok(extent)
926}
927
928/// Hands `visit` every box with area placed under `root`, the root left out,
929/// at the rect [`build_layout_tree_from_applier`] gives it, skipping a window
930/// root's subtree and an unplaced node's, until `visit` breaks. Answers
931/// whether it did.
932fn walk_placed_boxes(
933    applier: &mut MemoryApplier,
934    root: NodeId,
935    mut visit: impl FnMut(GeometryRect) -> std::ops::ControlFlow<()>,
936) -> Result<bool, NodeError> {
937    let mut pending: Vec<(NodeId, Point)> = Vec::new();
938    let placed = read_layout_node(applier, root, |state, children| {
939        if state.is_placed() {
940            pending.extend(
941                children
942                    .iter()
943                    .map(|&child| (child, state.content_offset())),
944            );
945        }
946    })?;
947    if placed.is_none() {
948        return Ok(false);
949    }
950    while let Some((node_id, origin)) = pending.pop() {
951        if crate::modifier::is_window_root(applier, node_id) {
952            continue;
953        }
954        let rect = read_layout_node(applier, node_id, |state, children| {
955            if !state.is_placed() {
956                return None;
957            }
958            let (rect, content) = semantics_placement(&state, Some(origin));
959            pending.extend(children.iter().map(|&child| (child, content)));
960            Some(rect)
961        })?
962        .flatten();
963        if let Some(rect) = rect
964            && rect.width > 0.0
965            && rect.height > 0.0
966            && visit(rect).is_break()
967        {
968            return Ok(true);
969        }
970    }
971    Ok(false)
972}
973
974fn layout_tree_origin(root: Option<LayoutState>) -> Point {
975    let Some(state) = root else {
976        return Point::default();
977    };
978    let position = state.position();
979    Point {
980        x: -position.x,
981        y: -position.y,
982    }
983}
984
985/// Places the root at the origin, the one placement no parent makes, and
986/// names what the pass left unplaced.
987fn finish_placement(applier: &mut MemoryApplier, root: NodeId) {
988    if applier
989        .with_node::<LayoutNode, _>(root, |node| node.set_position(Point::default()))
990        .is_err()
991    {
992        let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
993            node.set_position(Point::default());
994        });
995    }
996    if let Some(pass) = placement_pass_with_unplaced_nodes() {
997        record_unplaced_parents(applier, root, pass);
998    }
999}
1000
1001/// Names to the scene phase the parent of every node `pass` found placed and
1002/// left unplaced: nothing about the node itself changed, so the geometry
1003/// setters had nothing to report, yet its layer has to leave the scene. A
1004/// pass unplaces nodes rarely, so it pays for this walk only when it did.
1005fn record_unplaced_parents(applier: &mut MemoryApplier, root: NodeId, pass: u64) {
1006    let mut parents = vec![root];
1007    let mut children = Vec::new();
1008    while let Some(parent) = parents.pop() {
1009        children.clear();
1010        if !matches!(
1011            read_layout_node(applier, parent, |_, ids| children.extend_from_slice(ids)),
1012            Ok(Some(()))
1013        ) {
1014            continue;
1015        }
1016        for &child in &children {
1017            match read_layout_node(applier, child, |state, _| state.unplaced_in(pass)) {
1018                Ok(Some(true)) => crate::render_state::record_geometry_scene_node(parent),
1019                Ok(Some(false)) => parents.push(child),
1020                Ok(None) | Err(_) => {}
1021            }
1022        }
1023    }
1024}
1025
1026fn read_layout_node<R>(
1027    applier: &mut MemoryApplier,
1028    node_id: NodeId,
1029    mut read: impl FnMut(LayoutState, &[NodeId]) -> R,
1030) -> Result<Option<R>, NodeError> {
1031    read_live_layout_node(applier, node_id, |node| {
1032        node.with_children(|children| read(node.state(), children))
1033    })
1034}
1035
1036enum LiveLayoutNode<'a> {
1037    Layout(&'a LayoutNode),
1038    Subcompose(&'a SubcomposeLayoutNode),
1039}
1040
1041impl LiveLayoutNode<'_> {
1042    fn state(&self) -> LayoutState {
1043        match self {
1044            Self::Layout(node) => node.layout_state(),
1045            Self::Subcompose(node) => node.layout_state(),
1046        }
1047    }
1048
1049    fn parent(&self) -> Option<NodeId> {
1050        match self {
1051            Self::Layout(node) => node.parent(),
1052            Self::Subcompose(node) => node.parent(),
1053        }
1054    }
1055
1056    fn is_window_root(&self) -> bool {
1057        matches!(self, Self::Layout(node) if node.is_window_root())
1058    }
1059
1060    fn with_children<R>(&self, read: impl FnOnce(&[NodeId]) -> R) -> R {
1061        match self {
1062            Self::Layout(node) => read(&node.children),
1063            Self::Subcompose(node) => node.with_active_children(read),
1064        }
1065    }
1066
1067    fn semantics(&self) -> Option<SemanticsConfiguration> {
1068        match self {
1069            Self::Layout(node) => node.semantics_configuration(),
1070            Self::Subcompose(node) => node.semantics_configuration(),
1071        }
1072    }
1073}
1074
1075fn read_live_layout_node<R>(
1076    applier: &mut MemoryApplier,
1077    node_id: NodeId,
1078    mut read: impl FnMut(LiveLayoutNode<'_>) -> R,
1079) -> Result<Option<R>, NodeError> {
1080    match applier.with_node::<LayoutNode, _>(node_id, |node| read(LiveLayoutNode::Layout(node))) {
1081        Ok(value) => return Ok(Some(value)),
1082        Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {}
1083        Err(err) => return Err(err),
1084    }
1085    match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1086        read(LiveLayoutNode::Subcompose(node))
1087    }) {
1088        Ok(value) => Ok(Some(value)),
1089        Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1090        Err(err) => Err(err),
1091    }
1092}
1093
1094fn snapshot_node_data(
1095    applier: &mut MemoryApplier,
1096    node_id: NodeId,
1097    top_left: Point,
1098    size: Size,
1099    parent_transform: ProjectiveTransform,
1100) -> Result<(LayoutNodeData, ProjectiveTransform), NodeError> {
1101    let info = runtime_metadata_for(applier, node_id)?;
1102    let kind = layout_kind_from_metadata(node_id, &info);
1103    let RuntimeNodeMetadata {
1104        modifier,
1105        resolved_modifiers,
1106        modifier_slices,
1107        semantics,
1108        ..
1109    } = info;
1110
1111    let window_transform = modifier_slices
1112        .graphics_layer()
1113        .map_or(parent_transform, |layer| {
1114            layer_transform_to_window(
1115                parent_transform,
1116                top_left,
1117                modifier_slices.layer_bounds(size),
1118                &layer,
1119            )
1120        });
1121    modifier_slices.publish_window_geometry(top_left, window_transform, size);
1122
1123    let data = LayoutNodeData {
1124        #[cfg(feature = "inspection")]
1125        source_trace: applier
1126            .with_node::<LayoutNode, _>(node_id, |node| node.source_trace.clone())
1127            .unwrap_or_default(),
1128        modifier,
1129        resolved_modifiers,
1130        modifier_slices,
1131        semantics,
1132        kind,
1133    };
1134    Ok((data, window_transform))
1135}
1136
1137/// Places `node_id`'s box and its subtree. `child_stack` is shared by the
1138/// whole walk: each node pushes its children above its parent's, reads them
1139/// from there while its descendants push and pop above them, and pops them
1140/// when done, so no node's child list is copied out of the applier.
1141fn place_layout_box(
1142    applier: &mut MemoryApplier,
1143    node_id: NodeId,
1144    parent_content_origin: Point,
1145    parent_transform: ProjectiveTransform,
1146    child_stack: &mut Vec<NodeId>,
1147) -> Result<Option<LayoutBox>, NodeError> {
1148    let first_child = child_stack.len();
1149    let Some(state) = read_layout_node(applier, node_id, |state, children| {
1150        if state.is_placed() {
1151            child_stack.extend_from_slice(children);
1152        }
1153        state
1154    })?
1155    else {
1156        return Ok(None);
1157    };
1158    if !state.is_placed() {
1159        return Ok(None);
1160    }
1161
1162    let top_left = Point {
1163        x: parent_content_origin.x + state.position().x,
1164        y: parent_content_origin.y + state.position().y,
1165    };
1166    let rect = GeometryRect {
1167        x: top_left.x,
1168        y: top_left.y,
1169        width: state.size().width,
1170        height: state.size().height,
1171    };
1172    let (data, window_transform) =
1173        snapshot_node_data(applier, node_id, top_left, state.size(), parent_transform)?;
1174    let child_origin = Point {
1175        x: top_left.x + state.content_offset().x,
1176        y: top_left.y + state.content_offset().y,
1177    };
1178    let end = child_stack.len();
1179    let mut children = Vec::with_capacity(end - first_child);
1180    for index in first_child..end {
1181        let child_id = child_stack[index];
1182        if crate::modifier::is_window_root(applier, child_id) {
1183            continue;
1184        }
1185        if let Some(child) = place_layout_box(
1186            applier,
1187            child_id,
1188            child_origin,
1189            window_transform,
1190            child_stack,
1191        )? {
1192            children.push(child);
1193        }
1194    }
1195    child_stack.truncate(first_child);
1196
1197    Ok(Some(LayoutBox {
1198        node_generation: applier.node_generation(node_id),
1199        ..LayoutBox::new(node_id, rect, state.content_offset(), data, children)
1200    }))
1201}
1202
1203/// The modal the semantics tree of `root` would be rooted at, found without
1204/// building the tree: the topmost placed, visible modal that takes space,
1205/// or `None` when no modal is open. Unlike a tree update, it leaves the
1206/// nodes' semantics dirty flags alone.
1207pub fn top_modal_from_applier(
1208    applier: &mut MemoryApplier,
1209    root: NodeId,
1210) -> Result<Option<NodeId>, NodeError> {
1211    // One depth-first walk on a shared stack, last child first, with a
1212    // modal's own step below its children's: the first modal step popped is
1213    // the modal drawn on top. It runs every frame, so no node's children are
1214    // copied and each node is looked up once.
1215    enum Step {
1216        Enter { node: NodeId, child: bool },
1217        Modal(NodeId),
1218    }
1219
1220    fn push_placed(
1221        steps: &mut Vec<Step>,
1222        node: NodeId,
1223        reach: cranpose_foundation::SemanticsReach,
1224        size: Size,
1225        children: impl IntoIterator<Item = NodeId>,
1226    ) {
1227        if reach.hidden {
1228            return;
1229        }
1230        if reach.is_modal && modal_takes_space(size) {
1231            steps.push(Step::Modal(node));
1232        }
1233        steps.extend(
1234            children
1235                .into_iter()
1236                .map(|node| Step::Enter { node, child: true }),
1237        );
1238    }
1239
1240    let mut steps = vec![Step::Enter {
1241        node: root,
1242        child: false,
1243    }];
1244    while let Some(step) = steps.pop() {
1245        let (node_id, child) = match step {
1246            Step::Modal(node) => return Ok(Some(node)),
1247            Step::Enter { node, child } => (node, child),
1248        };
1249        let node = match applier.get_mut(node_id) {
1250            Ok(node) => node.as_any_mut(),
1251            Err(NodeError::Missing { .. }) => continue,
1252            Err(error) => return Err(error),
1253        };
1254        if let Some(layout) = node.downcast_mut::<LayoutNode>() {
1255            let state = layout.layout_state();
1256            if state.is_placed() && !(child && layout.is_window_root()) {
1257                let children = layout.children.iter().copied();
1258                push_placed(
1259                    &mut steps,
1260                    node_id,
1261                    layout.semantics_reach(),
1262                    state.size(),
1263                    children,
1264                );
1265            }
1266        } else if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
1267            let state = subcompose.layout_state();
1268            if state.is_placed() {
1269                let reach = subcompose.semantics_reach();
1270                subcompose.with_active_children(|children| {
1271                    push_placed(
1272                        &mut steps,
1273                        node_id,
1274                        reach,
1275                        state.size(),
1276                        children.iter().copied(),
1277                    );
1278                });
1279            }
1280        }
1281    }
1282    Ok(None)
1283}
1284
1285/// Selects which trees [`measure_layout_with_options`] builds.
1286#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1287pub struct MeasureLayoutOptions {
1288    /// Build the semantics tree.
1289    pub collect_semantics: bool,
1290    /// Build the layout tree.
1291    pub build_layout_tree: bool,
1292}
1293
1294impl Default for MeasureLayoutOptions {
1295    fn default() -> Self {
1296        Self {
1297            collect_semantics: true,
1298            build_layout_tree: true,
1299        }
1300    }
1301}
1302
1303/// Check if a node or any of its descendants needs measure (selective measure optimization).
1304/// This can be used by the app shell to skip layout when the tree is clean.
1305///
1306/// O(1) check - just looks at root's dirty flag.
1307/// Works because all mutation paths bubble dirty flags to root via composer commands.
1308///
1309/// Returns Result to force caller to handle errors explicitly. No more unwrap_or(true) safety net.
1310pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1311    Ok(applier.get_mut(root)?.layout_dirty())
1312}
1313
1314/// Check if the root semantics snapshot is dirty.
1315///
1316/// Semantics invalidations bubble to the root the same way layout invalidations do,
1317/// so a root check is sufficient to determine whether the next layout pass needs to
1318/// rebuild semantic data even when geometry is otherwise unchanged.
1319pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1320    Ok(applier.get_mut(root)?.needs_semantics())
1321}
1322
1323#[cfg(test)]
1324pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
1325    cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1326}
1327
1328/// Runs the measure phase for the subtree rooted at `root`.
1329pub fn measure_layout(
1330    applier: &mut MemoryApplier,
1331    root: NodeId,
1332    max_size: Size,
1333) -> Result<LayoutMeasurements, NodeError> {
1334    measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
1335}
1336
1337/// Runs the measure phase for the subtree rooted at `root` and builds only the trees that `options`
1338/// select.
1339pub fn measure_layout_with_options(
1340    applier: &mut MemoryApplier,
1341    root: NodeId,
1342    max_size: Size,
1343    options: MeasureLayoutOptions,
1344) -> Result<LayoutMeasurements, NodeError> {
1345    let mut stages = LayoutStages::start(layout_measure_telemetry_threshold_ms().is_some());
1346    let context = crate::render_state::require_current_app_context("a layout pass");
1347    context.begin_text_layout_pass();
1348    begin_placement_pass();
1349    process_pending_layout_repasses(&context, applier, root)?;
1350    stages.reached();
1351
1352    let constraints = Constraints {
1353        min_width: 0.0,
1354        max_width: max_size.width,
1355        min_height: 0.0,
1356        max_height: max_size.height,
1357    };
1358
1359    let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
1360        .with_node::<LayoutNode, _>(root, |node| {
1361            (
1362                node.needs_measure() || Node::descendant_needs_measure(node),
1363                node.needs_semantics(),
1364                node.cache_handles().epoch(),
1365            )
1366        }) {
1367        Ok(tuple) => tuple,
1368        Err(NodeError::TypeMismatch { .. }) => {
1369            let node = applier.get_mut(root)?;
1370            let measure_dirty = node.needs_measure() || node.descendant_needs_measure();
1371            let semantics_dirty = node.needs_semantics();
1372            (measure_dirty, semantics_dirty, 0)
1373        }
1374        Err(err) => return Err(err),
1375    };
1376
1377    let epoch = if needs_remeasure {
1378        context.next_layout_cache_epoch()
1379    } else if cached_epoch != 0 {
1380        cached_epoch
1381    } else {
1382        context.current_layout_cache_epoch()
1383    };
1384
1385    let guard = ApplierSlotGuard::new(applier, &context);
1386    let applier_host = guard.host();
1387    let slots_handle = guard.slots_handle();
1388    stages.reached();
1389
1390    let builder = LayoutBuilder::new_with_epoch(
1391        Rc::clone(applier_host),
1392        epoch,
1393        Rc::clone(slots_handle),
1394        &context,
1395    );
1396    stages.reached();
1397
1398    let measured = builder
1399        .state
1400        .measure_node(root, normalize_constraints(constraints))?;
1401    stages.reached();
1402
1403    if let Ok(mut applier) = applier_host.try_borrow_typed() {
1404        finish_placement(&mut applier, root);
1405    }
1406    stages.reached();
1407
1408    let (layout_tree, semantics) = {
1409        let mut applier_ref = applier_host.borrow_typed();
1410        let layout_tree = if options.build_layout_tree {
1411            Some(build_layout_tree(&mut applier_ref, &measured)?)
1412        } else {
1413            None
1414        };
1415        let semantics = if options.collect_semantics {
1416            let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
1417                clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
1418                build_semantics_tree_from_layout_tree(layout_tree)
1419            } else {
1420                build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
1421            };
1422            Some(semantics_tree)
1423        } else {
1424            None
1425        };
1426        (layout_tree, semantics)
1427    };
1428    stages.reached();
1429
1430    drop(builder);
1431    stages.reached();
1432
1433    drop(guard);
1434    stages.reached();
1435
1436    log_layout_measure_telemetry(root, &stages);
1437
1438    Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
1439}
1440
1441fn process_pending_layout_repasses(
1442    context: &AppContext,
1443    applier: &mut MemoryApplier,
1444    root: NodeId,
1445) -> Result<(), NodeError> {
1446    for node_id in context.take_modifier_slice_repass_nodes() {
1447        if let Ok(node) = applier.get_mut(node_id) {
1448            let any = node.as_any_mut();
1449            if let Some(layout) = any.downcast_mut::<crate::widgets::nodes::LayoutNode>() {
1450                layout.mark_modifier_slices_dirty();
1451            } else if let Some(subcompose) =
1452                any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1453            {
1454                subcompose.mark_modifier_slices_dirty();
1455            }
1456        }
1457    }
1458    let measure_repass_nodes = context.take_measure_repass_nodes();
1459    let repass_nodes = context.take_layout_repass_nodes();
1460    if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
1461        return Ok(());
1462    }
1463    for node_id in measure_repass_nodes {
1464        cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
1465    }
1466    for node_id in repass_nodes {
1467        cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1468    }
1469    applier.get_mut(root)?.mark_descendant_needs_layout(false);
1470    Ok(())
1471}
1472
1473/// A layout pass's builder, with the frame arena it took from `context` and
1474/// gives back when dropped.
1475struct LayoutBuilder<'a> {
1476    state: Rc<LayoutBuilderState>,
1477    context: &'a AppContext,
1478}
1479
1480impl<'a> LayoutBuilder<'a> {
1481    fn new_with_epoch(
1482        applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1483        epoch: u64,
1484        slots: Rc<RefCell<SlotTable>>,
1485        context: &'a AppContext,
1486    ) -> Self {
1487        Self {
1488            state: Rc::new(LayoutBuilderState::new_with_epoch(
1489                applier,
1490                epoch,
1491                slots,
1492                context.take_layout_frame_arena(),
1493            )),
1494            context,
1495        }
1496    }
1497}
1498
1499impl Drop for LayoutBuilder<'_> {
1500    fn drop(&mut self) {
1501        if Rc::strong_count(&self.state) != 1 {
1502            return;
1503        }
1504        let Ok(mut frame_arena) = self.state.frame_arena.try_borrow_mut() else {
1505            return;
1506        };
1507        self.context
1508            .replace_layout_frame_arena(std::mem::take(&mut *frame_arena));
1509    }
1510}
1511
1512struct LayoutBuilderState {
1513    applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1514    runtime_handle: RefCell<Option<RuntimeHandle>>,
1515    slots: Rc<RefCell<SlotTable>>,
1516    cache_epoch: u64,
1517    cache_floor: u64,
1518    frame_arena: RefCell<FrameLayoutArena>,
1519}
1520
1521struct LayoutRuntimeFrameBindingCleanup<'a> {
1522    state: &'a RefCell<LayoutRuntimeState>,
1523}
1524
1525impl Drop for LayoutRuntimeFrameBindingCleanup<'_> {
1526    fn drop(&mut self) {
1527        self.state.borrow().unbind_children();
1528    }
1529}
1530
1531enum LayoutNodeVisit<'a> {
1532    Cached(Rc<MeasuredNode>),
1533    /// Only nodes below this one changed: its measurement holds unless a
1534    /// changed child measures differently.
1535    Keep(Rc<MeasuredNode>),
1536    Measure(LayoutNodeMeasure<'a>),
1537}
1538
1539/// What measuring one changed child of a kept node again needs.
1540#[derive(Clone, Copy)]
1541struct ChangedChild {
1542    constraints: Constraints,
1543    placed: bool,
1544}
1545
1546/// What the walk over a kept measurement finds of one child.
1547enum ChildChange {
1548    Unchanged,
1549    Changed(ChangedChild),
1550    /// The node has to run its policy.
1551    Refuse,
1552}
1553
1554/// A changed child of a kept node as a layout pass finds it, from a layout
1555/// node or a subcompose node.
1556struct ChildView {
1557    self_dirty: bool,
1558    /// The child's parent data, read only when the child changed itself:
1559    /// only its own modifiers give it.
1560    parent_data: Option<cranpose_ui_layout::ParentData>,
1561    /// The constraints the child's own cache holds.
1562    cached_constraints: Option<Constraints>,
1563    read_intrinsics: bool,
1564    placed: bool,
1565}
1566
1567impl ChildView {
1568    /// The child, when it or a node below it changed.
1569    fn of(
1570        node: &dyn Node,
1571        cache: &LayoutNodeCacheHandles,
1572        parent_data: &dyn Fn() -> cranpose_ui_layout::ParentData,
1573        placed: bool,
1574    ) -> Option<Self> {
1575        let self_dirty = node.needs_measure() || node.needs_layout();
1576        if !self_dirty && !node.descendant_needs_layout() {
1577            return None;
1578        }
1579        Some(Self {
1580            self_dirty,
1581            parent_data: self_dirty.then(parent_data),
1582            cached_constraints: cache.measured_constraints(),
1583            read_intrinsics: cache.has_intrinsics(),
1584            placed,
1585        })
1586    }
1587}
1588
1589struct LayoutNodeMeasure<'a> {
1590    runtime_state: Rc<RefCell<LayoutRuntimeState>>,
1591    chain: ModifierChainInputs,
1592    pools: VecPools<'a>,
1593    spare: MeasuredSpare,
1594}
1595
1596impl LayoutBuilderState {
1597    fn new_with_epoch(
1598        applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1599        epoch: u64,
1600        slots: Rc<RefCell<SlotTable>>,
1601        frame_arena: FrameLayoutArena,
1602    ) -> Self {
1603        let runtime_handle = applier.borrow_typed().runtime_handle();
1604
1605        Self {
1606            applier,
1607            runtime_handle: RefCell::new(runtime_handle),
1608            slots,
1609            cache_epoch: epoch,
1610            cache_floor: crate::render_state::layout_cache_floor(),
1611            frame_arena: RefCell::new(frame_arena),
1612        }
1613    }
1614
1615    fn with_applier_result<R>(
1616        &self,
1617        f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1618    ) -> Result<R, NodeError> {
1619        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1620            return Err(NodeError::MissingContext {
1621                id: NodeId::default(),
1622                reason: "applier already borrowed",
1623            });
1624        };
1625        f(&mut applier)
1626    }
1627
1628    fn measure_subcompose_node(
1629        self: &Rc<Self>,
1630        node_id: NodeId,
1631        constraints: Constraints,
1632    ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1633        self.clear_subcompose_placed(node_id);
1634        if let Some(cached) = self.subcompose_measurement_to_keep(node_id, constraints)
1635            && let Some(kept) = self.keep_subcompose_measurement(node_id, constraints, &cached)?
1636        {
1637            return Ok(("subcompose kept", kept));
1638        }
1639        Ok(match self.try_measure_subcompose(node_id, constraints)? {
1640            Some(measured) => {
1641                self.store_subcompose_measurement(node_id, constraints, &measured);
1642                ("subcompose", measured)
1643            }
1644            None => (
1645                "fallback",
1646                Rc::new(MeasuredNode::new(
1647                    node_id,
1648                    Size::default(),
1649                    Point { x: 0.0, y: 0.0 },
1650                    Point::default(),
1651                    Vec::new(),
1652                )),
1653            ),
1654        })
1655    }
1656
1657    /// The measurement of a subcompose node whose own inputs did not change
1658    /// since it measured at `constraints`, as for a layout node: its policy
1659    /// does not run, so its content does not compose again.
1660    fn subcompose_measurement_to_keep(
1661        &self,
1662        node_id: NodeId,
1663        constraints: Constraints,
1664    ) -> Option<Rc<MeasuredNode>> {
1665        let mut applier = self.applier.try_borrow_typed().ok()?;
1666        applier
1667            .with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1668                if node.needs_measure() || Node::needs_layout(node) {
1669                    return None;
1670                }
1671                let cache = node.cache_handles();
1672                if cache.epoch() < self.cache_floor {
1673                    return None;
1674                }
1675                cache.get_measurement(constraints)
1676            })
1677            .ok()
1678            .flatten()
1679    }
1680
1681    /// Keeps a subcompose node's measurement as [`Self::keep_measurement`]
1682    /// keeps a layout node's. A subcompose node holds no record of what its
1683    /// policy gave each child, so a changed child must have only changed
1684    /// nodes below it, and measures again at the constraints its own cache
1685    /// holds.
1686    fn keep_subcompose_measurement(
1687        self: &Rc<Self>,
1688        node_id: NodeId,
1689        constraints: Constraints,
1690        cached: &Rc<MeasuredNode>,
1691    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1692        let kept = self.remeasure_changed_children(
1693            cached,
1694            |child_id| self.subcompose_child_change(child_id),
1695            |child_id| self.mark_measured_without_policy(child_id),
1696        )?;
1697        let Some(kept) = kept else {
1698            return Ok(None);
1699        };
1700        self.with_applier_result(|applier| {
1701            applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1702                node.cache_handles()
1703                    .store_measurement(constraints, Rc::clone(&kept));
1704                node.clear_needs_layout();
1705            })
1706        })?;
1707        Ok(Some(kept))
1708    }
1709
1710    /// What the walk over a kept subcompose node finds of a child. A changed
1711    /// child refuses the keep when it changed itself, a parent read its
1712    /// intrinsic sizes, or its cache holds no constraints.
1713    fn subcompose_child_change(&self, child_id: NodeId) -> Result<ChildChange, NodeError> {
1714        self.child_change(child_id, |view| {
1715            match view.cached_constraints.filter(|_| !view.self_dirty) {
1716                Some(constraints) if !view.read_intrinsics => ChildChange::Changed(ChangedChild {
1717                    constraints,
1718                    placed: view.placed,
1719                }),
1720                _ => ChildChange::Refuse,
1721            }
1722        })
1723    }
1724
1725    /// What the walk finds of a child: unchanged when neither it nor a node
1726    /// below it changed, otherwise what `decide` makes of it.
1727    fn child_change(
1728        &self,
1729        child_id: NodeId,
1730        decide: impl FnOnce(ChildView) -> ChildChange,
1731    ) -> Result<ChildChange, NodeError> {
1732        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1733            return Ok(ChildChange::Refuse);
1734        };
1735        Ok(
1736            match read_layout_child(&mut applier, child_id, ChildView::of)? {
1737                None => ChildChange::Refuse,
1738                Some(None) => ChildChange::Unchanged,
1739                Some(Some(view)) => decide(view),
1740            },
1741        )
1742    }
1743
1744    /// Marks a child a subcompose node measured again without its policy, so
1745    /// the policy checks it when it next runs: it may keep measurements of
1746    /// its own, as a lazy list keeps its items'.
1747    fn mark_measured_without_policy(&self, child_id: NodeId) {
1748        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1749            return;
1750        };
1751        if let Ok(node) = applier.get_mut(child_id) {
1752            node.mark_descendant_needs_layout(true);
1753        }
1754    }
1755
1756    fn clear_subcompose_placed(&self, node_id: NodeId) {
1757        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1758            return;
1759        };
1760        let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1761            node.clear_placed();
1762        });
1763    }
1764
1765    fn measure_node(
1766        self: &Rc<Self>,
1767        node_id: NodeId,
1768        constraints: Constraints,
1769    ) -> Result<Rc<MeasuredNode>, NodeError> {
1770        let telemetry = layout_measure_telemetry_threshold_ms().map(|ms| (ms, Instant::now()));
1771        let (kind, measured) =
1772            if let Some(measured) = self.measure_layout_node(node_id, constraints)? {
1773                ("layout", measured)
1774            } else {
1775                self.measure_subcompose_node(node_id, constraints)?
1776            };
1777        if let Some(telemetry) = telemetry {
1778            log_node_measure_telemetry(kind, node_id, constraints, &measured, telemetry);
1779        }
1780        Ok(measured)
1781    }
1782
1783    /// The measurement `node_id` cached for `constraints` in layout cache
1784    /// epoch `current_epoch`, the one the app context holds.
1785    fn cached_measure_node_with_applier(
1786        applier: &mut MemoryApplier,
1787        node_id: NodeId,
1788        constraints: Constraints,
1789        current_epoch: u64,
1790    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1791        let served = |cache: &LayoutNodeCacheHandles, dirty: bool| {
1792            let epoch = cache.epoch();
1793            if dirty || epoch == 0 || epoch != current_epoch {
1794                return None;
1795            }
1796            cache.get_measurement(constraints)
1797        };
1798
1799        match applier.with_node::<LayoutNode, _>(node_id, |node| {
1800            let measured = served(node.cache_handles(), Node::layout_dirty(node))?;
1801            node.set_measured_size(measured.size);
1802            Some(measured)
1803        }) {
1804            Ok(measured) => Ok(measured),
1805            Err(NodeError::TypeMismatch { .. }) => {
1806                match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1807                    let measured = served(node.cache_handles(), Node::layout_dirty(node))?;
1808                    node.set_measured_size(measured.size);
1809                    Some(measured)
1810                }) {
1811                    Ok(measured) => Ok(measured),
1812                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1813                    Err(err) => Err(err),
1814                }
1815            }
1816            Err(NodeError::Missing { .. }) => Ok(None),
1817            Err(err) => Err(err),
1818        }
1819    }
1820
1821    fn try_measure_subcompose(
1822        self: &Rc<Self>,
1823        node_id: NodeId,
1824        constraints: Constraints,
1825    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1826        let (node_handle, resolved_modifiers, spare) = {
1827            let Ok(mut applier) = self.applier.try_borrow_typed() else {
1828                return Ok(None);
1829            };
1830            let node = match applier.get_mut(node_id) {
1831                Ok(node) => node,
1832                Err(NodeError::Missing { .. }) => return Ok(None),
1833                Err(err) => return Err(err),
1834            };
1835            let any = node.as_any_mut();
1836            if let Some(subcompose) =
1837                any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1838            {
1839                let handle = subcompose.handle();
1840                let resolved_modifiers = handle
1841                    .resolved_modifiers()
1842                    .on_device_grid(subcompose.density().density());
1843                let spare = MeasuredSpare::take(subcompose.cache_handles());
1844                (handle, resolved_modifiers, spare)
1845            } else {
1846                return Ok(None);
1847            }
1848        };
1849
1850        let runtime_handle = {
1851            let mut runtime_handle = self.runtime_handle.borrow_mut();
1852            if runtime_handle.is_none()
1853                && let Ok(applier) = self.applier.try_borrow_typed()
1854            {
1855                *runtime_handle = applier.runtime_handle();
1856            }
1857            runtime_handle.clone().ok_or(NodeError::MissingContext {
1858                id: node_id,
1859                reason: "runtime handle required for subcomposition",
1860            })?
1861        };
1862
1863        let props = resolved_modifiers.layout_properties();
1864        let padding = resolved_modifiers.padding();
1865        let offset = resolved_modifiers.offset();
1866        let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1867
1868        if let DimensionConstraint::Points(width) = props.width() {
1869            let constrained_width = width - padding.horizontal_sum();
1870            inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1871            inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1872        }
1873        if let DimensionConstraint::Points(height) = props.height() {
1874            let constrained_height = height - padding.vertical_sum();
1875            inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1876            inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1877        }
1878
1879        let mut slots_guard = SlotsGuard::take(&self.slots);
1880        let slots_host = slots_guard.host();
1881        let applier_host_dyn: Rc<dyn ApplierHost> = Rc::clone(&self.applier) as Rc<dyn ApplierHost>;
1882        let composer = Composer::new(
1883            Rc::clone(&slots_host),
1884            applier_host_dyn,
1885            runtime_handle,
1886            Some(node_id),
1887        );
1888        composer.enter_phase(Phase::Measure);
1889
1890        let measure_error = RefCell::new(None);
1891        let measured_children = node_handle.measured_children_scratch();
1892
1893        let measure_result = node_handle.measure_with_cached_batch(
1894            &composer,
1895            node_id,
1896            inner_constraints,
1897            CachedBatchMeasureInputs {
1898                measurer: Box::new(|child_id: NodeId, child_constraints: Constraints| {
1899                    match self.measure_node(child_id, child_constraints) {
1900                        Ok(measured) => {
1901                            measured_children
1902                                .borrow_mut()
1903                                .insert(child_id, Rc::clone(&measured));
1904                            let size = measured.size_for_parent();
1905                            Placeable::value(size.width, size.height, child_id)
1906                                .with_alignment_lines(measured.alignment_lines_for_parent())
1907                        }
1908                        Err(err) => {
1909                            let mut slot = measure_error.borrow_mut();
1910                            if slot.is_none() {
1911                                *slot = Some(err);
1912                            }
1913                            Placeable::value(0.0, 0.0, child_id)
1914                        }
1915                    }
1916                }),
1917                cached_measure_batch_registrar: Box::new(
1918                    |child_ids: &[NodeId],
1919                     child_constraints: Constraints,
1920                     out: &mut Vec<Option<Size>>| {
1921                        out.clear();
1922                        out.resize(child_ids.len(), None);
1923
1924                        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1925                            return;
1926                        };
1927
1928                        let mut measured_children = measured_children.borrow_mut();
1929                        let current_epoch = crate::render_state::current_layout_cache_epoch();
1930                        for (index, &child_id) in child_ids.iter().enumerate() {
1931                            match Self::cached_measure_node_with_applier(
1932                                &mut applier,
1933                                child_id,
1934                                child_constraints,
1935                                current_epoch,
1936                            ) {
1937                                Ok(Some(measured)) => {
1938                                    out[index] = Some(measured.size);
1939                                    measured_children.insert(child_id, Rc::clone(&measured));
1940                                }
1941                                Ok(None) => {}
1942                                Err(err) => {
1943                                    let mut slot = measure_error.borrow_mut();
1944                                    if slot.is_none() {
1945                                        *slot = Some(err);
1946                                    }
1947                                    break;
1948                                }
1949                            }
1950                        }
1951                    },
1952                ),
1953                retained_measure_lookup: Box::new(|child_id| {
1954                    measured_children.borrow().get(&child_id).cloned()
1955                }),
1956                retained_measure_registrar: Box::new(|measurements| {
1957                    let mut measured_children = measured_children.borrow_mut();
1958                    for measured in measurements {
1959                        measured_children.insert(measured.node_id(), Rc::clone(measured));
1960                    }
1961                }),
1962                error: &measure_error,
1963            },
1964        )?;
1965        drop(composer);
1966        slots_guard.restore(slots_host.into_table()?);
1967
1968        if let Some(err) = measure_error.borrow_mut().take() {
1969            return Err(err);
1970        }
1971
1972        let cranpose_ui_layout::MeasureResult {
1973            size: measured_size,
1974            alignment_lines: explicit_lines,
1975            placements,
1976        } = measure_result;
1977
1978        let mut width = measured_size.width + padding.horizontal_sum();
1979        let mut height = measured_size.height + padding.vertical_sum();
1980
1981        width = resolve_dimension(
1982            width,
1983            props.width(),
1984            props.min_width(),
1985            props.max_width(),
1986            constraints.min_width,
1987            constraints.max_width,
1988        );
1989        height = resolve_dimension(
1990            height,
1991            props.height(),
1992            props.min_height(),
1993            props.max_height(),
1994            constraints.min_height,
1995            constraints.max_height,
1996        );
1997
1998        let MeasuredSpare {
1999            node: spare,
2000            mut children,
2001        } = spare;
2002        children.reserve_exact(placements.len());
2003        let mut alignment_lines = AlignmentLines::default();
2004        let mut measured_children_by_id = measured_children.borrow_mut();
2005
2006        if let Ok(mut applier) = self.applier.try_borrow_typed() {
2007            let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
2008                parent_node.set_measured_size(Size { width, height });
2009                parent_node.clear_needs_measure();
2010                parent_node.clear_needs_layout();
2011            });
2012        }
2013
2014        for placement in &placements {
2015            let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
2016                measured
2017            } else {
2018                self.measure_node(placement.node_id, inner_constraints)?
2019            };
2020            let policy_position = Point {
2021                x: padding.left + placement.x,
2022                y: padding.top + placement.y,
2023            };
2024            let retained_position = Point {
2025                x: policy_position.x + child.offset.x,
2026                y: policy_position.y + child.offset.y,
2027            };
2028            alignment_lines.merge(
2029                child
2030                    .alignment_lines_for_parent()
2031                    .translated(policy_position.y),
2032            );
2033
2034            if let Ok(mut applier) = self.applier.try_borrow_typed()
2035                && applier
2036                    .with_node::<LayoutNode, _>(placement.node_id, |node| {
2037                        node.set_position(retained_position);
2038                    })
2039                    .is_err()
2040            {
2041                let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
2042                    node.set_position(retained_position);
2043                });
2044            }
2045
2046            children.push(MeasuredChild::new(&child, policy_position));
2047        }
2048
2049        node_handle.set_active_children(children.iter().map(|child| child.node_id));
2050        node_handle.recycle_placement_scratch(placements);
2051
2052        Ok(Some(
2053            MeasuredNode::new(
2054                node_id,
2055                Size { width, height },
2056                offset,
2057                Point::default(),
2058                children,
2059            )
2060            .with_alignment_lines(
2061                alignment_lines
2062                    .with_overrides(explicit_lines.translated(padding.top))
2063                    .translated(offset.y),
2064            )
2065            .into_rc(spare),
2066        ))
2067    }
2068
2069    fn measure_layout_node(
2070        self: &Rc<Self>,
2071        node_id: NodeId,
2072        constraints: Constraints,
2073    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2074        let mut keep = true;
2075        let (mut applier, measure) = loop {
2076            let Ok(mut applier) = self.applier.try_borrow_typed() else {
2077                return Ok(None);
2078            };
2079            match applier.with_node::<LayoutNode, _>(node_id, |node| {
2080                self.visit_layout_node(node, constraints, keep)
2081            }) {
2082                Ok(LayoutNodeVisit::Measure(measure)) => break (applier, measure),
2083                Ok(LayoutNodeVisit::Cached(measured)) => return Ok(Some(measured)),
2084                Ok(LayoutNodeVisit::Keep(cached)) => {
2085                    drop(applier);
2086                    if let Some(kept) = self.keep_measurement(node_id, constraints, &cached)? {
2087                        return Ok(Some(kept));
2088                    }
2089                    keep = false;
2090                }
2091                Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
2092                Err(err) => return Err(err),
2093            }
2094        };
2095        let LayoutNodeMeasure {
2096            runtime_state,
2097            chain,
2098            mut pools,
2099            spare,
2100        } = measure;
2101
2102        let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
2103            state: &runtime_state,
2104        };
2105        self.bind_layout_children(
2106            &mut applier,
2107            &runtime_state,
2108            &pools.child_ids,
2109            ChildPass::Measure,
2110        )?;
2111        drop(applier);
2112        pools.child_ids.clear();
2113
2114        let runtime_state = runtime_state.borrow();
2115        let measurement = self.measure_through_modifier_chain(
2116            node_id,
2117            &runtime_state,
2118            chain,
2119            constraints,
2120            &mut pools.placements,
2121            &mut pools.child_ids,
2122        );
2123
2124        if let Some(err) = runtime_state.take_child_error() {
2125            for child_state in runtime_state.child_states() {
2126                child_state.measured.take();
2127            }
2128            self.with_applier_result(|applier| {
2129                applier.with_node::<LayoutNode, _>(node_id, |node| {
2130                    runtime_state.write_node_geometry(node_id, node, &measurement);
2131                })
2132            })
2133            .ok();
2134            return Err(err);
2135        }
2136
2137        let MeasuredSpare {
2138            node: spare,
2139            mut children,
2140        } = spare;
2141        runtime_state.measured_children(
2142            &pools.placements,
2143            &pools.child_ids,
2144            measurement.content_offset,
2145            &mut children,
2146        );
2147        let measured = MeasuredNode::new(
2148            node_id,
2149            measurement.size,
2150            measurement.offset,
2151            measurement.content_offset,
2152            children,
2153        )
2154        .with_alignment_lines(measurement.alignment_lines)
2155        .with_window_root(measurement.window_root)
2156        .into_rc(spare);
2157
2158        self.with_applier_result(|applier| {
2159            applier.with_node::<LayoutNode, _>(node_id, |node| {
2160                node.cache_handles().store_held_measurement(
2161                    constraints,
2162                    Rc::clone(&measured),
2163                    measurement.hold,
2164                );
2165                runtime_state.write_node_geometry(node_id, node, &measurement);
2166                node.clear_needs_measure();
2167                node.clear_needs_layout();
2168                node.set_measured_size(measurement.size);
2169                node.set_content_offset(measurement.content_offset);
2170            })
2171        })
2172        .ok();
2173
2174        Ok(Some(measured))
2175    }
2176
2177    /// The intrinsic size `kind` names of a layout node, through its modifier
2178    /// chain and measure policy, as Compose computes it: no measure, no
2179    /// placement, and the node's measurement cache stays as it was. `None`
2180    /// when the node is not a layout node.
2181    fn layout_node_intrinsic(
2182        self: &Rc<Self>,
2183        node_id: NodeId,
2184        kind: IntrinsicKind,
2185    ) -> Result<Option<f32>, NodeError> {
2186        let Ok(mut applier) = self.applier.try_borrow_typed() else {
2187            return Ok(None);
2188        };
2189        let mut pools = VecPools::acquire(&self.frame_arena);
2190        let bound = applier.with_node::<LayoutNode, _>(node_id, |node| {
2191            let runtime_state = node.layout_runtime_state_handle();
2192            let chain = runtime_state.borrow_mut().bind_node(node);
2193            pools.child_ids.extend_from_slice(&node.children);
2194            (runtime_state, chain)
2195        });
2196        let (runtime_state, chain) = match bound {
2197            Ok(bound) => bound,
2198            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
2199            Err(err) => return Err(err),
2200        };
2201        let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
2202            state: &runtime_state,
2203        };
2204        self.bind_layout_children(
2205            &mut applier,
2206            &runtime_state,
2207            &pools.child_ids,
2208            ChildPass::Intrinsics,
2209        )?;
2210        drop(applier);
2211        pools.child_ids.clear();
2212
2213        let runtime_state = runtime_state.borrow();
2214        let value = if chain.uses_chain {
2215            let scope = crate::density::DensityMeasureScope::new(chain.density);
2216            let frame = CoordinatorFrame::new(
2217                &runtime_state.measure_policy,
2218                &scope,
2219                runtime_state.child_measurables(),
2220                runtime_state.child_states(),
2221                &mut pools.placements,
2222                &mut pools.child_ids,
2223            );
2224            runtime_state
2225                .coordinator_chain
2226                .intrinsic_from(0, &frame, kind)
2227        } else {
2228            policy_intrinsic(
2229                runtime_state.measure_policy.as_ref(),
2230                runtime_state.child_measurables(),
2231                kind,
2232            )
2233        };
2234        match runtime_state.take_child_error() {
2235            Some(err) => Err(err),
2236            None => Ok(Some(value)),
2237        }
2238    }
2239
2240    /// The measurement of a node whose own inputs did not change since it
2241    /// measured at `constraints`: only nodes below it are dirty.
2242    fn measurement_to_keep(
2243        &self,
2244        node: &LayoutNode,
2245        constraints: Constraints,
2246    ) -> Option<Rc<MeasuredNode>> {
2247        if node.needs_measure() || node.needs_layout() || node.is_virtual() {
2248            return None;
2249        }
2250        let cache = node.cache_handles();
2251        if cache.epoch() < self.cache_floor {
2252            return None;
2253        }
2254        cache.get_measurement(constraints)
2255    }
2256
2257    /// Measures again the children of `node_id` that changed, at the
2258    /// constraints they last had, and keeps the node's measurement when each
2259    /// of them measures the same for its parent: the node's policy would
2260    /// place them as before. `None` when one of them changed, and the node
2261    /// has to measure again.
2262    fn keep_measurement(
2263        self: &Rc<Self>,
2264        node_id: NodeId,
2265        constraints: Constraints,
2266        cached: &Rc<MeasuredNode>,
2267    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2268        let runtime_state = self.with_applier_result(|applier| {
2269            applier.with_node::<LayoutNode, _>(node_id, |node| node.layout_runtime_state_handle())
2270        })?;
2271        let mut next_state = 0;
2272        let kept = self.remeasure_changed_children(
2273            cached,
2274            |child_id| self.layout_child_change(&runtime_state, &mut next_state, child_id),
2275            |_| {},
2276        )?;
2277        let Some(kept) = kept else {
2278            return Ok(None);
2279        };
2280        self.with_applier_result(|applier| {
2281            applier.with_node::<LayoutNode, _>(node_id, |node| {
2282                node.cache_handles()
2283                    .store_measurement(constraints, Rc::clone(&kept));
2284                node.clear_needs_layout();
2285            })
2286        })?;
2287        Ok(Some(kept))
2288    }
2289
2290    /// Walks the children of a kept measurement, measures again each one
2291    /// `change` finds changed, at the constraints it last had, and keeps the
2292    /// measurement when each of them reads the same for it. `None` as soon as
2293    /// `change` refuses a child or one measures differently for its parent.
2294    fn remeasure_changed_children(
2295        self: &Rc<Self>,
2296        cached: &Rc<MeasuredNode>,
2297        mut change: impl FnMut(NodeId) -> Result<ChildChange, NodeError>,
2298        mut remeasured: impl FnMut(NodeId),
2299    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2300        for previous in &cached.children {
2301            let child = match change(previous.node_id)? {
2302                ChildChange::Unchanged => continue,
2303                ChildChange::Refuse => return Ok(None),
2304                ChildChange::Changed(child) => child,
2305            };
2306            let measured = self.measure_node(previous.node_id, child.constraints)?;
2307            remeasured(previous.node_id);
2308            if !previous.for_parent.same_as(&ForParent::of(&measured)) {
2309                return Ok(None);
2310            }
2311            if child.placed {
2312                self.place_where_it_was(previous.node_id);
2313            }
2314        }
2315        Ok(Some(Rc::clone(cached)))
2316    }
2317
2318    /// What the walk over a kept layout node finds of a child, from the
2319    /// record of the node's last measure, which `next_state` walks in order.
2320    /// A changed child refuses the keep when its parent data changed, the
2321    /// node read its intrinsic sizes, or the node did not measure it.
2322    fn layout_child_change(
2323        &self,
2324        runtime_state: &RefCell<LayoutRuntimeState>,
2325        next_state: &mut usize,
2326        child_id: NodeId,
2327    ) -> Result<ChildChange, NodeError> {
2328        let runtime_state = runtime_state.borrow();
2329        let Some((offset, state)) = runtime_state
2330            .child_states()
2331            .get(*next_state..)
2332            .unwrap_or_default()
2333            .iter()
2334            .enumerate()
2335            .find(|(_, state)| state.node_id == child_id)
2336        else {
2337            return Ok(ChildChange::Refuse);
2338        };
2339        *next_state += offset + 1;
2340        self.child_change(child_id, |view| {
2341            let parent_data_changed = view.parent_data.as_ref().is_some_and(|data| {
2342                read_cell(&state.parent_data, |bound| bound.as_ref() != Some(data))
2343            });
2344            match state.measured_constraints.get() {
2345                Some(constraints) if !state.read_intrinsics.get() && !parent_data_changed => {
2346                    ChildChange::Changed(ChangedChild {
2347                        constraints,
2348                        placed: view.placed,
2349                    })
2350                }
2351                _ => ChildChange::Refuse,
2352            }
2353        })
2354    }
2355
2356    /// Stores a subcompose node's new measurement in its cache, as a layout
2357    /// node's measure stores its own.
2358    fn store_subcompose_measurement(
2359        &self,
2360        node_id: NodeId,
2361        constraints: Constraints,
2362        measured: &Rc<MeasuredNode>,
2363    ) {
2364        let Ok(mut applier) = self.applier.try_borrow_typed() else {
2365            return;
2366        };
2367        let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
2368            node.cache_handles()
2369                .store_measurement(constraints, Rc::clone(measured));
2370        });
2371    }
2372
2373    /// Places a child of a kept node where it was: the node's policy would
2374    /// place it there again.
2375    fn place_where_it_was(&self, child_id: NodeId) {
2376        let Ok(mut applier) = self.applier.try_borrow_typed() else {
2377            return;
2378        };
2379        if applier
2380            .with_node::<LayoutNode, _>(child_id, |child| child.set_position(child.position()))
2381            .is_err()
2382        {
2383            let _ = applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2384                child.set_position(child.layout_state().position());
2385            });
2386        }
2387    }
2388
2389    fn visit_layout_node(
2390        &self,
2391        node: &mut LayoutNode,
2392        constraints: Constraints,
2393        keep: bool,
2394    ) -> LayoutNodeVisit<'_> {
2395        node.clear_placed();
2396        if keep && let Some(cached) = self.measurement_to_keep(node, constraints) {
2397            return LayoutNodeVisit::Keep(cached);
2398        }
2399        let cache = node.cache_handles();
2400        cache.activate(self.cache_epoch);
2401        if !Node::layout_dirty(node)
2402            && let Some(cached) = cache.get_measurement(constraints)
2403        {
2404            node.clear_needs_measure();
2405            node.clear_needs_layout();
2406            return LayoutNodeVisit::Cached(cached);
2407        }
2408
2409        let spare = MeasuredSpare::take(cache);
2410        let runtime_state = node.layout_runtime_state_handle();
2411        let chain = runtime_state.borrow_mut().bind_node(node);
2412        let mut pools = VecPools::acquire(&self.frame_arena);
2413        pools.child_ids.extend_from_slice(&node.children);
2414        LayoutNodeVisit::Measure(LayoutNodeMeasure {
2415            runtime_state,
2416            chain,
2417            pools,
2418            spare,
2419        })
2420    }
2421
2422    fn bind_layout_children(
2423        self: &Rc<Self>,
2424        applier: &mut MemoryApplier,
2425        runtime_state: &RefCell<LayoutRuntimeState>,
2426        child_ids: &[NodeId],
2427        pass: ChildPass,
2428    ) -> Result<(), NodeError> {
2429        let mut runtime_state = runtime_state.borrow_mut();
2430        runtime_state.bind_children(self, pass, child_ids.len());
2431        let mut bound = 0;
2432        for &child_id in child_ids {
2433            if self.bind_layout_child(applier, &mut runtime_state, bound, child_id)? {
2434                bound += 1;
2435            }
2436        }
2437        runtime_state.truncate_children(bound);
2438        Ok(())
2439    }
2440
2441    fn bind_layout_child(
2442        &self,
2443        applier: &mut MemoryApplier,
2444        runtime_state: &mut LayoutRuntimeState,
2445        position: usize,
2446        child_id: NodeId,
2447    ) -> Result<bool, NodeError> {
2448        let bound = applier.with_node::<LayoutNode, _>(child_id, |child| {
2449            runtime_state.child_state_at(position, child_id).bind(
2450                LayoutChildBinding {
2451                    cache: child.cache_handles(),
2452                    layout_state: Some(child.layout_state_handle()),
2453                    parent_data: Some(child.parent_data()),
2454                    dirty: child.needs_layout() || child.needs_measure(),
2455                    descendant_dirty: Node::descendant_needs_layout(child),
2456                },
2457                self,
2458            );
2459        });
2460        match bound {
2461            Ok(()) => Ok(true),
2462            Err(NodeError::TypeMismatch { .. }) => {
2463                match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2464                    runtime_state.child_state_at(position, child_id).bind(
2465                        LayoutChildBinding {
2466                            cache: child.cache_handles(),
2467                            layout_state: None,
2468                            parent_data: Some(child.parent_data()),
2469                            dirty: child.needs_layout() || child.needs_measure(),
2470                            descendant_dirty: Node::descendant_needs_layout(child),
2471                        },
2472                        self,
2473                    );
2474                }) {
2475                    Ok(()) => Ok(true),
2476                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(false),
2477                    Err(err) => Err(err),
2478                }
2479            }
2480            Err(NodeError::Missing { .. }) => Ok(false),
2481            Err(err) => Err(err),
2482        }
2483    }
2484
2485    fn measure_through_modifier_chain(
2486        &self,
2487        node_id: NodeId,
2488        runtime_state: &LayoutRuntimeState,
2489        chain: ModifierChainInputs,
2490        constraints: Constraints,
2491        placements: &mut Vec<Placement>,
2492        placement_indices: &mut Vec<usize>,
2493    ) -> ModifierChainMeasurement {
2494        let scope = crate::density::DensityMeasureScope::new(chain.density);
2495
2496        if !chain.uses_chain {
2497            let measurement = runtime_state.measure_policy.measure_into(
2498                &scope,
2499                runtime_state.child_measurables(),
2500                constraints,
2501                placements,
2502            );
2503            return ModifierChainMeasurement {
2504                size: measurement.size,
2505                alignment_lines: inherited_alignment_lines(
2506                    runtime_state.child_states(),
2507                    placements,
2508                    placement_indices,
2509                )
2510                .with_overrides(measurement.alignment_lines),
2511                content_offset: Point::default(),
2512                offset: chain.offset,
2513                window_root: chain.window_root,
2514                uses_chain: false,
2515                hold: if chain.window_root {
2516                    None
2517                } else {
2518                    runtime_state.measure_policy.measure_hold(
2519                        runtime_state.child_measurables(),
2520                        constraints,
2521                        measurement.size,
2522                    )
2523                },
2524            };
2525        }
2526
2527        let frame = CoordinatorFrame::new(
2528            &runtime_state.measure_policy,
2529            &scope,
2530            runtime_state.child_measurables(),
2531            runtime_state.child_states(),
2532            placements,
2533            placement_indices,
2534        );
2535        let placeable = runtime_state
2536            .coordinator_chain
2537            .measure_from(0, &frame, constraints);
2538        let (content_x, content_y) = placeable.content_offset();
2539
2540        let invalidations = frame.take_invalidations();
2541        if !invalidations.is_empty() {
2542            self.with_applier_result(|applier| {
2543                applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2544                    for kind in invalidations {
2545                        match kind {
2546                            InvalidationKind::Layout => layout_node.mark_needs_measure(),
2547                            InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2548                            InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2549                            InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2550                            InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2551                        }
2552                    }
2553                })
2554            })
2555            .ok();
2556        }
2557
2558        ModifierChainMeasurement {
2559            alignment_lines: placeable.alignment_lines(),
2560            size: Size {
2561                width: placeable.width(),
2562                height: placeable.height(),
2563            },
2564            content_offset: Point {
2565                x: content_x - chain.offset.x,
2566                y: content_y - chain.offset.y,
2567            },
2568            offset: chain.offset,
2569            window_root: chain.window_root,
2570            uses_chain: true,
2571            hold: if chain.window_root {
2572                None
2573            } else {
2574                runtime_state.coordinator_chain.measured_hold(
2575                    cranpose_ui_layout::MeasureScope::density(&scope),
2576                    runtime_state.measure_policy.as_ref(),
2577                    runtime_state.child_measurables(),
2578                )
2579            },
2580        }
2581    }
2582}
2583
2584struct VecPools<'a> {
2585    arena: &'a RefCell<FrameLayoutArena>,
2586    child_ids: Vec<NodeId>,
2587    placements: Vec<Placement>,
2588}
2589
2590impl<'a> VecPools<'a> {
2591    fn acquire(arena: &'a RefCell<FrameLayoutArena>) -> Self {
2592        let mut pools = arena.borrow_mut();
2593        let child_ids = pools.tmp_child_ids.acquire();
2594        let placements = pools.tmp_placements.acquire();
2595        Self {
2596            arena,
2597            child_ids,
2598            placements,
2599        }
2600    }
2601}
2602
2603impl Drop for VecPools<'_> {
2604    fn drop(&mut self) {
2605        let mut pools = self.arena.borrow_mut();
2606        pools
2607            .tmp_child_ids
2608            .release(std::mem::take(&mut self.child_ids));
2609        pools
2610            .tmp_placements
2611            .release(std::mem::take(&mut self.placements));
2612    }
2613}
2614
2615struct SlotsGuard<'a> {
2616    table: &'a RefCell<SlotTable>,
2617    slots: Option<SlotTable>,
2618}
2619
2620impl<'a> SlotsGuard<'a> {
2621    fn take(table: &'a RefCell<SlotTable>) -> Self {
2622        let slots = std::mem::take(&mut *table.borrow_mut());
2623        Self {
2624            table,
2625            slots: Some(slots),
2626        }
2627    }
2628
2629    fn host(&mut self) -> Rc<SlotsHost> {
2630        let slots = self.slots.take().unwrap_or_default();
2631        Rc::new(SlotsHost::new(slots))
2632    }
2633
2634    fn restore(&mut self, slots: SlotTable) {
2635        debug_assert!(self.slots.is_none());
2636        self.slots = Some(slots);
2637    }
2638}
2639
2640impl Drop for SlotsGuard<'_> {
2641    fn drop(&mut self) {
2642        if let Some(slots) = self.slots.take() {
2643            *self.table.borrow_mut() = slots;
2644        }
2645    }
2646}
2647
2648#[derive(Debug, Clone)]
2649pub(crate) struct MeasuredNode {
2650    node_id: NodeId,
2651    size: Size,
2652    offset: Point,
2653    content_offset: Point,
2654    alignment_lines: AlignmentLines,
2655    children: Vec<MeasuredChild>,
2656    window_root: bool,
2657}
2658
2659impl MeasuredNode {
2660    fn new(
2661        node_id: NodeId,
2662        size: Size,
2663        offset: Point,
2664        content_offset: Point,
2665        children: Vec<MeasuredChild>,
2666    ) -> Self {
2667        Self {
2668            node_id,
2669            size,
2670            offset,
2671            content_offset,
2672            alignment_lines: AlignmentLines::default(),
2673            children,
2674            window_root: false,
2675        }
2676    }
2677
2678    fn with_window_root(mut self, window_root: bool) -> Self {
2679        self.window_root = window_root;
2680        self
2681    }
2682
2683    fn with_alignment_lines(mut self, alignment_lines: AlignmentLines) -> Self {
2684        self.alignment_lines = alignment_lines;
2685        self
2686    }
2687
2688    pub(crate) fn alignment_lines_for_parent(&self) -> AlignmentLines {
2689        if self.window_root {
2690            AlignmentLines::default()
2691        } else {
2692            self.alignment_lines
2693        }
2694    }
2695
2696    pub(crate) fn size_for_parent(&self) -> Size {
2697        if self.window_root {
2698            Size::new(0.0, 0.0)
2699        } else {
2700            self.size
2701        }
2702    }
2703
2704    #[cfg(test)]
2705    pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2706        Self::new(
2707            node_id,
2708            size,
2709            Point::default(),
2710            Point::default(),
2711            Vec::new(),
2712        )
2713    }
2714
2715    pub(crate) fn node_id(&self) -> NodeId {
2716        self.node_id
2717    }
2718
2719    pub(crate) fn size(&self) -> Size {
2720        self.size
2721    }
2722
2723    /// The measurement in `spare`'s memory, or in a new allocation when
2724    /// there is no spare.
2725    fn into_rc(self, spare: Option<Rc<MeasuredNode>>) -> Rc<MeasuredNode> {
2726        let Some(mut spare) = spare else {
2727            return Rc::new(self);
2728        };
2729        match Rc::get_mut(&mut spare) {
2730            Some(slot) => *slot = self,
2731            None => return Rc::new(self),
2732        }
2733        spare
2734    }
2735}
2736
2737/// The memory of a node's last measurement, children's list included, for
2738/// its next measure to fill. It is taken only when nothing else holds the
2739/// measurement.
2740#[derive(Default)]
2741struct MeasuredSpare {
2742    node: Option<Rc<MeasuredNode>>,
2743    children: Vec<MeasuredChild>,
2744}
2745
2746impl MeasuredSpare {
2747    fn take(cache: &LayoutNodeCacheHandles) -> Self {
2748        let Some(mut node) = cache.take_previous_measurement() else {
2749            return Self::default();
2750        };
2751        let Some(previous) = Rc::get_mut(&mut node) else {
2752            return Self::default();
2753        };
2754        let mut children = std::mem::take(&mut previous.children);
2755        children.clear();
2756        Self {
2757            node: Some(node),
2758            children,
2759        }
2760    }
2761}
2762
2763/// What a parent's measurement keeps of a child: which child, where the
2764/// policy placed it, and what of the child's measurement the policy read.
2765/// The measurement itself stays with the child's cache, its one holder, so
2766/// the child's next measure can fill it in place.
2767#[derive(Debug, Clone, Copy)]
2768struct MeasuredChild {
2769    node_id: NodeId,
2770    offset: Point,
2771    for_parent: ForParent,
2772}
2773
2774impl MeasuredChild {
2775    fn new(measured: &MeasuredNode, offset: Point) -> Self {
2776        Self {
2777            node_id: measured.node_id,
2778            offset,
2779            for_parent: ForParent::of(measured),
2780        }
2781    }
2782}
2783
2784/// What a parent's policy reads of a child's measurement: the size and the
2785/// baselines, and the offset it places the child by.
2786#[derive(Debug, Clone, Copy)]
2787struct ForParent {
2788    size: Size,
2789    offset: Point,
2790    alignment_lines: AlignmentLines,
2791}
2792
2793impl ForParent {
2794    fn of(measured: &MeasuredNode) -> Self {
2795        Self {
2796            size: measured.size_for_parent(),
2797            offset: measured.offset,
2798            alignment_lines: measured.alignment_lines_for_parent(),
2799        }
2800    }
2801
2802    /// Whether a parent's policy, reading `other` in place of this, would
2803    /// lay its children out the same.
2804    fn same_as(&self, other: &Self) -> bool {
2805        self.size == other.size
2806            && self.offset == other.offset
2807            && self.alignment_lines.first_baseline() == other.alignment_lines.first_baseline()
2808            && self.alignment_lines.last_baseline() == other.alignment_lines.last_baseline()
2809    }
2810}
2811
2812/// The intrinsic size `kind` names, of a measurable.
2813fn measurable_intrinsic(measurable: &dyn Measurable, kind: IntrinsicKind) -> f32 {
2814    match kind {
2815        IntrinsicKind::MinWidth(height) => measurable.min_intrinsic_width(height),
2816        IntrinsicKind::MaxWidth(height) => measurable.max_intrinsic_width(height),
2817        IntrinsicKind::MinHeight(width) => measurable.min_intrinsic_height(width),
2818        IntrinsicKind::MaxHeight(width) => measurable.max_intrinsic_height(width),
2819    }
2820}
2821
2822/// The intrinsic size `kind` names, of a measure policy over `measurables`.
2823fn policy_intrinsic(
2824    policy: &dyn MeasurePolicy,
2825    measurables: &[Box<dyn Measurable>],
2826    kind: IntrinsicKind,
2827) -> f32 {
2828    match kind {
2829        IntrinsicKind::MinWidth(height) => policy.min_intrinsic_width(measurables, height),
2830        IntrinsicKind::MaxWidth(height) => policy.max_intrinsic_width(measurables, height),
2831        IntrinsicKind::MinHeight(width) => policy.min_intrinsic_height(measurables, width),
2832        IntrinsicKind::MaxHeight(width) => policy.max_intrinsic_height(measurables, width),
2833    }
2834}
2835
2836/// The intrinsic size `kind` names, of a layout modifier around `wrapped`.
2837fn layout_modifier_intrinsic(
2838    node: &dyn cranpose_foundation::LayoutModifierNode,
2839    wrapped: &dyn Measurable,
2840    kind: IntrinsicKind,
2841    density: f32,
2842) -> f32 {
2843    match kind {
2844        IntrinsicKind::MinWidth(height) => node.min_intrinsic_width(wrapped, height, density),
2845        IntrinsicKind::MaxWidth(height) => node.max_intrinsic_width(wrapped, height, density),
2846        IntrinsicKind::MinHeight(width) => node.min_intrinsic_height(wrapped, width, density),
2847        IntrinsicKind::MaxHeight(width) => node.max_intrinsic_height(wrapped, width, density),
2848    }
2849}
2850
2851struct CoordinatorFrame<'a> {
2852    measure_policy: &'a Rc<dyn MeasurePolicy>,
2853    scope: &'a dyn cranpose_ui_layout::MeasureScope,
2854    measurables: &'a [Box<dyn Measurable>],
2855    child_states: &'a [Rc<LayoutChildMeasureState>],
2856    placements: RefCell<&'a mut Vec<Placement>>,
2857    placement_indices: RefCell<&'a mut Vec<usize>>,
2858    context: RefCell<LayoutNodeContext>,
2859}
2860
2861impl<'a> CoordinatorFrame<'a> {
2862    fn new(
2863        measure_policy: &'a Rc<dyn MeasurePolicy>,
2864        scope: &'a dyn cranpose_ui_layout::MeasureScope,
2865        measurables: &'a [Box<dyn Measurable>],
2866        child_states: &'a [Rc<LayoutChildMeasureState>],
2867        placements: &'a mut Vec<Placement>,
2868        placement_indices: &'a mut Vec<usize>,
2869    ) -> Self {
2870        Self {
2871            measure_policy,
2872            scope,
2873            measurables,
2874            child_states,
2875            placements: RefCell::new(placements),
2876            placement_indices: RefCell::new(placement_indices),
2877            context: RefCell::new(LayoutNodeContext::new(scope.density())),
2878        }
2879    }
2880
2881    fn take_invalidations(&self) -> Vec<InvalidationKind> {
2882        self.context.borrow_mut().take_invalidations()
2883    }
2884}
2885
2886struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2887    chain: &'chain CoordinatorChain,
2888    frame: &'frame_ref CoordinatorFrame<'frame_data>,
2889    index: usize,
2890    alignment_lines: Cell<AlignmentLines>,
2891}
2892
2893impl<'chain, 'frame_ref, 'frame_data> CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2894    fn new(
2895        chain: &'chain CoordinatorChain,
2896        frame: &'frame_ref CoordinatorFrame<'frame_data>,
2897        index: usize,
2898    ) -> Self {
2899        Self {
2900            chain,
2901            frame,
2902            index,
2903            alignment_lines: Cell::default(),
2904        }
2905    }
2906}
2907
2908impl Measurable for CoordinatorLink<'_, '_, '_> {
2909    fn measure(&self, constraints: Constraints) -> Placeable {
2910        let placeable = self.chain.measure_from(self.index, self.frame, constraints);
2911        self.alignment_lines.set(placeable.alignment_lines());
2912        placeable
2913    }
2914
2915    fn min_intrinsic_width(&self, height: f32) -> f32 {
2916        self.chain
2917            .intrinsic_from(self.index, self.frame, IntrinsicKind::MinWidth(height))
2918    }
2919
2920    fn max_intrinsic_width(&self, height: f32) -> f32 {
2921        self.chain
2922            .intrinsic_from(self.index, self.frame, IntrinsicKind::MaxWidth(height))
2923    }
2924
2925    fn min_intrinsic_height(&self, width: f32) -> f32 {
2926        self.chain
2927            .intrinsic_from(self.index, self.frame, IntrinsicKind::MinHeight(width))
2928    }
2929
2930    fn max_intrinsic_height(&self, width: f32) -> f32 {
2931        self.chain
2932            .intrinsic_from(self.index, self.frame, IntrinsicKind::MaxHeight(width))
2933    }
2934}
2935
2936struct CoordinatorNode {
2937    modifier_index: usize,
2938    node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2939    measured_size: Cell<Size>,
2940    /// The constraints this node last measured under.
2941    measured_constraints: Cell<Constraints>,
2942    accumulated_offset: Cell<Point>,
2943}
2944
2945impl CoordinatorNode {
2946    fn new(
2947        modifier_index: usize,
2948        node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2949    ) -> Self {
2950        Self {
2951            modifier_index,
2952            node,
2953            measured_size: Cell::new(Size::default()),
2954            measured_constraints: Cell::new(Constraints::tight(0.0, 0.0)),
2955            accumulated_offset: Cell::new(Point::default()),
2956        }
2957    }
2958
2959    fn matches(
2960        &self,
2961        modifier_index: usize,
2962        node: &Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2963    ) -> bool {
2964        self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2965    }
2966
2967    #[cfg(test)]
2968    fn ptr(&self) -> usize {
2969        Rc::as_ptr(&self.node) as *const () as usize
2970    }
2971}
2972
2973#[derive(Default)]
2974struct CoordinatorChain {
2975    nodes: Vec<CoordinatorNode>,
2976    /// The size the node's own measure policy measured its content at.
2977    inner_size: Cell<Size>,
2978    /// The constraints the node's own measure policy last measured under.
2979    inner_constraints: Cell<Option<Constraints>>,
2980    /// The chain revision the nodes were last synced at and the inputs it
2981    /// gave: a node whose modifiers did not change since measures again
2982    /// without walking its chain.
2983    synced: Option<(u64, ModifierChainInputs)>,
2984}
2985
2986impl CoordinatorChain {
2987    fn sync(&mut self, node: &LayoutNode) -> ModifierChainInputs {
2988        let density = node.density();
2989        let revision = node.modifier_chain().revision();
2990        if let Some((synced, inputs)) = self.synced
2991            && synced == revision
2992            && inputs.density == density
2993        {
2994            return inputs;
2995        }
2996        let inputs = self.walk(node, density);
2997        self.synced = Some((revision, inputs));
2998        inputs
2999    }
3000
3001    /// Syncs the nodes with the layout nodes of `node`'s chain and sums
3002    /// its offsets.
3003    fn walk(&mut self, node: &LayoutNode, density: crate::density::Density) -> ModifierChainInputs {
3004        let mut inputs = ModifierChainInputs {
3005            density,
3006            window_root: node.is_window_root(),
3007            offset: Point::default(),
3008            uses_chain: false,
3009        };
3010        let chain_handle = node.modifier_chain();
3011        if !chain_handle.has_layout_nodes() {
3012            return inputs;
3013        }
3014
3015        let chain = chain_handle.chain();
3016        let mut len = 0;
3017        let mut matches = true;
3018        chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
3019            let Some(index) = node_ref.entry_index() else {
3020                return;
3021            };
3022            if let Some(node) = chain.get_node_rc(index) {
3023                matches = matches
3024                    && self
3025                        .nodes
3026                        .get(len)
3027                        .is_some_and(|candidate| candidate.matches(index, node));
3028                len += 1;
3029            }
3030            node_ref.with_node(|node| {
3031                if let Some(offset_node) = node
3032                    .as_any()
3033                    .downcast_ref::<crate::modifier_nodes::OffsetNode>()
3034                {
3035                    let delta = offset_node.device_offset(density.density());
3036                    inputs.offset.x += delta.x;
3037                    inputs.offset.y += delta.y;
3038                }
3039            });
3040        });
3041
3042        inputs.uses_chain = len > 0;
3043        if inputs.uses_chain && !(matches && len == self.nodes.len()) {
3044            self.rebuild(chain, len);
3045        }
3046        inputs
3047    }
3048
3049    fn rebuild(&mut self, chain: &cranpose_foundation::ModifierNodeChain, len: usize) {
3050        let mut previous_nodes = std::mem::take(&mut self.nodes);
3051        // Exactly: most chains hold one layout modifier, where growth would
3052        // make room for four.
3053        self.nodes.reserve_exact(len);
3054        chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
3055            let Some((index, node)) = node_ref
3056                .entry_index()
3057                .and_then(|index| chain.get_node_rc(index).map(|node| (index, node)))
3058            else {
3059                return;
3060            };
3061            match previous_nodes
3062                .iter()
3063                .position(|candidate| candidate.matches(index, node))
3064            {
3065                Some(position) => self.nodes.push(previous_nodes.swap_remove(position)),
3066                None => self
3067                    .nodes
3068                    .push(CoordinatorNode::new(index, Rc::clone(node))),
3069            }
3070        });
3071    }
3072
3073    /// What the last measure through this chain holds for: the policy's own
3074    /// range, then each layout modifier from the innermost out turns its
3075    /// content's range into its own; any other node passes its constraints
3076    /// through.
3077    fn measured_hold(
3078        &self,
3079        density: f32,
3080        policy: &dyn MeasurePolicy,
3081        measurables: &[Box<dyn Measurable>],
3082    ) -> Option<cranpose_ui_layout::ConstraintsHold> {
3083        let inner_size = self.inner_size.get();
3084        let mut wrapped = cranpose_ui_layout::WrappedHold {
3085            size: inner_size,
3086            hold: self
3087                .inner_constraints
3088                .get()
3089                .and_then(|constraints| policy.measure_hold(measurables, constraints, inner_size)),
3090        };
3091        for node in self.nodes.iter().rev() {
3092            let size = node.measured_size.get();
3093            let hold = match node.node.borrow().as_layout_node() {
3094                Some(layout) => {
3095                    layout.measure_hold(density, node.measured_constraints.get(), size, wrapped)
3096                }
3097                None => wrapped.hold,
3098            };
3099            wrapped = cranpose_ui_layout::WrappedHold { size, hold };
3100        }
3101        wrapped.hold
3102    }
3103
3104    fn measure_from(
3105        &self,
3106        index: usize,
3107        frame: &CoordinatorFrame<'_>,
3108        constraints: Constraints,
3109    ) -> Placeable {
3110        let Some(node) = self.nodes.get(index) else {
3111            let mut placements = frame.placements.borrow_mut();
3112            let measurement = frame.measure_policy.measure_into(
3113                frame.scope,
3114                frame.measurables,
3115                constraints,
3116                &mut placements,
3117            );
3118            self.inner_size.set(measurement.size);
3119            self.inner_constraints.set(Some(constraints));
3120            return Placeable::value(
3121                measurement.size.width,
3122                measurement.size.height,
3123                NodeId::default(),
3124            )
3125            .with_alignment_lines(
3126                inherited_alignment_lines(
3127                    frame.child_states,
3128                    &placements,
3129                    &mut frame.placement_indices.borrow_mut(),
3130                )
3131                .with_overrides(measurement.alignment_lines),
3132            );
3133        };
3134
3135        let wrapped = CoordinatorLink::new(self, frame, index + 1);
3136        let node_borrow = node.node.borrow();
3137        node.measured_constraints.set(constraints);
3138
3139        let Some(layout_node) = node_borrow.as_layout_node() else {
3140            let placeable = wrapped.measure(constraints);
3141            node.measured_size.set(Size {
3142                width: placeable.width(),
3143                height: placeable.height(),
3144            });
3145            let child_accumulated = self.total_content_offset_from(index + 1);
3146            node.accumulated_offset.set(child_accumulated);
3147            return placeable;
3148        };
3149
3150        let result = match frame.context.try_borrow_mut() {
3151            Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
3152            Err(_) => {
3153                let mut temp = LayoutNodeContext::new(frame.scope.density());
3154                let result = layout_node.measure(&mut temp, &wrapped, constraints);
3155                if let Ok(mut context) = frame.context.try_borrow_mut() {
3156                    for kind in temp.take_invalidations() {
3157                        context.invalidate(kind);
3158                    }
3159                }
3160                result
3161            }
3162        };
3163
3164        node.measured_size.set(result.size);
3165        let local_offset = Point {
3166            x: result.placement_offset_x,
3167            y: result.placement_offset_y,
3168        };
3169        let child_accumulated = self.total_content_offset_from(index + 1);
3170        let accumulated = Point {
3171            x: local_offset.x + child_accumulated.x,
3172            y: local_offset.y + child_accumulated.y,
3173        };
3174        node.accumulated_offset.set(accumulated);
3175
3176        Placeable::value_with_offset(
3177            result.size.width,
3178            result.size.height,
3179            NodeId::default(),
3180            (accumulated.x, accumulated.y),
3181        )
3182        .with_alignment_lines(
3183            wrapped
3184                .alignment_lines
3185                .get()
3186                .translated(local_offset.y)
3187                .with_overrides(result.alignment_lines),
3188        )
3189    }
3190
3191    fn intrinsic_from(
3192        &self,
3193        index: usize,
3194        frame: &CoordinatorFrame<'_>,
3195        kind: IntrinsicKind,
3196    ) -> f32 {
3197        let Some(node) = self.nodes.get(index) else {
3198            return policy_intrinsic(frame.measure_policy.as_ref(), frame.measurables, kind);
3199        };
3200        let wrapped = CoordinatorLink::new(self, frame, index + 1);
3201        let node_borrow = node.node.borrow();
3202        node_borrow.as_layout_node().map_or_else(
3203            || measurable_intrinsic(&wrapped, kind),
3204            |layout_node| {
3205                layout_modifier_intrinsic(layout_node, &wrapped, kind, frame.scope.density())
3206            },
3207        )
3208    }
3209
3210    /// Writes where each coordinator and the node's content ended up in the
3211    /// last measure, relative to the node drawn at its `node_offset`, and
3212    /// says whether any of them moved.
3213    fn write_geometry(
3214        &self,
3215        geometry: &crate::modifier::CoordinatorGeometry,
3216        node_offset: Point,
3217    ) -> bool {
3218        let content = self.total_content_offset_from(0);
3219        let placed = |inner_offset: Point, size: Size| GeometryRect {
3220            x: content.x - inner_offset.x - node_offset.x,
3221            y: content.y - inner_offset.y - node_offset.y,
3222            width: size.width,
3223            height: size.height,
3224        };
3225        geometry.replace(
3226            self.nodes
3227                .iter()
3228                .map(|node| placed(node.accumulated_offset.get(), node.measured_size.get()))
3229                .chain(std::iter::once(placed(
3230                    Point::default(),
3231                    self.inner_size.get(),
3232                ))),
3233        )
3234    }
3235
3236    fn total_content_offset_from(&self, index: usize) -> Point {
3237        self.nodes
3238            .get(index)
3239            .map(|node| node.accumulated_offset.get())
3240            .unwrap_or_default()
3241    }
3242
3243    #[cfg(test)]
3244    fn debug_ptrs(&self) -> Vec<usize> {
3245        self.nodes.iter().map(CoordinatorNode::ptr).collect()
3246    }
3247}
3248
3249fn inherited_alignment_lines(
3250    child_states: &[Rc<LayoutChildMeasureState>],
3251    placements: &[Placement],
3252    placement_indices: &mut Vec<usize>,
3253) -> AlignmentLines {
3254    placement_indices.clear();
3255    let in_child_order = placements.len() == child_states.len()
3256        && placements
3257            .iter()
3258            .zip(child_states)
3259            .all(|(placement, child)| placement.node_id == child.node_id);
3260    if !in_child_order {
3261        placement_indices.extend(0..placements.len());
3262        placement_indices.sort_unstable_by_key(|&index| (placements[index].node_id, index));
3263    }
3264    let mut lines = AlignmentLines::default();
3265    for (index, child) in child_states.iter().enumerate() {
3266        let placement = placement_for_child(placements, placement_indices, index, child.node_id);
3267        read_cell(&child.measured, |measured| {
3268            if let Some(measured) = measured {
3269                lines.merge(
3270                    measured
3271                        .alignment_lines_for_parent()
3272                        .translated(child.placement_position(placement).y),
3273                );
3274            }
3275        });
3276    }
3277    lines
3278}
3279
3280fn placement_for_child<'a>(
3281    placements: &'a [Placement],
3282    indices: &[usize],
3283    child_index: usize,
3284    node_id: NodeId,
3285) -> Option<&'a Placement> {
3286    let index = if indices.is_empty() {
3287        child_index
3288    } else {
3289        let first = indices.partition_point(|&index| placements[index].node_id < node_id);
3290        *indices.get(first)?
3291    };
3292    placements
3293        .get(index)
3294        .filter(|placement| placement.node_id == node_id)
3295}
3296
3297pub(crate) struct LayoutRuntimeState {
3298    /// `None` until the node first binds a child: most nodes are leaves.
3299    children: Option<Box<LayoutChildren>>,
3300    coordinator_chain: CoordinatorChain,
3301    measure_policy: Rc<dyn MeasurePolicy>,
3302}
3303
3304/// What a node keeps for measuring its children, in their order.
3305struct LayoutChildren {
3306    ids: Vec<NodeId>,
3307    states: Vec<Rc<LayoutChildMeasureState>>,
3308    measurables: Vec<Box<dyn Measurable>>,
3309    frame: Rc<LayoutChildFrame>,
3310}
3311
3312impl LayoutChildren {
3313    /// Room for exactly `count` children, where growth would make room for
3314    /// four in each of the three lists. Children added later grow the lists
3315    /// as usual.
3316    fn with_capacity(count: usize) -> Self {
3317        Self {
3318            ids: Vec::with_capacity(count),
3319            states: Vec::with_capacity(count),
3320            measurables: Vec::with_capacity(count),
3321            frame: Rc::default(),
3322        }
3323    }
3324}
3325
3326impl LayoutRuntimeState {
3327    pub(crate) fn new(measure_policy: Rc<dyn MeasurePolicy>) -> Self {
3328        Self {
3329            children: None,
3330            coordinator_chain: CoordinatorChain::default(),
3331            measure_policy,
3332        }
3333    }
3334
3335    fn bind_node(&mut self, node: &LayoutNode) -> ModifierChainInputs {
3336        if !Rc::ptr_eq(&self.measure_policy, &node.measure_policy) {
3337            self.measure_policy = Rc::clone(&node.measure_policy);
3338        }
3339        self.coordinator_chain.sync(node)
3340    }
3341
3342    fn child_states(&self) -> &[Rc<LayoutChildMeasureState>] {
3343        self.children
3344            .as_deref()
3345            .map_or(&[], |children| children.states.as_slice())
3346    }
3347
3348    fn child_measurables(&self) -> &[Box<dyn Measurable>] {
3349        self.children
3350            .as_deref()
3351            .map_or(&[], |children| children.measurables.as_slice())
3352    }
3353
3354    /// Binds the children's frame to `builder` for a pass over `count`
3355    /// children. A node that never had one binds nothing.
3356    fn bind_children(&mut self, builder: &Rc<LayoutBuilderState>, pass: ChildPass, count: usize) {
3357        if count == 0 && self.children.is_none() {
3358            return;
3359        }
3360        self.children
3361            .get_or_insert_with(|| Box::new(LayoutChildren::with_capacity(count)))
3362            .frame
3363            .bind(builder, pass);
3364    }
3365
3366    fn unbind_children(&self) {
3367        if let Some(children) = &self.children {
3368            children.frame.unbind();
3369        }
3370    }
3371
3372    /// The first error a child recorded in the pass, taken.
3373    fn take_child_error(&self) -> Option<NodeError> {
3374        self.children
3375            .as_ref()
3376            .and_then(|children| children.frame.error.borrow_mut().take())
3377    }
3378
3379    /// The state of the child at `position`, for a pass `bind_children`
3380    /// bound.
3381    fn child_state_at(&mut self, position: usize, child_id: NodeId) -> &LayoutChildMeasureState {
3382        let children = self
3383            .children
3384            .get_or_insert_with(|| Box::new(LayoutChildren::with_capacity(position + 1)));
3385        if children.ids.get(position) != Some(&child_id) {
3386            let from = match children.ids[position..]
3387                .iter()
3388                .position(|&id| id == child_id)
3389            {
3390                Some(offset) => position + offset,
3391                None => {
3392                    let state = LayoutChildMeasureState::new(child_id, Rc::clone(&children.frame));
3393                    children.ids.push(child_id);
3394                    children.states.push(Rc::clone(&state));
3395                    children
3396                        .measurables
3397                        .push(Box::new(LayoutChildMeasurable::new(state)));
3398                    children.ids.len() - 1
3399                }
3400            };
3401            children.ids.swap(position, from);
3402            children.states.swap(position, from);
3403            children.measurables.swap(position, from);
3404        }
3405        &children.states[position]
3406    }
3407
3408    fn truncate_children(&mut self, len: usize) {
3409        if let Some(children) = &mut self.children {
3410            children.ids.truncate(len);
3411            children.states.truncate(len);
3412            children.measurables.truncate(len);
3413        }
3414    }
3415
3416    /// Puts the children the last measure measured into `measured_children`.
3417    fn measured_children(
3418        &self,
3419        placements: &[Placement],
3420        placement_indices: &[usize],
3421        content_offset: Point,
3422        measured_children: &mut Vec<MeasuredChild>,
3423    ) {
3424        let child_states = self.child_states();
3425        measured_children.reserve_exact(child_states.len());
3426        for (index, child_state) in child_states.iter().enumerate() {
3427            let Some(measured) = child_state.measured.take() else {
3428                continue;
3429            };
3430            let placement =
3431                placement_for_child(placements, placement_indices, index, child_state.node_id);
3432            let base_position = child_state.placement_position(placement);
3433            if placement.is_some() {
3434                child_state.place_retained(Point {
3435                    x: base_position.x + measured.offset.x,
3436                    y: base_position.y + measured.offset.y,
3437                });
3438            }
3439            measured_children.push(MeasuredChild::new(
3440                &measured,
3441                Point {
3442                    x: content_offset.x + base_position.x,
3443                    y: content_offset.y + base_position.y,
3444                },
3445            ));
3446        }
3447    }
3448
3449    fn write_node_geometry(
3450        &self,
3451        node_id: NodeId,
3452        node: &LayoutNode,
3453        measurement: &ModifierChainMeasurement,
3454    ) {
3455        let geometry = node.coordinator_geometry();
3456        let moved = if measurement.uses_chain {
3457            self.coordinator_chain
3458                .write_geometry(geometry, measurement.offset)
3459        } else {
3460            geometry.replace([GeometryRect {
3461                x: 0.0,
3462                y: 0.0,
3463                width: measurement.size.width,
3464                height: measurement.size.height,
3465            }])
3466        };
3467        if moved {
3468            crate::render_state::record_geometry_scene_node(node_id);
3469        }
3470    }
3471
3472    #[cfg(test)]
3473    pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
3474        LayoutRuntimeDebugStats {
3475            child_ids: self
3476                .children
3477                .as_ref()
3478                .map(|children| children.ids.clone())
3479                .unwrap_or_default(),
3480            child_state_ptrs: self
3481                .child_states()
3482                .iter()
3483                .map(|state| Rc::as_ptr(state) as *const () as usize)
3484                .collect(),
3485            child_measurable_ptrs: self
3486                .child_measurables()
3487                .iter()
3488                .map(|measurable| {
3489                    measurable.as_ref() as *const dyn Measurable as *const () as usize
3490                })
3491                .collect(),
3492            child_measurable_count: self.child_measurables().len(),
3493            coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
3494            coordinator_node_count: self.coordinator_chain.nodes.len(),
3495        }
3496    }
3497}
3498
3499#[cfg(test)]
3500#[derive(Debug, Clone, PartialEq, Eq)]
3501pub(crate) struct LayoutRuntimeDebugStats {
3502    pub(crate) child_ids: Vec<NodeId>,
3503    pub(crate) child_state_ptrs: Vec<usize>,
3504    pub(crate) child_measurable_ptrs: Vec<usize>,
3505    pub(crate) child_measurable_count: usize,
3506    pub(crate) coordinator_node_ptrs: Vec<usize>,
3507    pub(crate) coordinator_node_count: usize,
3508}
3509
3510#[derive(Default)]
3511struct LayoutChildFrame {
3512    builder: RefCell<Option<Rc<LayoutBuilderState>>>,
3513    error: RefCell<Option<NodeError>>,
3514    pass: Cell<ChildPass>,
3515}
3516
3517/// What a node binds its children for.
3518#[derive(Clone, Copy, Default, PartialEq, Eq)]
3519enum ChildPass {
3520    /// A measure of the node, which records how it read each child, for a
3521    /// later keep of its measurement.
3522    #[default]
3523    Measure,
3524    /// An intrinsic size of the node, which its parent asks: the record of
3525    /// the node's last measure stays as it was.
3526    Intrinsics,
3527}
3528
3529impl LayoutChildFrame {
3530    fn bind(&self, builder: &Rc<LayoutBuilderState>, pass: ChildPass) {
3531        self.error.borrow_mut().take();
3532        *self.builder.borrow_mut() = Some(Rc::clone(builder));
3533        self.pass.set(pass);
3534    }
3535
3536    fn measures(&self) -> bool {
3537        self.pass.get() == ChildPass::Measure
3538    }
3539
3540    fn unbind(&self) {
3541        self.builder.borrow_mut().take();
3542    }
3543
3544    fn record_error(&self, err: NodeError) {
3545        if self.builder.borrow().is_none() {
3546            return;
3547        }
3548        let mut slot = self.error.borrow_mut();
3549        if slot.is_none() {
3550            *slot = Some(err);
3551        }
3552    }
3553}
3554
3555struct LayoutChildBinding<'a> {
3556    cache: &'a LayoutNodeCacheHandles,
3557    layout_state: Option<&'a Rc<RefCell<LayoutState>>>,
3558    parent_data: Option<cranpose_ui_layout::ParentData>,
3559    /// The child's own layout or measure is dirty.
3560    dirty: bool,
3561    /// Only nodes below the child are dirty.
3562    descendant_dirty: bool,
3563}
3564
3565/// Reads a child that is a layout node or a subcompose node: its flags,
3566/// cache, parent data and placement. `None` for another node type.
3567fn read_layout_child<R>(
3568    applier: &mut MemoryApplier,
3569    child_id: NodeId,
3570    read: impl Fn(
3571        &dyn Node,
3572        &LayoutNodeCacheHandles,
3573        &dyn Fn() -> cranpose_ui_layout::ParentData,
3574        bool,
3575    ) -> R,
3576) -> Result<Option<R>, NodeError> {
3577    match applier.with_node::<LayoutNode, _>(child_id, |child| {
3578        read(
3579            &*child,
3580            child.cache_handles(),
3581            &|| child.parent_data(),
3582            child.is_placed(),
3583        )
3584    }) {
3585        Ok(value) => Ok(Some(value)),
3586        Err(NodeError::TypeMismatch { .. }) => {
3587            match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
3588                read(
3589                    &*child,
3590                    child.cache_handles(),
3591                    &|| child.parent_data(),
3592                    child.layout_state().is_placed(),
3593                )
3594            }) {
3595                Ok(value) => Ok(Some(value)),
3596                Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
3597                Err(err) => Err(err),
3598            }
3599        }
3600        Err(NodeError::Missing { .. }) => Ok(None),
3601        Err(err) => Err(err),
3602    }
3603}
3604
3605/// A parent's record of one child, for each child of every parent: its
3606/// shared parts sit in `Cell`s, read by take and put back, where a
3607/// `RefCell`'s borrow flag would take a word of every record.
3608struct LayoutChildMeasureState {
3609    node_id: NodeId,
3610    frame: Rc<LayoutChildFrame>,
3611    /// The child's cache, `None` until the record is first bound.
3612    cache: Cell<Option<LayoutNodeCacheHandles>>,
3613    cache_epoch: Cell<u64>,
3614    force_remeasure: Cell<bool>,
3615    parent_data: Cell<Option<cranpose_ui_layout::ParentData>>,
3616    /// The constraints the parent's last measure gave the child.
3617    measured_constraints: Cell<Option<Constraints>>,
3618    /// Whether the parent's last measure read the child's intrinsic sizes.
3619    read_intrinsics: Cell<bool>,
3620    measured: Cell<Option<Rc<MeasuredNode>>>,
3621    last_position: Cell<Option<Point>>,
3622    layout_state: Cell<Option<Rc<RefCell<LayoutState>>>>,
3623}
3624
3625/// Runs `read` on what `cell` holds and puts it back. `read` must not reach
3626/// the same cell again.
3627pub(crate) fn read_cell<T: Default, R>(cell: &Cell<T>, read: impl FnOnce(&T) -> R) -> R {
3628    let value = cell.take();
3629    let result = read(&value);
3630    cell.set(value);
3631    result
3632}
3633
3634impl LayoutChildMeasureState {
3635    fn placement_position(&self, placement: Option<&Placement>) -> Point {
3636        placement
3637            .map(|placement| Point {
3638                x: placement.x,
3639                y: placement.y,
3640            })
3641            .or_else(|| self.last_position.get())
3642            .unwrap_or_default()
3643    }
3644
3645    fn new(node_id: NodeId, frame: Rc<LayoutChildFrame>) -> Rc<Self> {
3646        Rc::new(Self {
3647            node_id,
3648            frame,
3649            cache: Cell::new(None),
3650            cache_epoch: Cell::new(0),
3651            force_remeasure: Cell::new(true),
3652            parent_data: Cell::new(None),
3653            measured_constraints: Cell::new(None),
3654            read_intrinsics: Cell::new(false),
3655            measured: Cell::new(None),
3656            last_position: Cell::new(None),
3657            layout_state: Cell::new(None),
3658        })
3659    }
3660
3661    /// Binds the child for a measure of its parent. A child with only dirty
3662    /// nodes below it keeps its cache: it measures again, but may keep its
3663    /// measurement.
3664    fn bind(&self, binding: LayoutChildBinding<'_>, pass: &LayoutBuilderState) {
3665        let child_epoch = binding.cache.epoch();
3666        let stale = binding.dirty || child_epoch < pass.cache_floor;
3667        let cache_epoch = if stale { pass.cache_epoch } else { child_epoch };
3668        binding.cache.activate(cache_epoch);
3669        let mut cache = self.cache.take();
3670        match &mut cache {
3671            Some(cache) => cache.clone_from(binding.cache),
3672            None => cache = Some(binding.cache.clone()),
3673        }
3674        self.cache.set(cache);
3675        self.cache_epoch.set(cache_epoch);
3676        self.force_remeasure.set(stale || binding.descendant_dirty);
3677        self.parent_data.set(binding.parent_data);
3678        if self.frame.measures() {
3679            self.measured.take();
3680            self.last_position.set(None);
3681            self.measured_constraints.set(None);
3682            self.read_intrinsics.set(false);
3683        }
3684        let layout_state = self.layout_state.take();
3685        let shared = layout_state
3686            .as_ref()
3687            .zip(binding.layout_state)
3688            .is_some_and(|(current, bound)| Rc::ptr_eq(current, bound));
3689        self.layout_state.set(if shared {
3690            layout_state
3691        } else {
3692            binding.layout_state.cloned()
3693        });
3694    }
3695
3696    fn place_retained(&self, position: Point) {
3697        self.last_position.set(Some(position));
3698        let builder = self.frame.builder.borrow();
3699        let Some(builder) = builder.as_ref() else {
3700            return;
3701        };
3702        let placed = read_cell(&self.layout_state, |layout_state| {
3703            layout_state
3704                .as_ref()
3705                .map(|layout_state| layout_state.borrow_mut().place(position))
3706                .is_some()
3707        });
3708        if placed {
3709            return;
3710        }
3711        let Ok(mut applier) = builder.applier.try_borrow_typed() else {
3712            return;
3713        };
3714        let _ = applier.with_node::<SubcomposeLayoutNode, _>(self.node_id, |node| {
3715            node.set_position(position);
3716        });
3717    }
3718
3719    /// The intrinsic size of a child that is a layout node, from its
3720    /// modifier chain and measure policy, without measuring it. `None` for
3721    /// another node type, which measures for it.
3722    fn layout_intrinsic(&self, kind: IntrinsicKind) -> Option<f32> {
3723        let builder = self.frame.builder.borrow();
3724        let builder = builder.as_ref()?;
3725        match builder.layout_node_intrinsic(self.node_id, kind) {
3726            Ok(value) => value,
3727            Err(err) => {
3728                self.frame.record_error(err);
3729                Some(0.0)
3730            }
3731        }
3732    }
3733
3734    fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
3735        let builder = self.frame.builder.borrow();
3736        let builder = builder.as_ref().ok_or(NodeError::MissingContext {
3737            id: self.node_id,
3738            reason: "layout child applier not configured",
3739        })?;
3740        builder.measure_node(self.node_id, constraints)
3741    }
3742
3743    fn measure_cached(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
3744        if self.frame.measures() {
3745            self.measured_constraints.set(Some(constraints));
3746        }
3747        let cache = read_cell(&self.cache, Clone::clone);
3748        if let Some(cache) = &cache {
3749            cache.activate(self.cache_epoch.get());
3750        }
3751        if !self.force_remeasure.get()
3752            && let Some(cached) = cache
3753                .as_ref()
3754                .and_then(|cache| cache.get_measurement(constraints))
3755        {
3756            // A measure clears the placed flag the parent's placement sets
3757            // again; a child it no longer places then leaves the scene.
3758            if self.frame.measures() {
3759                read_cell(&self.layout_state, |state| {
3760                    if let Some(state) = state {
3761                        state.borrow_mut().clear_placed();
3762                    }
3763                });
3764            }
3765            return Some(cached);
3766        }
3767
3768        match self.perform_measure(constraints) {
3769            Ok(measured) => {
3770                self.force_remeasure.set(false);
3771                if let Some(cache) = &cache {
3772                    cache.store_measurement(constraints, Rc::clone(&measured));
3773                }
3774                Some(measured)
3775            }
3776            Err(err) => {
3777                self.frame.record_error(err);
3778                None
3779            }
3780        }
3781    }
3782}
3783
3784struct LayoutChildMeasurable {
3785    state: Rc<LayoutChildMeasureState>,
3786}
3787
3788impl LayoutChildMeasurable {
3789    fn new(state: Rc<LayoutChildMeasureState>) -> Self {
3790        Self { state }
3791    }
3792
3793    fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
3794        if self.state.frame.builder.borrow().is_none() {
3795            return None;
3796        }
3797        read_cell(&self.state.parent_data, Clone::clone)
3798    }
3799
3800    fn intrinsic(
3801        &self,
3802        kind: IntrinsicKind,
3803        constraints: Constraints,
3804        extent: fn(Size) -> f32,
3805    ) -> f32 {
3806        let state = &self.state;
3807        if state.frame.measures() {
3808            state.read_intrinsics.set(true);
3809        }
3810        let cache = read_cell(&state.cache, Clone::clone);
3811        if let Some(cache) = &cache {
3812            cache.activate(state.cache_epoch.get());
3813        }
3814        if !state.force_remeasure.get()
3815            && let Some(value) = cache.as_ref().and_then(|cache| cache.get_intrinsic(&kind))
3816        {
3817            return value;
3818        }
3819        let value = match state.layout_intrinsic(kind) {
3820            Some(value) => value,
3821            None => state
3822                .measure_cached(constraints)
3823                .map_or(0.0, |node| extent(node.size_for_parent())),
3824        };
3825        if let Some(cache) = &cache {
3826            cache.store_intrinsic(kind, value);
3827        }
3828        value
3829    }
3830}
3831
3832impl PlaceTarget for LayoutChildMeasureState {
3833    fn place(&self, x: f32, y: f32) {
3834        let internal_offset = read_cell(&self.measured, |measured| {
3835            measured
3836                .as_ref()
3837                .map(|measured| measured.offset)
3838                .unwrap_or_default()
3839        });
3840        self.place_retained(Point {
3841            x: x + internal_offset.x,
3842            y: y + internal_offset.y,
3843        });
3844    }
3845}
3846
3847impl Measurable for LayoutChildMeasurable {
3848    fn measured_hold(&self) -> Option<(Size, cranpose_ui_layout::ConstraintsHold)> {
3849        read_cell(&self.state.measured, |measured| {
3850            let measured = measured.as_ref()?;
3851            let hold = read_cell(&self.state.cache, |cache| cache.as_ref()?.hold_of(measured))?;
3852            Some((measured.size_for_parent(), hold))
3853        })
3854    }
3855
3856    fn measure(&self, constraints: Constraints) -> Placeable {
3857        let state = &self.state;
3858        let measured = state.measure_cached(constraints);
3859        let (measured_size, size_for_parent) = measured.as_ref().map_or(
3860            (
3861                Size {
3862                    width: 0.0,
3863                    height: 0.0,
3864                },
3865                Size {
3866                    width: 0.0,
3867                    height: 0.0,
3868                },
3869            ),
3870            |measured| (measured.size, measured.size_for_parent()),
3871        );
3872        read_cell(&state.layout_state, |layout_state| {
3873            if let Some(layout_state) = layout_state {
3874                layout_state.borrow_mut().set_size(measured_size);
3875            }
3876        });
3877        let alignment_lines = measured
3878            .as_ref()
3879            .map_or_else(AlignmentLines::default, |node| {
3880                node.alignment_lines_for_parent()
3881            });
3882        state.measured.set(measured);
3883
3884        Placeable::with_place_target(
3885            size_for_parent.width,
3886            size_for_parent.height,
3887            state.node_id,
3888            Rc::clone(&self.state) as Rc<dyn PlaceTarget>,
3889        )
3890        .with_alignment_lines(alignment_lines)
3891    }
3892
3893    fn min_intrinsic_width(&self, height: f32) -> f32 {
3894        self.intrinsic(
3895            IntrinsicKind::MinWidth(height),
3896            Constraints {
3897                min_width: 0.0,
3898                max_width: f32::INFINITY,
3899                min_height: height,
3900                max_height: height,
3901            },
3902            |size| size.width,
3903        )
3904    }
3905
3906    fn max_intrinsic_width(&self, height: f32) -> f32 {
3907        self.intrinsic(
3908            IntrinsicKind::MaxWidth(height),
3909            Constraints {
3910                min_width: 0.0,
3911                max_width: f32::INFINITY,
3912                min_height: 0.0,
3913                max_height: height,
3914            },
3915            |size| size.width,
3916        )
3917    }
3918
3919    fn min_intrinsic_height(&self, width: f32) -> f32 {
3920        self.intrinsic(
3921            IntrinsicKind::MinHeight(width),
3922            Constraints {
3923                min_width: width,
3924                max_width: width,
3925                min_height: 0.0,
3926                max_height: f32::INFINITY,
3927            },
3928            |size| size.height,
3929        )
3930    }
3931
3932    fn max_intrinsic_height(&self, width: f32) -> f32 {
3933        self.intrinsic(
3934            IntrinsicKind::MaxHeight(width),
3935            Constraints {
3936                min_width: 0.0,
3937                max_width: width,
3938                min_height: 0.0,
3939                max_height: f32::INFINITY,
3940            },
3941            |size| size.height,
3942        )
3943    }
3944
3945    fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3946        let parent_data = self.resolved_parent_data()?;
3947        if !parent_data.has_weight() {
3948            return None;
3949        }
3950        Some(cranpose_ui_layout::FlexParentData::new(
3951            parent_data.weight,
3952            parent_data.fill,
3953        ))
3954    }
3955
3956    fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3957        self.resolved_parent_data().unwrap_or_default()
3958    }
3959}
3960
3961#[derive(Clone)]
3962struct RuntimeNodeMetadata {
3963    modifier: Modifier,
3964    resolved_modifiers: ResolvedModifiers,
3965    modifier_slices: Rc<ModifierNodeSlices>,
3966    semantics: Option<Rc<SemanticsConfiguration>>,
3967    role: SemanticsRole,
3968    button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3969}
3970
3971impl Default for RuntimeNodeMetadata {
3972    fn default() -> Self {
3973        Self {
3974            modifier: Modifier::empty(),
3975            resolved_modifiers: ResolvedModifiers::default(),
3976            modifier_slices: Rc::default(),
3977            semantics: None,
3978            role: SemanticsRole::Unknown,
3979            button_handler: None,
3980        }
3981    }
3982}
3983
3984fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3985    modifier_slices
3986        .annotated_text()
3987        .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3988            value: SemanticsText(Rc::clone(&text)),
3989        })
3990}
3991
3992fn runtime_metadata_for(
3993    applier: &mut MemoryApplier,
3994    node_id: NodeId,
3995) -> Result<RuntimeNodeMetadata, NodeError> {
3996    if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3997        let modifier = layout.modifier.clone();
3998        let resolved_modifiers = layout.resolved_modifiers();
3999        let modifier_slices = layout.modifier_slices_snapshot();
4000        let role = role_from_modifier_slices(&modifier_slices);
4001
4002        RuntimeNodeMetadata {
4003            modifier,
4004            resolved_modifiers,
4005            modifier_slices,
4006            semantics: layout.semantics_configuration().map(Rc::new),
4007            role,
4008            button_handler: None,
4009        }
4010    }) {
4011        return Ok(meta);
4012    }
4013
4014    if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
4015        applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
4016            (
4017                node.modifier(),
4018                node.resolved_modifiers(),
4019                node.modifier_slices_snapshot(),
4020                node.semantics_configuration().map(Rc::new),
4021            )
4022        })
4023    {
4024        return Ok(RuntimeNodeMetadata {
4025            modifier,
4026            resolved_modifiers,
4027            modifier_slices,
4028            semantics,
4029            role: SemanticsRole::Subcompose,
4030            button_handler: None,
4031        });
4032    }
4033    Ok(RuntimeNodeMetadata::default())
4034}
4035
4036/// Runs `visit` on the measurement `child` holds in its cache, or on a leaf
4037/// of no size for a child that holds none, as a node of another kind
4038/// measures.
4039fn visit_child<R>(
4040    applier: &mut MemoryApplier,
4041    child: &MeasuredChild,
4042    visit: impl FnOnce(&mut MemoryApplier, &MeasuredNode) -> R,
4043) -> R {
4044    let held = applier.get_mut(child.node_id).ok().and_then(|node| {
4045        let any = node.as_any_mut();
4046        if let Some(layout) = any.downcast_ref::<LayoutNode>() {
4047            return layout.cache_handles().held_measurement();
4048        }
4049        any.downcast_ref::<SubcomposeLayoutNode>()
4050            .and_then(|subcompose| subcompose.cache_handles().held_measurement())
4051    });
4052    match held {
4053        Some(held) => visit(applier, &held),
4054        None => visit(
4055            applier,
4056            &MeasuredNode::new(
4057                child.node_id,
4058                Size::default(),
4059                Point::default(),
4060                Point::default(),
4061                Vec::new(),
4062            ),
4063        ),
4064    }
4065}
4066
4067fn clear_semantics_dirty_flags(
4068    applier: &mut MemoryApplier,
4069    node: &MeasuredNode,
4070) -> Result<(), NodeError> {
4071    match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
4072        layout.clear_needs_semantics();
4073    }) {
4074        Ok(()) => {}
4075        Err(NodeError::Missing { .. }) => {}
4076        Err(NodeError::TypeMismatch { .. }) => {
4077            match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
4078                subcompose.clear_needs_semantics();
4079            }) {
4080                Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
4081                Err(err) => return Err(err),
4082            }
4083        }
4084        Err(err) => return Err(err),
4085    }
4086
4087    for child in &node.children {
4088        visit_child(applier, child, clear_semantics_dirty_flags)?;
4089    }
4090
4091    Ok(())
4092}
4093
4094fn build_semantics_tree_from_live_nodes(
4095    applier: &mut MemoryApplier,
4096    node: &MeasuredNode,
4097) -> Result<SemanticsTree, NodeError> {
4098    Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
4099        applier, node, None,
4100    )?))
4101}
4102
4103fn semantics_node_from_parts(
4104    node_id: NodeId,
4105    node_generation: u32,
4106    mut role: SemanticsRole,
4107    config: Option<SemanticsConfiguration>,
4108    children: Vec<SemanticsNode>,
4109    held_details: Option<Box<SemanticsDetails>>,
4110    bounds: GeometryRect,
4111) -> SemanticsNode {
4112    let focusable = crate::focus_dispatch::has_focus_target(node_id);
4113    let mut node = SemanticsNode {
4114        node_id,
4115        bounds,
4116        node_generation,
4117        children,
4118        focusable,
4119        focused: focusable && crate::focus_dispatch::active_focus_target() == Some(node_id),
4120        details: held_details,
4121        ..SemanticsNode::default()
4122    };
4123    let details = match config {
4124        Some(mut config) => {
4125            if config.role == Some(SemanticsWidgetRole::Button) {
4126                role = SemanticsRole::Button;
4127            }
4128            if config.is_activatable() {
4129                node.actions.push(SemanticsAction::Click {
4130                    handler: SemanticsCallback::new(node_id),
4131                });
4132            }
4133            let on_click_label = config.on_click_label.take().or_else(|| {
4134                config
4135                    .on_click
4136                    .as_ref()
4137                    .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
4138            });
4139            node.widget_role = config.role;
4140            node.description = config.content_description.take();
4141            node.on_click = config.on_click.take();
4142            node.selected = config.selected;
4143            node.toggled = config.toggled;
4144            node.enabled = config.enabled;
4145            node.hidden = config.hidden;
4146            node.merge_descendants = config.merge_descendants;
4147            node.traversal_index = config.traversal_index;
4148            node.text = config.text.take();
4149            let is_modal = config.is_modal
4150                && modal_takes_space(Size {
4151                    width: bounds.width,
4152                    height: bounds.height,
4153                });
4154            SemanticsDetails::from_configuration(config, on_click_label, is_modal)
4155        }
4156        None => SemanticsDetails::NONE,
4157    };
4158    node.set_details(details);
4159    node.role = role;
4160    node
4161}
4162
4163#[inline(never)]
4164fn build_semantics_node_from_live_nodes(
4165    applier: &mut MemoryApplier,
4166    node: &MeasuredNode,
4167    origin: Option<Point>,
4168) -> Result<SemanticsNode, NodeError> {
4169    let (role, config, (bounds, content), placement) =
4170        match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
4171            let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
4172            let config = layout.semantics_configuration();
4173            layout.clear_needs_semantics();
4174            let state = layout.layout_state();
4175            (
4176                role,
4177                config,
4178                semantics_placement(&state, origin),
4179                SemanticsPlacement::of(&state),
4180            )
4181        }) {
4182            Ok(data) => data,
4183            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
4184                match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
4185                    subcompose.clear_needs_semantics();
4186                    let state = subcompose.layout_state();
4187                    (
4188                        SemanticsRole::Subcompose,
4189                        subcompose.semantics_configuration(),
4190                        semantics_placement(&state, origin),
4191                        SemanticsPlacement::of(&state),
4192                    )
4193                }) {
4194                    Ok(data) => data,
4195                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
4196                        let top_left = origin.unwrap_or_default();
4197                        (
4198                            SemanticsRole::Unknown,
4199                            None,
4200                            (
4201                                GeometryRect::from_origin_size(top_left, node.size),
4202                                top_left,
4203                            ),
4204                            SemanticsPlacement::default(),
4205                        )
4206                    }
4207                    Err(err) => return Err(err),
4208                }
4209            }
4210            Err(err) => return Err(err),
4211        };
4212
4213    let mut children = Vec::with_capacity(node.children.len());
4214    for child in &node.children {
4215        children.push(visit_child(applier, child, |applier, child| {
4216            build_semantics_node_from_live_nodes(applier, child, Some(content))
4217        })?);
4218    }
4219
4220    Ok(SemanticsNode {
4221        placement,
4222        ..semantics_node_from_parts(
4223            node.node_id,
4224            applier.node_generation(node.node_id),
4225            role,
4226            config,
4227            children,
4228            None,
4229            bounds,
4230        )
4231    })
4232}
4233
4234fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
4235    stats.semantics_node_count += 1;
4236    stats.semantics_action_count += node.actions.len();
4237    stats.semantics_action_capacity += node.actions.capacity();
4238    stats.semantics_child_count += node.children.len();
4239    stats.semantics_child_capacity += node.children.capacity();
4240    stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
4241    stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
4242    if node.details.is_some() {
4243        stats.semantics_heap_bytes += size_of::<SemanticsDetails>();
4244    }
4245
4246    if let Some(description) = &node.description {
4247        stats.semantics_description_count += 1;
4248        stats.semantics_description_bytes += description.capacity();
4249        stats.semantics_heap_bytes += description.capacity();
4250    }
4251
4252    for child in &node.children {
4253        record_semantics_allocation_stats(child, stats);
4254    }
4255}
4256
4257fn record_layout_box_allocation_stats(
4258    layout_box: &LayoutBox,
4259    stats: &mut LayoutAllocationDebugStats,
4260) {
4261    stats.layout_box_count += 1;
4262    stats.layout_box_child_count += layout_box.children.len();
4263    stats.layout_box_child_capacity += layout_box.children.capacity();
4264    stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
4265    stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
4266
4267    for child in &layout_box.children {
4268        record_layout_box_allocation_stats(child, stats);
4269    }
4270}
4271
4272fn build_layout_tree(
4273    applier: &mut MemoryApplier,
4274    node: &MeasuredNode,
4275) -> Result<LayoutTree, NodeError> {
4276    fn place(
4277        applier: &mut MemoryApplier,
4278        node: &MeasuredNode,
4279        origin: Point,
4280        parent_transform: ProjectiveTransform,
4281    ) -> Result<LayoutBox, NodeError> {
4282        let top_left = Point {
4283            x: origin.x + node.offset.x,
4284            y: origin.y + node.offset.y,
4285        };
4286        let rect = GeometryRect {
4287            x: top_left.x,
4288            y: top_left.y,
4289            width: node.size.width,
4290            height: node.size.height,
4291        };
4292        let (data, window_transform) =
4293            snapshot_node_data(applier, node.node_id, top_left, node.size, parent_transform)?;
4294        let mut children = Vec::with_capacity(node.children.len());
4295        for child in &node.children {
4296            if crate::modifier::is_window_root(applier, child.node_id) {
4297                continue;
4298            }
4299            let child_origin = Point {
4300                x: top_left.x + child.offset.x,
4301                y: top_left.y + child.offset.y,
4302            };
4303            children.push(visit_child(applier, child, |applier, child| {
4304                place(applier, child, child_origin, window_transform)
4305            })?);
4306        }
4307        Ok(LayoutBox {
4308            node_generation: applier.node_generation(node.node_id),
4309            ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
4310        })
4311    }
4312
4313    Ok(LayoutTree::new(place(
4314        applier,
4315        node,
4316        Point { x: 0.0, y: 0.0 },
4317        ProjectiveTransform::identity(),
4318    )?))
4319}
4320
4321fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
4322    match &layout_box.node_data.kind {
4323        LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
4324        LayoutNodeKind::Spacer => SemanticsRole::Spacer,
4325        LayoutNodeKind::Unknown => SemanticsRole::Unknown,
4326        LayoutNodeKind::Button { .. } => SemanticsRole::Button,
4327        LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
4328    }
4329}
4330
4331#[inline(never)]
4332fn build_semantics_node_from_layout_box(layout_box: &LayoutBox, origin: Point) -> SemanticsNode {
4333    let rect = layout_box.rect;
4334    let content = Point {
4335        x: rect.x + layout_box.content_offset.x,
4336        y: rect.y + layout_box.content_offset.y,
4337    };
4338    let children = layout_box
4339        .children
4340        .iter()
4341        .map(|child| build_semantics_node_from_layout_box(child, content))
4342        .collect();
4343
4344    SemanticsNode {
4345        placement: SemanticsPlacement {
4346            position: Point {
4347                x: rect.x - origin.x,
4348                y: rect.y - origin.y,
4349            },
4350            content_offset: layout_box.content_offset,
4351        },
4352        ..semantics_node_from_parts(
4353            layout_box.node_id,
4354            layout_box.node_generation,
4355            semantics_role_from_layout_box(layout_box),
4356            layout_box.node_data.semantics().cloned(),
4357            children,
4358            None,
4359            rect,
4360        )
4361    }
4362}
4363
4364fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
4365    match &info.role {
4366        SemanticsRole::Layout => LayoutNodeKind::Layout,
4367        SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
4368        SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
4369        SemanticsRole::Spacer => LayoutNodeKind::Spacer,
4370        SemanticsRole::Button => {
4371            let handler = info
4372                .button_handler
4373                .as_ref()
4374                .cloned()
4375                .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
4376            LayoutNodeKind::Button { on_click: handler }
4377        }
4378        SemanticsRole::Unknown => LayoutNodeKind::Unknown,
4379    }
4380}
4381
4382fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
4383    let horizontal = padding.horizontal_sum();
4384    let vertical = padding.vertical_sum();
4385    let min_width = (constraints.min_width - horizontal).max(0.0);
4386    let mut max_width = constraints.max_width;
4387    if max_width.is_finite() {
4388        max_width = (max_width - horizontal).max(0.0);
4389    }
4390    let min_height = (constraints.min_height - vertical).max(0.0);
4391    let mut max_height = constraints.max_height;
4392    if max_height.is_finite() {
4393        max_height = (max_height - vertical).max(0.0);
4394    }
4395    normalize_constraints(Constraints {
4396        min_width,
4397        max_width,
4398        min_height,
4399        max_height,
4400    })
4401}
4402
4403fn resolve_dimension(
4404    base: f32,
4405    explicit: DimensionConstraint,
4406    min_override: Option<f32>,
4407    max_override: Option<f32>,
4408    min_limit: f32,
4409    max_limit: f32,
4410) -> f32 {
4411    let mut min_bound = min_limit;
4412    if let Some(min_value) = min_override {
4413        min_bound = min_bound.max(min_value);
4414    }
4415
4416    let mut max_bound = if max_limit.is_finite() {
4417        max_limit
4418    } else {
4419        max_override.unwrap_or(max_limit)
4420    };
4421    if let Some(max_value) = max_override {
4422        if max_bound.is_finite() {
4423            max_bound = max_bound.min(max_value);
4424        } else {
4425            max_bound = max_value;
4426        }
4427    }
4428    if max_bound < min_bound {
4429        max_bound = min_bound;
4430    }
4431
4432    let mut size = match explicit {
4433        DimensionConstraint::Points(points) => points,
4434        DimensionConstraint::Fraction(fraction) => {
4435            if max_limit.is_finite() {
4436                max_limit * fraction.clamp(0.0, 1.0)
4437            } else {
4438                base
4439            }
4440        }
4441        DimensionConstraint::Unspecified => base,
4442        DimensionConstraint::Intrinsic(_) => base,
4443    };
4444
4445    size = clamp_dimension(size, min_bound, max_bound);
4446    size = clamp_dimension(size, min_limit, max_limit);
4447    size.max(0.0)
4448}
4449
4450fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
4451    let mut result = value.max(min);
4452    if max.is_finite() {
4453        result = result.min(max);
4454    }
4455    result
4456}
4457
4458fn normalize_constraints(mut constraints: Constraints) -> Constraints {
4459    if constraints.max_width < constraints.min_width {
4460        constraints.max_width = constraints.min_width;
4461    }
4462    if constraints.max_height < constraints.min_height {
4463        constraints.max_height = constraints.min_height;
4464    }
4465    constraints
4466}
4467
4468#[cfg(test)]
4469#[path = "tests/layout_tests.rs"]
4470mod tests;
4471
4472#[cfg(test)]
4473#[path = "tests/semantics_update_tests.rs"]
4474mod semantics_update_tests;
4475
4476#[cfg(test)]
4477#[path = "tests/coordinator_geometry_tests.rs"]
4478mod coordinator_geometry_tests;