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