Skip to main content

cranpose_ui/layout/
mod.rs

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