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, 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/// The modal the semantics tree of `root` would be rooted at, found without
1129/// building the tree: the topmost placed, visible modal that takes space,
1130/// or `None` when no modal is open. Unlike a tree build, it leaves the
1131/// nodes' semantics dirty flags alone.
1132pub fn top_modal_from_applier(
1133    applier: &mut MemoryApplier,
1134    root: NodeId,
1135) -> Result<Option<NodeId>, NodeError> {
1136    // One depth-first walk on a shared stack, last child first, with a
1137    // modal's own step below its children's: the first modal step popped is
1138    // the modal drawn on top. It runs every frame, so no node's children are
1139    // copied and each node is looked up once.
1140    enum Step {
1141        Enter { node: NodeId, child: bool },
1142        Modal(NodeId),
1143    }
1144
1145    fn push_placed(
1146        steps: &mut Vec<Step>,
1147        node: NodeId,
1148        reach: cranpose_foundation::SemanticsReach,
1149        size: Size,
1150        children: impl IntoIterator<Item = NodeId>,
1151    ) {
1152        if reach.hidden {
1153            return;
1154        }
1155        if reach.is_modal && modal_takes_space(size) {
1156            steps.push(Step::Modal(node));
1157        }
1158        steps.extend(
1159            children
1160                .into_iter()
1161                .map(|node| Step::Enter { node, child: true }),
1162        );
1163    }
1164
1165    let mut steps = vec![Step::Enter {
1166        node: root,
1167        child: false,
1168    }];
1169    while let Some(step) = steps.pop() {
1170        let (node_id, child) = match step {
1171            Step::Modal(node) => return Ok(Some(node)),
1172            Step::Enter { node, child } => (node, child),
1173        };
1174        let node = match applier.get_mut(node_id) {
1175            Ok(node) => node.as_any_mut(),
1176            Err(NodeError::Missing { .. }) => continue,
1177            Err(error) => return Err(error),
1178        };
1179        if let Some(layout) = node.downcast_mut::<LayoutNode>() {
1180            let state = layout.layout_state();
1181            if state.is_placed() && !(child && layout.is_window_root()) {
1182                let children = layout.children.iter().copied();
1183                push_placed(
1184                    &mut steps,
1185                    node_id,
1186                    layout.semantics_reach(),
1187                    state.size(),
1188                    children,
1189                );
1190            }
1191        } else if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
1192            let state = subcompose.layout_state();
1193            if state.is_placed() {
1194                let reach = subcompose.semantics_reach();
1195                subcompose.with_active_children(|children| {
1196                    push_placed(
1197                        &mut steps,
1198                        node_id,
1199                        reach,
1200                        state.size(),
1201                        children.iter().copied(),
1202                    );
1203                });
1204            }
1205        }
1206    }
1207    Ok(None)
1208}
1209
1210#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1211pub struct MeasureLayoutOptions {
1212    pub collect_semantics: bool,
1213    pub build_layout_tree: bool,
1214}
1215
1216impl Default for MeasureLayoutOptions {
1217    fn default() -> Self {
1218        Self {
1219            collect_semantics: true,
1220            build_layout_tree: true,
1221        }
1222    }
1223}
1224
1225/// Check if a node or any of its descendants needs measure (selective measure optimization).
1226/// This can be used by the app shell to skip layout when the tree is clean.
1227///
1228/// O(1) check - just looks at root's dirty flag.
1229/// Works because all mutation paths bubble dirty flags to root via composer commands.
1230///
1231/// Returns Result to force caller to handle errors explicitly. No more unwrap_or(true) safety net.
1232pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1233    Ok(applier.get_mut(root)?.needs_layout())
1234}
1235
1236fn publish_window_geometry(
1237    modifier_slices: &crate::modifier::ModifierNodeSlices,
1238    top_left: Point,
1239    layer_translation: Point,
1240    size: Size,
1241) {
1242    let origin = Point {
1243        x: top_left.x + layer_translation.x,
1244        y: top_left.y + layer_translation.y,
1245    };
1246    if let Some(sink) = modifier_slices.text_window_origin() {
1247        sink.set(origin);
1248    }
1249    if let Some(sink) = modifier_slices.viewport_window_rect() {
1250        sink.set(GeometryRect {
1251            x: origin.x,
1252            y: origin.y,
1253            width: size.width,
1254            height: size.height,
1255        });
1256    }
1257    modifier_slices.publish_pointer_input_size(size);
1258}
1259
1260/// Check if the root semantics snapshot is dirty.
1261///
1262/// Semantics invalidations bubble to the root the same way layout invalidations do,
1263/// so a root check is sufficient to determine whether the next layout pass needs to
1264/// rebuild semantic data even when geometry is otherwise unchanged.
1265pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1266    Ok(applier.get_mut(root)?.needs_semantics())
1267}
1268
1269#[cfg(test)]
1270pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
1271    cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1272}
1273
1274/// Runs the measure phase for the subtree rooted at `root`.
1275pub fn measure_layout(
1276    applier: &mut MemoryApplier,
1277    root: NodeId,
1278    max_size: Size,
1279) -> Result<LayoutMeasurements, NodeError> {
1280    measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
1281}
1282
1283pub fn measure_layout_with_options(
1284    applier: &mut MemoryApplier,
1285    root: NodeId,
1286    max_size: Size,
1287    options: MeasureLayoutOptions,
1288) -> Result<LayoutMeasurements, NodeError> {
1289    let telemetry_start = Instant::now();
1290    process_pending_layout_repasses(applier, root)?;
1291    let after_repasses = Instant::now();
1292
1293    let constraints = Constraints {
1294        min_width: 0.0,
1295        max_width: max_size.width,
1296        min_height: 0.0,
1297        max_height: max_size.height,
1298    };
1299
1300    let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
1301        .with_node::<LayoutNode, _>(root, |node| {
1302            (
1303                node.needs_measure(),
1304                node.needs_semantics(),
1305                node.cache_handles().epoch(),
1306            )
1307        }) {
1308        Ok(tuple) => tuple,
1309        Err(NodeError::TypeMismatch { .. }) => {
1310            let node = applier.get_mut(root)?;
1311            let measure_dirty = node.needs_measure();
1312            let semantics_dirty = node.needs_semantics();
1313            (measure_dirty, semantics_dirty, 0)
1314        }
1315        Err(err) => return Err(err),
1316    };
1317
1318    let epoch = if needs_remeasure {
1319        crate::render_state::next_layout_cache_epoch()
1320    } else if cached_epoch != 0 {
1321        cached_epoch
1322    } else {
1323        crate::render_state::current_layout_cache_epoch()
1324    };
1325
1326    let guard = ApplierSlotGuard::new(applier);
1327    let applier_host = guard.host();
1328    let slots_handle = guard.slots_handle();
1329    let after_guard = Instant::now();
1330
1331    let frame_arena = crate::render_state::take_layout_frame_arena();
1332    let mut builder = LayoutBuilder::new_with_epoch(
1333        Rc::clone(&applier_host),
1334        epoch,
1335        Rc::clone(&slots_handle),
1336        frame_arena,
1337    );
1338    let after_builder = Instant::now();
1339
1340    let measured = builder.measure_node(root, normalize_constraints(constraints))?;
1341    let after_measure = Instant::now();
1342
1343    if let Ok(mut applier) = applier_host.try_borrow_typed()
1344        && applier
1345            .with_node::<LayoutNode, _>(root, |node| {
1346                node.set_position(Point::default());
1347            })
1348            .is_err()
1349    {
1350        let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
1351            node.set_position(Point::default());
1352        });
1353    }
1354    let after_root_place = Instant::now();
1355
1356    let (layout_tree, semantics) = {
1357        let mut applier_ref = applier_host.borrow_typed();
1358        let layout_tree = if options.build_layout_tree {
1359            Some(build_layout_tree(&mut applier_ref, &measured)?)
1360        } else {
1361            None
1362        };
1363        let semantics = if options.collect_semantics {
1364            let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
1365                clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
1366                build_semantics_tree_from_layout_tree(layout_tree)
1367            } else {
1368                build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
1369            };
1370            Some(semantics_tree)
1371        } else {
1372            None
1373        };
1374        (layout_tree, semantics)
1375    };
1376    let after_aux = Instant::now();
1377
1378    drop(builder);
1379    let after_builder_drop = Instant::now();
1380
1381    drop(guard);
1382    let after_guard_drop = Instant::now();
1383
1384    log_layout_measure_telemetry(LayoutMeasureTelemetry {
1385        root,
1386        start: telemetry_start,
1387        after_repasses,
1388        after_guard,
1389        after_builder,
1390        after_measure,
1391        after_root_place,
1392        after_aux,
1393        after_builder_drop,
1394        after_guard_drop,
1395    });
1396
1397    Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
1398}
1399
1400fn process_pending_layout_repasses(
1401    applier: &mut MemoryApplier,
1402    root: NodeId,
1403) -> Result<(), NodeError> {
1404    for node_id in crate::render_state::take_modifier_slice_repass_nodes() {
1405        if let Ok(node) = applier.get_mut(node_id) {
1406            let any = node.as_any_mut();
1407            if let Some(layout) = any.downcast_mut::<crate::widgets::nodes::LayoutNode>() {
1408                layout.mark_modifier_slices_dirty();
1409            } else if let Some(subcompose) =
1410                any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1411            {
1412                subcompose.mark_modifier_slices_dirty();
1413            }
1414        }
1415    }
1416    let measure_repass_nodes = crate::take_measure_repass_nodes();
1417    let repass_nodes = crate::take_layout_repass_nodes();
1418    if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
1419        return Ok(());
1420    }
1421    for node_id in measure_repass_nodes {
1422        cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
1423    }
1424    for node_id in repass_nodes {
1425        cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1426    }
1427    applier.get_mut(root)?.mark_needs_layout();
1428    Ok(())
1429}
1430
1431struct LayoutBuilder {
1432    state: Rc<RefCell<LayoutBuilderState>>,
1433}
1434
1435impl LayoutBuilder {
1436    fn new_with_epoch(
1437        applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1438        epoch: u64,
1439        slots: Rc<RefCell<SlotTable>>,
1440        frame_arena: FrameLayoutArena,
1441    ) -> Self {
1442        Self {
1443            state: Rc::new(RefCell::new(LayoutBuilderState::new_with_epoch(
1444                applier,
1445                epoch,
1446                slots,
1447                frame_arena,
1448            ))),
1449        }
1450    }
1451
1452    fn measure_node(
1453        &mut self,
1454        node_id: NodeId,
1455        constraints: Constraints,
1456    ) -> Result<Rc<MeasuredNode>, NodeError> {
1457        LayoutBuilderState::measure_node(Rc::clone(&self.state), node_id, constraints)
1458    }
1459
1460    fn set_runtime_handle(&mut self, handle: Option<RuntimeHandle>) {
1461        self.state.borrow_mut().runtime_handle = handle;
1462    }
1463}
1464
1465impl Drop for LayoutBuilder {
1466    fn drop(&mut self) {
1467        if Rc::strong_count(&self.state) != 1 {
1468            return;
1469        }
1470        let Ok(mut state) = self.state.try_borrow_mut() else {
1471            return;
1472        };
1473        crate::render_state::replace_layout_frame_arena(std::mem::take(&mut state.frame_arena));
1474    }
1475}
1476
1477struct LayoutBuilderState {
1478    applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1479    runtime_handle: Option<RuntimeHandle>,
1480    slots: Rc<RefCell<SlotTable>>,
1481    cache_epoch: u64,
1482    cache_floor: u64,
1483    frame_arena: FrameLayoutArena,
1484}
1485
1486struct LayoutRuntimeFrameBindingCleanup {
1487    state: Rc<RefCell<LayoutRuntimeState>>,
1488}
1489
1490impl LayoutRuntimeFrameBindingCleanup {
1491    fn new(state: Rc<RefCell<LayoutRuntimeState>>) -> Self {
1492        Self { state }
1493    }
1494}
1495
1496impl Drop for LayoutRuntimeFrameBindingCleanup {
1497    fn drop(&mut self) {
1498        self.state.borrow().clear_frame_bindings();
1499    }
1500}
1501
1502impl LayoutBuilderState {
1503    fn new_with_epoch(
1504        applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1505        epoch: u64,
1506        slots: Rc<RefCell<SlotTable>>,
1507        frame_arena: FrameLayoutArena,
1508    ) -> Self {
1509        let runtime_handle = applier.borrow_typed().runtime_handle();
1510
1511        Self {
1512            applier,
1513            runtime_handle,
1514            slots,
1515            cache_epoch: epoch,
1516            cache_floor: crate::render_state::layout_cache_floor(),
1517            frame_arena,
1518        }
1519    }
1520
1521    fn try_with_applier_result<R>(
1522        state_rc: &Rc<RefCell<Self>>,
1523        f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1524    ) -> Option<Result<R, NodeError>> {
1525        let host = {
1526            let state = state_rc.borrow();
1527            Rc::clone(&state.applier)
1528        };
1529
1530        let Ok(mut applier) = host.try_borrow_typed() else {
1531            return None;
1532        };
1533
1534        Some(f(&mut applier))
1535    }
1536
1537    fn with_applier_result<R>(
1538        state_rc: &Rc<RefCell<Self>>,
1539        f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1540    ) -> Result<R, NodeError> {
1541        Self::try_with_applier_result(state_rc, f).unwrap_or_else(|| {
1542            Err(NodeError::MissingContext {
1543                id: NodeId::default(),
1544                reason: "applier already borrowed",
1545            })
1546        })
1547    }
1548
1549    fn clear_node_placed(state_rc: &Rc<RefCell<Self>>, node_id: NodeId) {
1550        let host = {
1551            let state = state_rc.borrow();
1552            Rc::clone(&state.applier)
1553        };
1554        let Ok(mut applier) = host.try_borrow_typed() else {
1555            return;
1556        };
1557        if applier
1558            .with_node::<LayoutNode, _>(node_id, |node| {
1559                node.clear_placed();
1560            })
1561            .is_err()
1562        {
1563            let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1564                node.clear_placed();
1565            });
1566        }
1567    }
1568
1569    fn measure_node(
1570        state_rc: Rc<RefCell<Self>>,
1571        node_id: NodeId,
1572        constraints: Constraints,
1573    ) -> Result<Rc<MeasuredNode>, NodeError> {
1574        let telemetry_start = Instant::now();
1575        Self::clear_node_placed(&state_rc, node_id);
1576
1577        if let Some(subcompose) =
1578            Self::try_measure_subcompose(Rc::clone(&state_rc), node_id, constraints)?
1579        {
1580            log_node_measure_telemetry(
1581                "subcompose",
1582                node_id,
1583                constraints,
1584                subcompose.size,
1585                subcompose.children.len(),
1586                telemetry_start,
1587            );
1588            return Ok(subcompose);
1589        }
1590
1591        if let Some(result) = Self::try_with_applier_result(&state_rc, |applier| {
1592            match applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1593                LayoutNodeSnapshot::from_layout_node(layout_node)
1594            }) {
1595                Ok(snapshot) => Ok(Some(snapshot)),
1596                Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1597                Err(err) => Err(err),
1598            }
1599        }) && let Some(snapshot) = result?
1600        {
1601            let measured =
1602                Self::measure_layout_node(Rc::clone(&state_rc), node_id, snapshot, constraints)?;
1603            log_node_measure_telemetry(
1604                "layout",
1605                node_id,
1606                constraints,
1607                measured.size,
1608                measured.children.len(),
1609                telemetry_start,
1610            );
1611            return Ok(measured);
1612        }
1613
1614        let measured = Rc::new(MeasuredNode::new(
1615            node_id,
1616            Size::default(),
1617            Point { x: 0.0, y: 0.0 },
1618            Point::default(),
1619            Vec::new(),
1620        ));
1621        log_node_measure_telemetry(
1622            "fallback",
1623            node_id,
1624            constraints,
1625            measured.size,
1626            measured.children.len(),
1627            telemetry_start,
1628        );
1629        Ok(measured)
1630    }
1631
1632    fn cached_measure_node_with_applier(
1633        applier: &mut MemoryApplier,
1634        node_id: NodeId,
1635        constraints: Constraints,
1636    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1637        let Some(data) = Self::layout_child_measure_data(applier, node_id)? else {
1638            return Ok(None);
1639        };
1640        if data.needs_measure
1641            || data.needs_layout
1642            || data.cache.epoch() == 0
1643            || data.cache.epoch() != crate::render_state::current_layout_cache_epoch()
1644        {
1645            return Ok(None);
1646        }
1647
1648        let Some(measured) = data.cache.get_measurement(constraints) else {
1649            return Ok(None);
1650        };
1651
1652        if let Some(layout_state) = data.layout_state {
1653            let mut layout_state = layout_state.borrow_mut();
1654            layout_state.set_size(measured.size);
1655        } else {
1656            let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1657                node.set_measured_size(measured.size);
1658            });
1659        }
1660
1661        Ok(Some(measured))
1662    }
1663
1664    fn try_measure_subcompose(
1665        state_rc: Rc<RefCell<Self>>,
1666        node_id: NodeId,
1667        constraints: Constraints,
1668    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1669        let applier_host = {
1670            let state = state_rc.borrow();
1671            Rc::clone(&state.applier)
1672        };
1673
1674        let (node_handle, resolved_modifiers) = {
1675            let Ok(mut applier) = applier_host.try_borrow_typed() else {
1676                return Ok(None);
1677            };
1678            let node = match applier.get_mut(node_id) {
1679                Ok(node) => node,
1680                Err(NodeError::Missing { .. }) => return Ok(None),
1681                Err(err) => return Err(err),
1682            };
1683            let any = node.as_any_mut();
1684            if let Some(subcompose) =
1685                any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1686            {
1687                let handle = subcompose.handle();
1688                let resolved_modifiers = handle
1689                    .resolved_modifiers()
1690                    .on_device_grid(subcompose.density().density());
1691                (handle, resolved_modifiers)
1692            } else {
1693                return Ok(None);
1694            }
1695        };
1696
1697        let runtime_handle = {
1698            let mut state = state_rc.borrow_mut();
1699            if state.runtime_handle.is_none()
1700                && let Ok(applier) = applier_host.try_borrow_typed()
1701            {
1702                state.runtime_handle = applier.runtime_handle();
1703            }
1704            state
1705                .runtime_handle
1706                .clone()
1707                .ok_or(NodeError::MissingContext {
1708                    id: node_id,
1709                    reason: "runtime handle required for subcomposition",
1710                })?
1711        };
1712
1713        let props = resolved_modifiers.layout_properties();
1714        let padding = resolved_modifiers.padding();
1715        let offset = resolved_modifiers.offset();
1716        let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1717
1718        if let DimensionConstraint::Points(width) = props.width() {
1719            let constrained_width = width - padding.horizontal_sum();
1720            inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1721            inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1722        }
1723        if let DimensionConstraint::Points(height) = props.height() {
1724            let constrained_height = height - padding.vertical_sum();
1725            inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1726            inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1727        }
1728
1729        let mut slots_guard = SlotsGuard::take(Rc::clone(&state_rc));
1730        let slots_host = slots_guard.host();
1731        let applier_host_dyn: Rc<dyn ApplierHost> = applier_host.clone();
1732        let observer = SnapshotStateObserver::new(|callback| callback());
1733        let composer = Composer::new(
1734            Rc::clone(&slots_host),
1735            applier_host_dyn,
1736            runtime_handle,
1737            observer,
1738            Some(node_id),
1739        );
1740        composer.enter_phase(Phase::Measure);
1741
1742        let state_rc_clone = Rc::clone(&state_rc);
1743        let measure_error = RefCell::new(None);
1744        let state_rc_for_subcompose = Rc::clone(&state_rc_clone);
1745        let error_for_subcompose = &measure_error;
1746        let measured_children = node_handle.measured_children_scratch();
1747        let measured_children_for_subcompose = Rc::clone(&measured_children);
1748        let state_rc_for_cached = Rc::clone(&state_rc_clone);
1749        let error_for_cached = &measure_error;
1750        let measured_children_for_cached = Rc::clone(&measured_children);
1751        let measured_children_for_lookup = Rc::clone(&measured_children);
1752        let measured_children_for_retained = Rc::clone(&measured_children);
1753
1754        let measure_result = node_handle.measure_with_cached_batch(
1755            &composer,
1756            node_id,
1757            inner_constraints,
1758            CachedBatchMeasureInputs {
1759                measurer: Box::new(
1760                    move |child_id: NodeId, child_constraints: Constraints| -> Size {
1761                        match Self::measure_node(
1762                            Rc::clone(&state_rc_for_subcompose),
1763                            child_id,
1764                            child_constraints,
1765                        ) {
1766                            Ok(measured) => {
1767                                measured_children_for_subcompose
1768                                    .borrow_mut()
1769                                    .insert(child_id, Rc::clone(&measured));
1770                                measured.size
1771                            }
1772                            Err(err) => {
1773                                let mut slot = error_for_subcompose.borrow_mut();
1774                                if slot.is_none() {
1775                                    *slot = Some(err);
1776                                }
1777                                Size::default()
1778                            }
1779                        }
1780                    },
1781                ),
1782                cached_measure_batch_registrar: Box::new(
1783                    move |child_ids: &[NodeId],
1784                          child_constraints: Constraints,
1785                          out: &mut Vec<Option<Size>>| {
1786                        out.clear();
1787                        out.resize(child_ids.len(), None);
1788
1789                        let applier_host = {
1790                            let state = state_rc_for_cached.borrow();
1791                            Rc::clone(&state.applier)
1792                        };
1793                        let Ok(mut applier) = applier_host.try_borrow_typed() else {
1794                            return;
1795                        };
1796
1797                        let mut measured_children = measured_children_for_cached.borrow_mut();
1798                        for (index, &child_id) in child_ids.iter().enumerate() {
1799                            match Self::cached_measure_node_with_applier(
1800                                &mut applier,
1801                                child_id,
1802                                child_constraints,
1803                            ) {
1804                                Ok(Some(measured)) => {
1805                                    out[index] = Some(measured.size);
1806                                    measured_children.insert(child_id, Rc::clone(&measured));
1807                                }
1808                                Ok(None) => {}
1809                                Err(err) => {
1810                                    let mut slot = error_for_cached.borrow_mut();
1811                                    if slot.is_none() {
1812                                        *slot = Some(err);
1813                                    }
1814                                    break;
1815                                }
1816                            }
1817                        }
1818                    },
1819                ),
1820                retained_measure_lookup: Box::new(move |child_id| {
1821                    measured_children_for_lookup
1822                        .borrow()
1823                        .get(&child_id)
1824                        .cloned()
1825                }),
1826                retained_measure_registrar: Box::new(move |measurements| {
1827                    let mut measured_children = measured_children_for_retained.borrow_mut();
1828                    for measured in measurements {
1829                        measured_children.insert(measured.node_id(), Rc::clone(measured));
1830                    }
1831                }),
1832                error: &measure_error,
1833            },
1834        )?;
1835        drop(composer);
1836        slots_guard.restore(slots_host.into_table()?);
1837
1838        if let Some(err) = measure_error.borrow_mut().take() {
1839            return Err(err);
1840        }
1841
1842        let cranpose_ui_layout::MeasureResult {
1843            size: measured_size,
1844            placements,
1845        } = measure_result;
1846
1847        let mut width = measured_size.width + padding.horizontal_sum();
1848        let mut height = measured_size.height + padding.vertical_sum();
1849
1850        width = resolve_dimension(
1851            width,
1852            props.width(),
1853            props.min_width(),
1854            props.max_width(),
1855            constraints.min_width,
1856            constraints.max_width,
1857        );
1858        height = resolve_dimension(
1859            height,
1860            props.height(),
1861            props.min_height(),
1862            props.max_height(),
1863            constraints.min_height,
1864            constraints.max_height,
1865        );
1866
1867        let mut children = Vec::with_capacity(placements.len());
1868        let mut measured_children_by_id = measured_children.borrow_mut();
1869
1870        if let Ok(mut applier) = applier_host.try_borrow_typed() {
1871            let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1872                parent_node.set_measured_size(Size { width, height });
1873                parent_node.clear_needs_measure();
1874                parent_node.clear_needs_layout();
1875            });
1876        }
1877
1878        for placement in &placements {
1879            let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1880                measured
1881            } else {
1882                Self::measure_node(Rc::clone(&state_rc), placement.node_id, inner_constraints)?
1883            };
1884            let policy_position = Point {
1885                x: padding.left + placement.x,
1886                y: padding.top + placement.y,
1887            };
1888            let retained_position = Point {
1889                x: policy_position.x + child.offset.x,
1890                y: policy_position.y + child.offset.y,
1891            };
1892
1893            if let Ok(mut applier) = applier_host.try_borrow_typed()
1894                && applier
1895                    .with_node::<LayoutNode, _>(placement.node_id, |node| {
1896                        node.set_position(retained_position);
1897                    })
1898                    .is_err()
1899            {
1900                let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1901                    node.set_position(retained_position);
1902                });
1903            }
1904
1905            children.push(MeasuredChild {
1906                node: child,
1907                offset: policy_position,
1908            });
1909        }
1910
1911        node_handle.set_active_children(children.iter().map(|c| c.node.node_id));
1912        node_handle.recycle_placement_scratch(placements);
1913
1914        Ok(Some(Rc::new(MeasuredNode::new(
1915            node_id,
1916            Size { width, height },
1917            offset,
1918            Point::default(),
1919            children,
1920        ))))
1921    }
1922    fn measure_through_modifier_chain(
1923        state_rc: &Rc<RefCell<Self>>,
1924        node_id: NodeId,
1925        runtime_state: &mut LayoutRuntimeState,
1926        measure_policy: &Rc<dyn MeasurePolicy>,
1927        constraints: Constraints,
1928        layout_node_data: &mut Vec<LayoutModifierNodeData>,
1929        placements: &mut Vec<Placement>,
1930    ) -> ModifierChainMeasurement {
1931        use cranpose_foundation::NodeCapabilities;
1932
1933        layout_node_data.clear();
1934        let mut offset = Point::default();
1935        let mut density = crate::density::Density::default();
1936        let mut window_root = false;
1937        let mut geometry = None;
1938
1939        {
1940            let state = state_rc.borrow();
1941            let mut applier = state.applier.borrow_typed();
1942
1943            let _ = applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1944                density = layout_node.density();
1945                window_root = layout_node.is_window_root();
1946                geometry = Some(layout_node.coordinator_geometry());
1947                let chain_handle = layout_node.modifier_chain();
1948
1949                if !chain_handle.has_layout_nodes() {
1950                    return;
1951                }
1952
1953                chain_handle.chain().for_each_forward_matching(
1954                    NodeCapabilities::LAYOUT,
1955                    |node_ref| {
1956                        if let Some(index) = node_ref.entry_index() {
1957                            if let Some(node_rc) = chain_handle.chain().get_node_rc(index) {
1958                                layout_node_data.push((index, Rc::clone(&node_rc)));
1959                            }
1960
1961                            node_ref.with_node(|node| {
1962                                if let Some(offset_node) =
1963                                    node.as_any()
1964                                        .downcast_ref::<crate::modifier_nodes::OffsetNode>()
1965                                {
1966                                    let delta = offset_node.device_offset(density.density());
1967                                    offset.x += delta.x;
1968                                    offset.y += delta.y;
1969                                }
1970                            });
1971                        }
1972                    },
1973                );
1974            });
1975        }
1976
1977        let scope = crate::density::DensityMeasureScope::new(density);
1978
1979        if layout_node_data.is_empty() {
1980            let final_size = measure_policy.measure_into(
1981                &scope,
1982                runtime_state.child_measurables(),
1983                constraints,
1984                placements,
1985            );
1986            if let Some(geometry) = geometry {
1987                geometry.replace([GeometryRect {
1988                    x: 0.0,
1989                    y: 0.0,
1990                    width: final_size.width,
1991                    height: final_size.height,
1992                }]);
1993            }
1994
1995            return ModifierChainMeasurement {
1996                size: final_size,
1997                content_offset: Point::default(),
1998                offset,
1999                window_root,
2000            };
2001        }
2002
2003        runtime_state.reconcile_coordinator_chain(layout_node_data.as_slice());
2004        let frame = CoordinatorFrame::new(
2005            measure_policy,
2006            &scope,
2007            runtime_state.child_measurables(),
2008            placements,
2009        );
2010
2011        let placeable = runtime_state
2012            .coordinator_chain()
2013            .measure_from(0, &frame, constraints);
2014        let final_size = Size {
2015            width: placeable.width(),
2016            height: placeable.height(),
2017        };
2018
2019        if let Some(geometry) = geometry {
2020            runtime_state
2021                .coordinator_chain()
2022                .write_geometry(&geometry, offset);
2023        }
2024
2025        let content_offset = placeable.content_offset();
2026        let all_placement_offset = Point {
2027            x: content_offset.0,
2028            y: content_offset.1,
2029        };
2030
2031        let content_offset = Point {
2032            x: all_placement_offset.x - offset.x,
2033            y: all_placement_offset.y - offset.y,
2034        };
2035
2036        let invalidations = frame.take_invalidations();
2037        if !invalidations.is_empty() {
2038            Self::with_applier_result(state_rc, |applier| {
2039                applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2040                    for kind in invalidations {
2041                        match kind {
2042                            InvalidationKind::Layout => layout_node.mark_needs_measure(),
2043                            InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2044                            InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2045                            InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2046                            InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2047                        }
2048                    }
2049                })
2050            })
2051            .ok();
2052        }
2053
2054        ModifierChainMeasurement {
2055            size: final_size,
2056            content_offset,
2057            offset,
2058            window_root,
2059        }
2060    }
2061
2062    fn layout_child_measure_data(
2063        applier: &mut MemoryApplier,
2064        child_id: NodeId,
2065    ) -> Result<Option<LayoutChildMeasureData>, NodeError> {
2066        match applier.with_node::<LayoutNode, _>(child_id, |n| LayoutChildMeasureData {
2067            cache: n.cache_handles(),
2068            layout_state: Some(n.layout_state_handle()),
2069            needs_layout: n.needs_layout(),
2070            needs_measure: n.needs_measure(),
2071        }) {
2072            Ok(data) => Ok(Some(data)),
2073            Err(NodeError::TypeMismatch { .. }) => {
2074                match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |n| {
2075                    LayoutChildMeasureData {
2076                        cache: n.cache_handles(),
2077                        layout_state: None,
2078                        needs_layout: n.needs_layout(),
2079                        needs_measure: n.needs_measure(),
2080                    }
2081                }) {
2082                    Ok(data) => Ok(Some(data)),
2083                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
2084                    Err(err) => Err(err),
2085                }
2086            }
2087            Err(NodeError::Missing { .. }) => Ok(None),
2088            Err(err) => Err(err),
2089        }
2090    }
2091
2092    fn measure_layout_node(
2093        state_rc: Rc<RefCell<Self>>,
2094        node_id: NodeId,
2095        snapshot: LayoutNodeSnapshot,
2096        constraints: Constraints,
2097    ) -> Result<Rc<MeasuredNode>, NodeError> {
2098        let (cache_epoch, cache_floor) = {
2099            let state = state_rc.borrow();
2100            (state.cache_epoch, state.cache_floor)
2101        };
2102        let LayoutNodeSnapshot {
2103            measure_policy,
2104            cache,
2105            layout_runtime_state,
2106            needs_layout,
2107            needs_measure,
2108        } = snapshot;
2109        cache.activate(cache_epoch);
2110
2111        if !needs_measure
2112            && !needs_layout
2113            && let Some(cached) = cache.get_measurement(constraints)
2114        {
2115            Self::with_applier_result(&state_rc, |applier| {
2116                applier.with_node::<LayoutNode, _>(node_id, |node| {
2117                    node.clear_needs_measure();
2118                    node.clear_needs_layout();
2119                })
2120            })
2121            .ok();
2122            return Ok(cached);
2123        }
2124
2125        let (runtime_handle, applier_host) = {
2126            let state = state_rc.borrow();
2127            (state.runtime_handle.clone(), Rc::clone(&state.applier))
2128        };
2129
2130        let measure_handle = LayoutMeasureHandle::new(Rc::clone(&state_rc));
2131        let error = Rc::new(RefCell::new(None));
2132        let mut pools = VecPools::acquire(Rc::clone(&state_rc));
2133        let (records, child_ids, layout_node_data, placements) = pools.parts();
2134
2135        applier_host
2136            .borrow_typed()
2137            .with_node::<LayoutNode, _>(node_id, |node| {
2138                child_ids.extend_from_slice(&node.children);
2139            })?;
2140
2141        let mut valid_child_count = 0;
2142        for index in 0..child_ids.len() {
2143            let child_id = child_ids[index];
2144            let child_exists = {
2145                let mut applier = applier_host.borrow_typed();
2146                Self::layout_child_measure_data(&mut applier, child_id)?.is_some()
2147            };
2148            if child_exists {
2149                child_ids[valid_child_count] = child_id;
2150                valid_child_count += 1;
2151            }
2152        }
2153        child_ids.truncate(valid_child_count);
2154
2155        let _frame_binding_cleanup =
2156            LayoutRuntimeFrameBindingCleanup::new(Rc::clone(&layout_runtime_state));
2157
2158        {
2159            let mut runtime_state = layout_runtime_state.borrow_mut();
2160            runtime_state.reconcile_child_measurables(child_ids.as_slice());
2161
2162            for (index, &child_id) in child_ids.iter().enumerate() {
2163                let data = {
2164                    let mut applier = applier_host.borrow_typed();
2165                    Self::layout_child_measure_data(&mut applier, child_id)?
2166                };
2167                let Some(data) = data else {
2168                    continue;
2169                };
2170
2171                let child_is_stale = data.is_stale(cache_floor);
2172                let child_cache_epoch = if child_is_stale {
2173                    cache_epoch
2174                } else {
2175                    data.cache.epoch()
2176                };
2177                let child_state = runtime_state.child_state(index);
2178                child_state.configure(LayoutChildMeasureConfig {
2179                    applier: Rc::clone(&applier_host),
2180                    node_id: child_id,
2181                    error: Rc::clone(&error),
2182                    runtime_handle: runtime_handle.clone(),
2183                    cache: data.cache,
2184                    cache_epoch: child_cache_epoch,
2185                    force_remeasure: child_is_stale,
2186                    measure_handle: Some(measure_handle.clone()),
2187                    layout_state: data.layout_state,
2188                });
2189                records.push((child_id, ChildRecord { state: child_state }));
2190            }
2191        }
2192
2193        let chain_constraints = constraints;
2194
2195        let modifier_chain_result = {
2196            let mut runtime_state = layout_runtime_state.borrow_mut();
2197            Self::measure_through_modifier_chain(
2198                &state_rc,
2199                node_id,
2200                &mut runtime_state,
2201                &measure_policy,
2202                chain_constraints,
2203                layout_node_data,
2204                placements,
2205            )
2206        };
2207
2208        let (width, height, content_offset, offset, window_root) = {
2209            let result = modifier_chain_result;
2210            if let Some(err) = error.borrow_mut().take() {
2211                return Err(err);
2212            }
2213
2214            (
2215                result.size.width,
2216                result.size.height,
2217                result.content_offset,
2218                result.offset,
2219                result.window_root,
2220            )
2221        };
2222
2223        let mut measured_children = Vec::with_capacity(records.len());
2224        for (child_id, record) in records.iter() {
2225            if let Some(measured) = record.state.take_measured() {
2226                let placed = placements
2227                    .iter()
2228                    .find(|placement| placement.node_id == *child_id)
2229                    .map(|placement| Point {
2230                        x: placement.x,
2231                        y: placement.y,
2232                    });
2233                if let Some(raw) = placed {
2234                    record.state.place_retained(Point {
2235                        x: raw.x + measured.offset.x,
2236                        y: raw.y + measured.offset.y,
2237                    });
2238                }
2239                let base_position = placed
2240                    .or_else(|| record.state.last_position())
2241                    .unwrap_or(Point { x: 0.0, y: 0.0 });
2242                let position = Point {
2243                    x: content_offset.x + base_position.x,
2244                    y: content_offset.y + base_position.y,
2245                };
2246                measured_children.push(MeasuredChild {
2247                    node: measured,
2248                    offset: position,
2249                });
2250            }
2251        }
2252
2253        let measured = Rc::new(
2254            MeasuredNode::new(
2255                node_id,
2256                Size { width, height },
2257                offset,
2258                content_offset,
2259                measured_children,
2260            )
2261            .with_window_root(window_root),
2262        );
2263
2264        cache.store_measurement(constraints, Rc::clone(&measured));
2265
2266        Self::with_applier_result(&state_rc, |applier| {
2267            applier.with_node::<LayoutNode, _>(node_id, |node| {
2268                node.clear_needs_measure();
2269                node.clear_needs_layout();
2270                node.set_measured_size(Size { width, height });
2271                node.set_content_offset(content_offset);
2272            })
2273        })
2274        .ok();
2275
2276        Ok(measured)
2277    }
2278}
2279
2280struct LayoutChildMeasureData {
2281    cache: LayoutNodeCacheHandles,
2282    layout_state: Option<Rc<RefCell<LayoutState>>>,
2283    needs_layout: bool,
2284    needs_measure: bool,
2285}
2286
2287impl LayoutChildMeasureData {
2288    fn is_stale(&self, cache_floor: u64) -> bool {
2289        self.needs_layout || self.needs_measure || self.cache.epoch() < cache_floor
2290    }
2291}
2292
2293struct LayoutNodeSnapshot {
2294    measure_policy: Rc<dyn MeasurePolicy>,
2295    cache: LayoutNodeCacheHandles,
2296    layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
2297    needs_layout: bool,
2298    needs_measure: bool,
2299}
2300
2301impl LayoutNodeSnapshot {
2302    fn from_layout_node(node: &LayoutNode) -> Self {
2303        Self {
2304            measure_policy: Rc::clone(&node.measure_policy),
2305            cache: node.cache_handles(),
2306            layout_runtime_state: node.layout_runtime_state_handle(),
2307            needs_layout: node.needs_layout(),
2308            needs_measure: node.needs_measure(),
2309        }
2310    }
2311}
2312
2313struct VecPools {
2314    state: Rc<RefCell<LayoutBuilderState>>,
2315    records: Vec<(NodeId, ChildRecord)>,
2316    child_ids: Vec<NodeId>,
2317    layout_node_data: Vec<LayoutModifierNodeData>,
2318    placements: Vec<Placement>,
2319}
2320
2321impl VecPools {
2322    fn acquire(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2323        let (records, child_ids, layout_node_data, placements) = {
2324            let mut state_mut = state.borrow_mut();
2325            (
2326                state_mut.frame_arena.tmp_records.acquire(),
2327                state_mut.frame_arena.tmp_child_ids.acquire(),
2328                state_mut.frame_arena.tmp_layout_node_data.acquire(),
2329                state_mut.frame_arena.tmp_placements.acquire(),
2330            )
2331        };
2332        Self {
2333            state,
2334            records,
2335            child_ids,
2336            layout_node_data,
2337            placements,
2338        }
2339    }
2340
2341    #[expect(clippy::type_complexity)]
2342    fn parts(
2343        &mut self,
2344    ) -> (
2345        &mut Vec<(NodeId, ChildRecord)>,
2346        &mut Vec<NodeId>,
2347        &mut Vec<LayoutModifierNodeData>,
2348        &mut Vec<Placement>,
2349    ) {
2350        (
2351            &mut self.records,
2352            &mut self.child_ids,
2353            &mut self.layout_node_data,
2354            &mut self.placements,
2355        )
2356    }
2357}
2358
2359impl Drop for VecPools {
2360    fn drop(&mut self) {
2361        let mut state = self.state.borrow_mut();
2362        state
2363            .frame_arena
2364            .tmp_records
2365            .release(std::mem::take(&mut self.records));
2366        state
2367            .frame_arena
2368            .tmp_child_ids
2369            .release(std::mem::take(&mut self.child_ids));
2370        state
2371            .frame_arena
2372            .tmp_layout_node_data
2373            .release(std::mem::take(&mut self.layout_node_data));
2374        state
2375            .frame_arena
2376            .tmp_placements
2377            .release(std::mem::take(&mut self.placements));
2378    }
2379}
2380
2381struct SlotsGuard {
2382    state: Rc<RefCell<LayoutBuilderState>>,
2383    slots: Option<SlotTable>,
2384}
2385
2386impl SlotsGuard {
2387    fn take(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2388        let slots = {
2389            let state_ref = state.borrow();
2390            let mut slots_ref = state_ref.slots.borrow_mut();
2391            std::mem::take(&mut *slots_ref)
2392        };
2393        Self {
2394            state,
2395            slots: Some(slots),
2396        }
2397    }
2398
2399    fn host(&mut self) -> Rc<SlotsHost> {
2400        let slots = self.slots.take().unwrap_or_default();
2401        Rc::new(SlotsHost::new(slots))
2402    }
2403
2404    fn restore(&mut self, slots: SlotTable) {
2405        debug_assert!(self.slots.is_none());
2406        self.slots = Some(slots);
2407    }
2408}
2409
2410impl Drop for SlotsGuard {
2411    fn drop(&mut self) {
2412        if let Some(slots) = self.slots.take() {
2413            let state_ref = self.state.borrow();
2414            *state_ref.slots.borrow_mut() = slots;
2415        }
2416    }
2417}
2418
2419#[derive(Clone)]
2420struct LayoutMeasureHandle {
2421    state: Rc<RefCell<LayoutBuilderState>>,
2422}
2423
2424impl LayoutMeasureHandle {
2425    fn new(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2426        Self { state }
2427    }
2428
2429    fn measure(
2430        &self,
2431        node_id: NodeId,
2432        constraints: Constraints,
2433    ) -> Result<Rc<MeasuredNode>, NodeError> {
2434        LayoutBuilderState::measure_node(Rc::clone(&self.state), node_id, constraints)
2435    }
2436}
2437
2438#[derive(Debug, Clone)]
2439pub(crate) struct MeasuredNode {
2440    node_id: NodeId,
2441    size: Size,
2442    offset: Point,
2443    content_offset: Point,
2444    children: Vec<MeasuredChild>,
2445    window_root: bool,
2446}
2447
2448impl MeasuredNode {
2449    fn new(
2450        node_id: NodeId,
2451        size: Size,
2452        offset: Point,
2453        content_offset: Point,
2454        children: Vec<MeasuredChild>,
2455    ) -> Self {
2456        Self {
2457            node_id,
2458            size,
2459            offset,
2460            content_offset,
2461            children,
2462            window_root: false,
2463        }
2464    }
2465
2466    fn with_window_root(mut self, window_root: bool) -> Self {
2467        self.window_root = window_root;
2468        self
2469    }
2470
2471    pub(crate) fn size_for_parent(&self) -> Size {
2472        if self.window_root {
2473            Size::new(0.0, 0.0)
2474        } else {
2475            self.size
2476        }
2477    }
2478
2479    #[cfg(test)]
2480    pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2481        Self::new(
2482            node_id,
2483            size,
2484            Point::default(),
2485            Point::default(),
2486            Vec::new(),
2487        )
2488    }
2489
2490    pub(crate) fn node_id(&self) -> NodeId {
2491        self.node_id
2492    }
2493
2494    pub(crate) fn size(&self) -> Size {
2495        self.size
2496    }
2497}
2498
2499#[derive(Debug, Clone)]
2500struct MeasuredChild {
2501    node: Rc<MeasuredNode>,
2502    offset: Point,
2503}
2504
2505struct ChildRecord {
2506    state: Rc<LayoutChildMeasureState>,
2507}
2508
2509struct CoordinatorFrame<'a> {
2510    measure_policy: &'a Rc<dyn MeasurePolicy>,
2511    scope: &'a dyn cranpose_ui_layout::MeasureScope,
2512    measurables: &'a [Box<dyn Measurable>],
2513    placements: RefCell<&'a mut Vec<Placement>>,
2514    context: RefCell<LayoutNodeContext>,
2515}
2516
2517impl<'a> CoordinatorFrame<'a> {
2518    fn new(
2519        measure_policy: &'a Rc<dyn MeasurePolicy>,
2520        scope: &'a dyn cranpose_ui_layout::MeasureScope,
2521        measurables: &'a [Box<dyn Measurable>],
2522        placements: &'a mut Vec<Placement>,
2523    ) -> Self {
2524        Self {
2525            measure_policy,
2526            scope,
2527            measurables,
2528            placements: RefCell::new(placements),
2529            context: RefCell::new(LayoutNodeContext::new(scope.density())),
2530        }
2531    }
2532
2533    fn take_invalidations(&self) -> Vec<InvalidationKind> {
2534        self.context.borrow_mut().take_invalidations()
2535    }
2536}
2537
2538struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2539    chain: &'chain CoordinatorChain,
2540    frame: &'frame_ref CoordinatorFrame<'frame_data>,
2541    index: usize,
2542}
2543
2544impl Measurable for CoordinatorLink<'_, '_, '_> {
2545    fn measure(&self, constraints: Constraints) -> Placeable {
2546        self.chain.measure_from(self.index, self.frame, constraints)
2547    }
2548
2549    fn min_intrinsic_width(&self, height: f32) -> f32 {
2550        self.chain
2551            .min_intrinsic_width_from(self.index, self.frame, height)
2552    }
2553
2554    fn max_intrinsic_width(&self, height: f32) -> f32 {
2555        self.chain
2556            .max_intrinsic_width_from(self.index, self.frame, height)
2557    }
2558
2559    fn min_intrinsic_height(&self, width: f32) -> f32 {
2560        self.chain
2561            .min_intrinsic_height_from(self.index, self.frame, width)
2562    }
2563
2564    fn max_intrinsic_height(&self, width: f32) -> f32 {
2565        self.chain
2566            .max_intrinsic_height_from(self.index, self.frame, width)
2567    }
2568}
2569
2570struct CoordinatorNode {
2571    modifier_index: usize,
2572    node: Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2573    measured_size: Cell<Size>,
2574    accumulated_offset: Cell<Point>,
2575}
2576
2577impl CoordinatorNode {
2578    fn new(
2579        modifier_index: usize,
2580        node: Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2581    ) -> Self {
2582        Self {
2583            modifier_index,
2584            node,
2585            measured_size: Cell::new(Size::default()),
2586            accumulated_offset: Cell::new(Point::default()),
2587        }
2588    }
2589
2590    fn matches(
2591        &self,
2592        modifier_index: usize,
2593        node: &Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2594    ) -> bool {
2595        self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2596    }
2597
2598    #[cfg(test)]
2599    fn ptr(&self) -> usize {
2600        Rc::as_ptr(&self.node) as *const () as usize
2601    }
2602}
2603
2604#[derive(Default)]
2605struct CoordinatorChain {
2606    nodes: Vec<CoordinatorNode>,
2607    /// The size the node's own measure policy measured its content at.
2608    inner_size: Cell<Size>,
2609}
2610
2611impl CoordinatorChain {
2612    fn reconcile(&mut self, layout_node_data: &[LayoutModifierNodeData]) {
2613        if self.matches(layout_node_data) {
2614            return;
2615        }
2616
2617        let mut previous_nodes = std::mem::take(&mut self.nodes);
2618        self.nodes.reserve(layout_node_data.len());
2619
2620        for (modifier_index, node) in layout_node_data {
2621            if let Some(position) = previous_nodes
2622                .iter()
2623                .position(|candidate| candidate.matches(*modifier_index, node))
2624            {
2625                self.nodes.push(previous_nodes.swap_remove(position));
2626            } else {
2627                self.nodes
2628                    .push(CoordinatorNode::new(*modifier_index, Rc::clone(node)));
2629            }
2630        }
2631    }
2632
2633    fn matches(&self, layout_node_data: &[LayoutModifierNodeData]) -> bool {
2634        self.nodes.len() == layout_node_data.len()
2635            && self
2636                .nodes
2637                .iter()
2638                .zip(layout_node_data.iter())
2639                .all(|(node, (modifier_index, node_rc))| node.matches(*modifier_index, node_rc))
2640    }
2641
2642    fn measure_from(
2643        &self,
2644        index: usize,
2645        frame: &CoordinatorFrame<'_>,
2646        constraints: Constraints,
2647    ) -> Placeable {
2648        let Some(node) = self.nodes.get(index) else {
2649            let mut placements = frame.placements.borrow_mut();
2650            let size = frame.measure_policy.measure_into(
2651                frame.scope,
2652                frame.measurables,
2653                constraints,
2654                &mut placements,
2655            );
2656            self.inner_size.set(size);
2657            return Placeable::value(size.width, size.height, NodeId::default());
2658        };
2659
2660        let wrapped = CoordinatorLink {
2661            chain: self,
2662            frame,
2663            index: index + 1,
2664        };
2665        let node_borrow = node.node.borrow();
2666
2667        let Some(layout_node) = node_borrow.as_layout_node() else {
2668            let placeable = wrapped.measure(constraints);
2669            node.measured_size.set(Size {
2670                width: placeable.width(),
2671                height: placeable.height(),
2672            });
2673            let child_accumulated = self.total_content_offset_from(index + 1);
2674            node.accumulated_offset.set(child_accumulated);
2675            return Placeable::value_with_offset(
2676                placeable.width(),
2677                placeable.height(),
2678                NodeId::default(),
2679                (child_accumulated.x, child_accumulated.y),
2680            );
2681        };
2682
2683        let result = match frame.context.try_borrow_mut() {
2684            Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
2685            Err(_) => {
2686                let mut temp = LayoutNodeContext::new(frame.scope.density());
2687                let result = layout_node.measure(&mut temp, &wrapped, constraints);
2688                if let Ok(mut context) = frame.context.try_borrow_mut() {
2689                    for kind in temp.take_invalidations() {
2690                        context.invalidate(kind);
2691                    }
2692                }
2693                result
2694            }
2695        };
2696
2697        node.measured_size.set(result.size);
2698        let local_offset = Point {
2699            x: result.placement_offset_x,
2700            y: result.placement_offset_y,
2701        };
2702        let child_accumulated = self.total_content_offset_from(index + 1);
2703        let accumulated = Point {
2704            x: local_offset.x + child_accumulated.x,
2705            y: local_offset.y + child_accumulated.y,
2706        };
2707        node.accumulated_offset.set(accumulated);
2708
2709        Placeable::value_with_offset(
2710            result.size.width,
2711            result.size.height,
2712            NodeId::default(),
2713            (accumulated.x, accumulated.y),
2714        )
2715    }
2716
2717    fn min_intrinsic_width_from(
2718        &self,
2719        index: usize,
2720        frame: &CoordinatorFrame<'_>,
2721        height: f32,
2722    ) -> f32 {
2723        let Some(node) = self.nodes.get(index) else {
2724            return frame
2725                .measure_policy
2726                .min_intrinsic_width(frame.measurables, height);
2727        };
2728        let wrapped = CoordinatorLink {
2729            chain: self,
2730            frame,
2731            index: index + 1,
2732        };
2733        let node_borrow = node.node.borrow();
2734        node_borrow.as_layout_node().map_or_else(
2735            || wrapped.min_intrinsic_width(height),
2736            |layout_node| layout_node.min_intrinsic_width(&wrapped, height, frame.scope.density()),
2737        )
2738    }
2739
2740    fn max_intrinsic_width_from(
2741        &self,
2742        index: usize,
2743        frame: &CoordinatorFrame<'_>,
2744        height: f32,
2745    ) -> f32 {
2746        let Some(node) = self.nodes.get(index) else {
2747            return frame
2748                .measure_policy
2749                .max_intrinsic_width(frame.measurables, height);
2750        };
2751        let wrapped = CoordinatorLink {
2752            chain: self,
2753            frame,
2754            index: index + 1,
2755        };
2756        let node_borrow = node.node.borrow();
2757        node_borrow.as_layout_node().map_or_else(
2758            || wrapped.max_intrinsic_width(height),
2759            |layout_node| layout_node.max_intrinsic_width(&wrapped, height, frame.scope.density()),
2760        )
2761    }
2762
2763    fn min_intrinsic_height_from(
2764        &self,
2765        index: usize,
2766        frame: &CoordinatorFrame<'_>,
2767        width: f32,
2768    ) -> f32 {
2769        let Some(node) = self.nodes.get(index) else {
2770            return frame
2771                .measure_policy
2772                .min_intrinsic_height(frame.measurables, width);
2773        };
2774        let wrapped = CoordinatorLink {
2775            chain: self,
2776            frame,
2777            index: index + 1,
2778        };
2779        let node_borrow = node.node.borrow();
2780        node_borrow.as_layout_node().map_or_else(
2781            || wrapped.min_intrinsic_height(width),
2782            |layout_node| layout_node.min_intrinsic_height(&wrapped, width, frame.scope.density()),
2783        )
2784    }
2785
2786    fn max_intrinsic_height_from(
2787        &self,
2788        index: usize,
2789        frame: &CoordinatorFrame<'_>,
2790        width: f32,
2791    ) -> f32 {
2792        let Some(node) = self.nodes.get(index) else {
2793            return frame
2794                .measure_policy
2795                .max_intrinsic_height(frame.measurables, width);
2796        };
2797        let wrapped = CoordinatorLink {
2798            chain: self,
2799            frame,
2800            index: index + 1,
2801        };
2802        let node_borrow = node.node.borrow();
2803        node_borrow.as_layout_node().map_or_else(
2804            || wrapped.max_intrinsic_height(width),
2805            |layout_node| layout_node.max_intrinsic_height(&wrapped, width, frame.scope.density()),
2806        )
2807    }
2808
2809    /// Writes where each coordinator and the node's content ended up in the
2810    /// last measure, relative to the node drawn at its `node_offset`.
2811    fn write_geometry(&self, geometry: &crate::modifier::CoordinatorGeometry, node_offset: Point) {
2812        let content = self.total_content_offset_from(0);
2813        let placed = |inner_offset: Point, size: Size| GeometryRect {
2814            x: content.x - inner_offset.x - node_offset.x,
2815            y: content.y - inner_offset.y - node_offset.y,
2816            width: size.width,
2817            height: size.height,
2818        };
2819        geometry.replace(
2820            self.nodes
2821                .iter()
2822                .map(|node| placed(node.accumulated_offset.get(), node.measured_size.get()))
2823                .chain(std::iter::once(placed(
2824                    Point::default(),
2825                    self.inner_size.get(),
2826                ))),
2827        );
2828    }
2829
2830    fn total_content_offset_from(&self, index: usize) -> Point {
2831        self.nodes
2832            .get(index)
2833            .map(|node| node.accumulated_offset.get())
2834            .unwrap_or_default()
2835    }
2836
2837    #[cfg(test)]
2838    fn debug_ptrs(&self) -> Vec<usize> {
2839        self.nodes.iter().map(CoordinatorNode::ptr).collect()
2840    }
2841}
2842
2843#[derive(Default)]
2844pub(crate) struct LayoutRuntimeState {
2845    child_ids: Vec<NodeId>,
2846    child_states: Vec<Rc<LayoutChildMeasureState>>,
2847    child_measurables: Vec<Box<dyn Measurable>>,
2848    coordinator_chain: CoordinatorChain,
2849}
2850
2851impl LayoutRuntimeState {
2852    fn reconcile_child_measurables(&mut self, child_ids: &[NodeId]) {
2853        if self.child_ids == child_ids {
2854            return;
2855        }
2856
2857        let mut previous_ids = std::mem::take(&mut self.child_ids);
2858        let mut previous_states = std::mem::take(&mut self.child_states);
2859        let mut previous_measurables = std::mem::take(&mut self.child_measurables);
2860
2861        self.child_ids.reserve(child_ids.len());
2862        self.child_states.reserve(child_ids.len());
2863        self.child_measurables.reserve(child_ids.len());
2864
2865        for &child_id in child_ids {
2866            if let Some(position) = previous_ids.iter().position(|&id| id == child_id) {
2867                self.child_ids.push(previous_ids.swap_remove(position));
2868                self.child_states
2869                    .push(previous_states.swap_remove(position));
2870                self.child_measurables
2871                    .push(previous_measurables.swap_remove(position));
2872            } else {
2873                let state = LayoutChildMeasureState::new(child_id);
2874                self.child_ids.push(child_id);
2875                self.child_states.push(Rc::clone(&state));
2876                self.child_measurables
2877                    .push(Box::new(LayoutChildMeasurable::new(state)));
2878            }
2879        }
2880    }
2881
2882    fn child_state(&self, index: usize) -> Rc<LayoutChildMeasureState> {
2883        Rc::clone(&self.child_states[index])
2884    }
2885
2886    fn child_measurables(&self) -> &[Box<dyn Measurable>] {
2887        self.child_measurables.as_slice()
2888    }
2889
2890    fn reconcile_coordinator_chain(&mut self, layout_node_data: &[LayoutModifierNodeData]) {
2891        self.coordinator_chain.reconcile(layout_node_data);
2892    }
2893
2894    fn coordinator_chain(&self) -> &CoordinatorChain {
2895        &self.coordinator_chain
2896    }
2897
2898    fn clear_frame_bindings(&self) {
2899        for child_state in &self.child_states {
2900            child_state.clear_frame_bindings();
2901        }
2902    }
2903
2904    #[cfg(test)]
2905    pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
2906        LayoutRuntimeDebugStats {
2907            child_ids: self.child_ids.clone(),
2908            child_state_ptrs: self
2909                .child_states
2910                .iter()
2911                .map(|state| Rc::as_ptr(state) as *const () as usize)
2912                .collect(),
2913            child_measurable_ptrs: self
2914                .child_measurables
2915                .iter()
2916                .map(|measurable| {
2917                    measurable.as_ref() as *const dyn Measurable as *const () as usize
2918                })
2919                .collect(),
2920            child_measurable_count: self.child_measurables.len(),
2921            coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
2922            coordinator_node_count: self.coordinator_chain.nodes.len(),
2923        }
2924    }
2925}
2926
2927#[cfg(test)]
2928#[derive(Debug, Clone, PartialEq, Eq)]
2929pub(crate) struct LayoutRuntimeDebugStats {
2930    pub(crate) child_ids: Vec<NodeId>,
2931    pub(crate) child_state_ptrs: Vec<usize>,
2932    pub(crate) child_measurable_ptrs: Vec<usize>,
2933    pub(crate) child_measurable_count: usize,
2934    pub(crate) coordinator_node_ptrs: Vec<usize>,
2935    pub(crate) coordinator_node_count: usize,
2936}
2937
2938struct LayoutChildMeasureConfig {
2939    applier: Rc<ConcreteApplierHost<MemoryApplier>>,
2940    node_id: NodeId,
2941    error: Rc<RefCell<Option<NodeError>>>,
2942    runtime_handle: Option<RuntimeHandle>,
2943    cache: LayoutNodeCacheHandles,
2944    cache_epoch: u64,
2945    force_remeasure: bool,
2946    measure_handle: Option<LayoutMeasureHandle>,
2947    layout_state: Option<Rc<RefCell<LayoutState>>>,
2948}
2949
2950struct LayoutChildMeasureState {
2951    applier: RefCell<Option<Rc<ConcreteApplierHost<MemoryApplier>>>>,
2952    node_id: Cell<NodeId>,
2953    measured: RefCell<Option<Rc<MeasuredNode>>>,
2954    last_position: Cell<Option<Point>>,
2955    error: RefCell<Option<Rc<RefCell<Option<NodeError>>>>>,
2956    runtime_handle: RefCell<Option<RuntimeHandle>>,
2957    cache: RefCell<LayoutNodeCacheHandles>,
2958    cache_epoch: Cell<u64>,
2959    force_remeasure: Cell<bool>,
2960    measure_handle: RefCell<Option<LayoutMeasureHandle>>,
2961    layout_state: RefCell<Option<Rc<RefCell<LayoutState>>>>,
2962}
2963
2964impl LayoutChildMeasureState {
2965    fn new(node_id: NodeId) -> Rc<Self> {
2966        Rc::new(Self {
2967            applier: RefCell::new(None),
2968            node_id: Cell::new(node_id),
2969            measured: RefCell::new(None),
2970            last_position: Cell::new(None),
2971            error: RefCell::new(None),
2972            runtime_handle: RefCell::new(None),
2973            cache: RefCell::new(LayoutNodeCacheHandles::default()),
2974            cache_epoch: Cell::new(0),
2975            force_remeasure: Cell::new(true),
2976            measure_handle: RefCell::new(None),
2977            layout_state: RefCell::new(None),
2978        })
2979    }
2980
2981    fn configure(&self, config: LayoutChildMeasureConfig) {
2982        config.cache.activate(config.cache_epoch);
2983        *self.applier.borrow_mut() = Some(config.applier);
2984        self.node_id.set(config.node_id);
2985        self.measured.borrow_mut().take();
2986        self.last_position.set(None);
2987        *self.error.borrow_mut() = Some(config.error);
2988        *self.runtime_handle.borrow_mut() = config.runtime_handle;
2989        *self.cache.borrow_mut() = config.cache;
2990        self.cache_epoch.set(config.cache_epoch);
2991        self.force_remeasure.set(config.force_remeasure);
2992        *self.measure_handle.borrow_mut() = config.measure_handle;
2993        *self.layout_state.borrow_mut() = config.layout_state;
2994    }
2995
2996    fn clear_frame_bindings(&self) {
2997        self.measured.borrow_mut().take();
2998        *self.applier.borrow_mut() = None;
2999        *self.error.borrow_mut() = None;
3000        *self.runtime_handle.borrow_mut() = None;
3001        *self.measure_handle.borrow_mut() = None;
3002        *self.layout_state.borrow_mut() = None;
3003    }
3004
3005    fn node_id(&self) -> NodeId {
3006        self.node_id.get()
3007    }
3008
3009    fn cache(&self) -> LayoutNodeCacheHandles {
3010        self.cache.borrow().clone()
3011    }
3012
3013    fn applier(&self) -> Option<Rc<ConcreteApplierHost<MemoryApplier>>> {
3014        self.applier.borrow().clone()
3015    }
3016
3017    fn layout_state(&self) -> Option<Rc<RefCell<LayoutState>>> {
3018        self.layout_state.borrow().clone()
3019    }
3020
3021    fn take_measured(&self) -> Option<Rc<MeasuredNode>> {
3022        self.measured.borrow_mut().take()
3023    }
3024
3025    fn last_position(&self) -> Option<Point> {
3026        self.last_position.get()
3027    }
3028
3029    fn set_last_position(&self, position: Point) {
3030        self.last_position.set(Some(position));
3031    }
3032
3033    fn place_retained(&self, position: Point) {
3034        self.set_last_position(position);
3035        if let Some(layout_state) = self.layout_state() {
3036            layout_state.borrow_mut().place(position);
3037            return;
3038        }
3039        let Some(applier) = self.applier() else {
3040            return;
3041        };
3042        let Ok(mut applier) = applier.try_borrow_typed() else {
3043            return;
3044        };
3045        let node_id = self.node_id();
3046        if applier
3047            .with_node::<LayoutNode, _>(node_id, |node| {
3048                node.set_position(position);
3049            })
3050            .is_err()
3051        {
3052            let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3053                node.set_position(position);
3054            });
3055        }
3056    }
3057
3058    fn set_measured(&self, measured: Option<Rc<MeasuredNode>>) {
3059        *self.measured.borrow_mut() = measured;
3060    }
3061
3062    fn record_error(&self, err: NodeError) {
3063        let Some(error) = self.error.borrow().clone() else {
3064            return;
3065        };
3066        let mut slot = error.borrow_mut();
3067        if slot.is_none() {
3068            *slot = Some(err);
3069        }
3070    }
3071
3072    fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
3073        let node_id = self.node_id();
3074        if let Some(handle) = self.measure_handle.borrow().clone() {
3075            return handle.measure(node_id, constraints);
3076        }
3077        let applier = self.applier().ok_or(NodeError::MissingContext {
3078            id: node_id,
3079            reason: "layout child applier not configured",
3080        })?;
3081        measure_node_with_host(
3082            applier,
3083            self.runtime_handle.borrow().clone(),
3084            node_id,
3085            constraints,
3086            self.cache_epoch.get(),
3087        )
3088    }
3089
3090    fn intrinsic_measure(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
3091        let cache = self.cache();
3092        cache.activate(self.cache_epoch.get());
3093        if !self.force_remeasure.get()
3094            && let Some(cached) = cache.get_measurement(constraints)
3095        {
3096            return Some(cached);
3097        }
3098
3099        match self.perform_measure(constraints) {
3100            Ok(measured) => {
3101                self.force_remeasure.set(false);
3102                cache.store_measurement(constraints, Rc::clone(&measured));
3103                Some(measured)
3104            }
3105            Err(err) => {
3106                self.record_error(err);
3107                None
3108            }
3109        }
3110    }
3111}
3112
3113struct LayoutChildMeasurable {
3114    state: Rc<LayoutChildMeasureState>,
3115}
3116
3117impl LayoutChildMeasurable {
3118    fn new(state: Rc<LayoutChildMeasureState>) -> Self {
3119        Self { state }
3120    }
3121
3122    fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
3123        let applier = self.state.applier()?;
3124        let node_id = self.state.node_id();
3125        let Ok(mut applier) = applier.try_borrow_typed() else {
3126            return None;
3127        };
3128
3129        applier
3130            .with_node::<LayoutNode, _>(node_id, |layout_node| {
3131                let props = layout_node.resolved_modifiers().layout_properties();
3132                let weight = props.weight().unwrap_or_default();
3133                cranpose_ui_layout::ParentData {
3134                    weight: weight.weight,
3135                    fill: weight.fill,
3136                    box_alignment: props.box_alignment(),
3137                    row_alignment: props.row_alignment(),
3138                    column_alignment: props.column_alignment(),
3139                }
3140            })
3141            .ok()
3142    }
3143}
3144
3145impl Measurable for LayoutChildMeasurable {
3146    fn measure(&self, constraints: Constraints) -> Placeable {
3147        let state = &self.state;
3148        let cache = state.cache();
3149        cache.activate(state.cache_epoch.get());
3150        let measured_size;
3151        if !state.force_remeasure.get() {
3152            if let Some(cached) = cache.get_measurement(constraints) {
3153                measured_size = cached.size;
3154                state.set_measured(Some(Rc::clone(&cached)));
3155            } else {
3156                match state.perform_measure(constraints) {
3157                    Ok(measured) => {
3158                        state.force_remeasure.set(false);
3159                        measured_size = measured.size;
3160                        cache.store_measurement(constraints, Rc::clone(&measured));
3161                        state.set_measured(Some(measured));
3162                    }
3163                    Err(err) => {
3164                        state.record_error(err);
3165                        state.set_measured(None);
3166                        measured_size = Size {
3167                            width: 0.0,
3168                            height: 0.0,
3169                        };
3170                    }
3171                }
3172            }
3173        } else {
3174            match state.perform_measure(constraints) {
3175                Ok(measured) => {
3176                    state.force_remeasure.set(false);
3177                    measured_size = measured.size;
3178                    cache.store_measurement(constraints, Rc::clone(&measured));
3179                    state.set_measured(Some(measured));
3180                }
3181                Err(err) => {
3182                    state.record_error(err);
3183                    state.set_measured(None);
3184                    measured_size = Size {
3185                        width: 0.0,
3186                        height: 0.0,
3187                    };
3188                }
3189            }
3190        }
3191
3192        if let Some(layout_state) = state.layout_state() {
3193            let mut layout_state = layout_state.borrow_mut();
3194            layout_state.set_size(measured_size);
3195        } else if let Some(applier) = state.applier() {
3196            let Ok(mut applier) = applier.try_borrow_typed() else {
3197                return Placeable::value(
3198                    measured_size.width,
3199                    measured_size.height,
3200                    state.node_id(),
3201                );
3202            };
3203            let _ = applier.with_node::<LayoutNode, _>(state.node_id(), |node| {
3204                node.set_measured_size(measured_size);
3205            });
3206        }
3207
3208        let state = Rc::clone(&self.state);
3209        let node_id = state.node_id();
3210        let size_for_parent = state
3211            .measured
3212            .borrow()
3213            .as_ref()
3214            .map_or(measured_size, |measured| measured.size_for_parent());
3215
3216        let place_fn = Rc::new(move |x: f32, y: f32| {
3217            let internal_offset = state
3218                .measured
3219                .borrow()
3220                .as_ref()
3221                .map(|m| m.offset)
3222                .unwrap_or_default();
3223
3224            state.place_retained(Point {
3225                x: x + internal_offset.x,
3226                y: y + internal_offset.y,
3227            });
3228        });
3229
3230        Placeable::with_place_fn(
3231            size_for_parent.width,
3232            size_for_parent.height,
3233            node_id,
3234            place_fn,
3235        )
3236    }
3237
3238    fn min_intrinsic_width(&self, height: f32) -> f32 {
3239        let kind = IntrinsicKind::MinWidth(height);
3240        let cache = self.state.cache();
3241        cache.activate(self.state.cache_epoch.get());
3242        if !self.state.force_remeasure.get()
3243            && let Some(value) = cache.get_intrinsic(&kind)
3244        {
3245            return value;
3246        }
3247        let constraints = Constraints {
3248            min_width: 0.0,
3249            max_width: f32::INFINITY,
3250            min_height: height,
3251            max_height: height,
3252        };
3253        if let Some(node) = self.state.intrinsic_measure(constraints) {
3254            let value = node.size_for_parent().width;
3255            cache.store_intrinsic(kind, value);
3256            value
3257        } else {
3258            0.0
3259        }
3260    }
3261
3262    fn max_intrinsic_width(&self, height: f32) -> f32 {
3263        let kind = IntrinsicKind::MaxWidth(height);
3264        let cache = self.state.cache();
3265        cache.activate(self.state.cache_epoch.get());
3266        if !self.state.force_remeasure.get()
3267            && let Some(value) = cache.get_intrinsic(&kind)
3268        {
3269            return value;
3270        }
3271        let constraints = Constraints {
3272            min_width: 0.0,
3273            max_width: f32::INFINITY,
3274            min_height: 0.0,
3275            max_height: height,
3276        };
3277        if let Some(node) = self.state.intrinsic_measure(constraints) {
3278            let value = node.size_for_parent().width;
3279            cache.store_intrinsic(kind, value);
3280            value
3281        } else {
3282            0.0
3283        }
3284    }
3285
3286    fn min_intrinsic_height(&self, width: f32) -> f32 {
3287        let kind = IntrinsicKind::MinHeight(width);
3288        let cache = self.state.cache();
3289        cache.activate(self.state.cache_epoch.get());
3290        if !self.state.force_remeasure.get()
3291            && let Some(value) = cache.get_intrinsic(&kind)
3292        {
3293            return value;
3294        }
3295        let constraints = Constraints {
3296            min_width: width,
3297            max_width: width,
3298            min_height: 0.0,
3299            max_height: f32::INFINITY,
3300        };
3301        if let Some(node) = self.state.intrinsic_measure(constraints) {
3302            let value = node.size_for_parent().height;
3303            cache.store_intrinsic(kind, value);
3304            value
3305        } else {
3306            0.0
3307        }
3308    }
3309
3310    fn max_intrinsic_height(&self, width: f32) -> f32 {
3311        let kind = IntrinsicKind::MaxHeight(width);
3312        let cache = self.state.cache();
3313        cache.activate(self.state.cache_epoch.get());
3314        if !self.state.force_remeasure.get()
3315            && let Some(value) = cache.get_intrinsic(&kind)
3316        {
3317            return value;
3318        }
3319        let constraints = Constraints {
3320            min_width: 0.0,
3321            max_width: width,
3322            min_height: 0.0,
3323            max_height: f32::INFINITY,
3324        };
3325        if let Some(node) = self.state.intrinsic_measure(constraints) {
3326            let value = node.size_for_parent().height;
3327            cache.store_intrinsic(kind, value);
3328            value
3329        } else {
3330            0.0
3331        }
3332    }
3333
3334    fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3335        let parent_data = self.resolved_parent_data()?;
3336        if !parent_data.has_weight() {
3337            return None;
3338        }
3339        Some(cranpose_ui_layout::FlexParentData::new(
3340            parent_data.weight,
3341            parent_data.fill,
3342        ))
3343    }
3344
3345    fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3346        self.resolved_parent_data().unwrap_or_default()
3347    }
3348}
3349
3350fn measure_node_with_host(
3351    applier: Rc<ConcreteApplierHost<MemoryApplier>>,
3352    runtime_handle: Option<RuntimeHandle>,
3353    node_id: NodeId,
3354    constraints: Constraints,
3355    epoch: u64,
3356) -> Result<Rc<MeasuredNode>, NodeError> {
3357    let runtime_handle = match runtime_handle {
3358        Some(handle) => Some(handle),
3359        None => applier.borrow_typed().runtime_handle(),
3360    };
3361    let mut builder = LayoutBuilder::new_with_epoch(
3362        applier,
3363        epoch,
3364        Rc::new(RefCell::new(SlotTable::default())),
3365        FrameLayoutArena::default(),
3366    );
3367    builder.set_runtime_handle(runtime_handle);
3368    builder.measure_node(node_id, constraints)
3369}
3370
3371#[derive(Clone)]
3372struct RuntimeNodeMetadata {
3373    modifier: Modifier,
3374    resolved_modifiers: ResolvedModifiers,
3375    modifier_slices: Rc<ModifierNodeSlices>,
3376    semantics: Option<Rc<SemanticsConfiguration>>,
3377    role: SemanticsRole,
3378    button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3379}
3380
3381impl Default for RuntimeNodeMetadata {
3382    fn default() -> Self {
3383        Self {
3384            modifier: Modifier::empty(),
3385            resolved_modifiers: ResolvedModifiers::default(),
3386            modifier_slices: Rc::default(),
3387            semantics: None,
3388            role: SemanticsRole::Unknown,
3389            button_handler: None,
3390        }
3391    }
3392}
3393
3394fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3395    modifier_slices
3396        .annotated_text()
3397        .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3398            value: SemanticsText(Rc::clone(text)),
3399        })
3400}
3401
3402fn runtime_metadata_for(
3403    applier: &mut MemoryApplier,
3404    node_id: NodeId,
3405) -> Result<RuntimeNodeMetadata, NodeError> {
3406    if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3407        let modifier = layout.modifier.clone();
3408        let resolved_modifiers = layout.resolved_modifiers();
3409        let modifier_slices = layout.modifier_slices_snapshot();
3410        let role = role_from_modifier_slices(&modifier_slices);
3411
3412        RuntimeNodeMetadata {
3413            modifier,
3414            resolved_modifiers,
3415            modifier_slices,
3416            semantics: layout.semantics_configuration().map(Rc::new),
3417            role,
3418            button_handler: None,
3419        }
3420    }) {
3421        return Ok(meta);
3422    }
3423
3424    if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
3425        applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3426            (
3427                node.modifier(),
3428                node.resolved_modifiers(),
3429                node.modifier_slices_snapshot(),
3430                node.semantics_configuration().map(Rc::new),
3431            )
3432        })
3433    {
3434        return Ok(RuntimeNodeMetadata {
3435            modifier,
3436            resolved_modifiers,
3437            modifier_slices,
3438            semantics,
3439            role: SemanticsRole::Subcompose,
3440            button_handler: None,
3441        });
3442    }
3443    Ok(RuntimeNodeMetadata::default())
3444}
3445
3446fn clear_semantics_dirty_flags(
3447    applier: &mut MemoryApplier,
3448    node: &MeasuredNode,
3449) -> Result<(), NodeError> {
3450    match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3451        layout.clear_needs_semantics();
3452    }) {
3453        Ok(()) => {}
3454        Err(NodeError::Missing { .. }) => {}
3455        Err(NodeError::TypeMismatch { .. }) => {
3456            match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3457                subcompose.clear_needs_semantics();
3458            }) {
3459                Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
3460                Err(err) => return Err(err),
3461            }
3462        }
3463        Err(err) => return Err(err),
3464    }
3465
3466    for child in &node.children {
3467        clear_semantics_dirty_flags(applier, &child.node)?;
3468    }
3469
3470    Ok(())
3471}
3472
3473fn build_semantics_tree_from_live_nodes(
3474    applier: &mut MemoryApplier,
3475    node: &MeasuredNode,
3476) -> Result<SemanticsTree, NodeError> {
3477    Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3478        applier, node, None,
3479    )?))
3480}
3481
3482fn semantics_node_from_parts(
3483    node_id: NodeId,
3484    node_generation: u32,
3485    mut role: SemanticsRole,
3486    config: Option<SemanticsConfiguration>,
3487    children: Vec<SemanticsNode>,
3488    bounds: GeometryRect,
3489) -> SemanticsNode {
3490    let mut node = SemanticsNode {
3491        node_id,
3492        bounds,
3493        node_generation,
3494        children,
3495        ..SemanticsNode::default()
3496    };
3497
3498    if let Some(config) = config {
3499        if config.role == Some(SemanticsWidgetRole::Button) {
3500            role = SemanticsRole::Button;
3501        }
3502        if config.is_activatable() {
3503            node.actions.push(SemanticsAction::Click {
3504                handler: SemanticsCallback::new(node_id),
3505            });
3506        }
3507        node.widget_role = config.role;
3508        node.description = config.content_description;
3509        node.state_description = config.state_description;
3510        node.on_click_label = config.on_click_label.or_else(|| {
3511            config
3512                .on_click
3513                .as_ref()
3514                .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
3515        });
3516        node.on_click = config.on_click;
3517        node.on_long_click = config.on_long_click;
3518        node.on_long_click_label = config.on_long_click_label;
3519        node.on_magic_tap = config.on_magic_tap;
3520        node.on_magic_tap_label = config.on_magic_tap_label;
3521        node.input_labels = config.input_labels;
3522        node.language = config.language;
3523        node.selected = config.selected;
3524        node.toggled = config.toggled;
3525        node.enabled = config.enabled;
3526        node.custom_actions = config.custom_actions;
3527        node.canvas_children = config.canvas_children;
3528        node.editable_text = config.is_editable_text;
3529        node.multiline = config.multiline;
3530        node.hidden = config.hidden;
3531        node.is_modal = config.is_modal
3532            && modal_takes_space(Size {
3533                width: bounds.width,
3534                height: bounds.height,
3535            });
3536        node.merge_descendants = config.merge_descendants;
3537        node.selectable_group = config.selectable_group;
3538        node.pane_title = config.pane_title;
3539        node.error = config.error;
3540        node.password = config.password;
3541        node.traversal_index = config.traversal_index;
3542        node.text = config.text;
3543        node.text_selection = config.text_selection;
3544        node.live_region = config.live_region;
3545        node.progress = config.progress;
3546        node.set_progress = config.set_progress;
3547        node.set_text = config.set_text;
3548        node.set_selection = config.set_selection;
3549        node.expand = config.expand;
3550        node.collapse = config.collapse;
3551        node.dismiss = config.dismiss;
3552        node.vertical_scroll = config.vertical_scroll;
3553        node.horizontal_scroll = config.horizontal_scroll;
3554        node.scroll_by = config.scroll_by;
3555        node.scroll_to_index = config.scroll_to_index;
3556        node.collection = config.collection;
3557    }
3558
3559    node.focusable = crate::focus_dispatch::has_focus_target(node_id);
3560    node.focused = node.focusable && crate::focus_dispatch::active_focus_target() == Some(node_id);
3561
3562    node.role = role;
3563    node
3564}
3565
3566fn build_semantics_node_from_live_nodes(
3567    applier: &mut MemoryApplier,
3568    node: &MeasuredNode,
3569    origin: Option<Point>,
3570) -> Result<SemanticsNode, NodeError> {
3571    let (role, config, (bounds, content)) =
3572        match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3573            let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
3574            let config = layout.semantics_configuration();
3575            layout.clear_needs_semantics();
3576            (
3577                role,
3578                config,
3579                semantics_placement(&layout.layout_state(), origin),
3580            )
3581        }) {
3582            Ok(data) => data,
3583            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3584                match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3585                    subcompose.clear_needs_semantics();
3586                    (
3587                        SemanticsRole::Subcompose,
3588                        subcompose.semantics_configuration(),
3589                        semantics_placement(&subcompose.layout_state(), origin),
3590                    )
3591                }) {
3592                    Ok(data) => data,
3593                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3594                        let top_left = origin.unwrap_or_default();
3595                        (
3596                            SemanticsRole::Unknown,
3597                            None,
3598                            (
3599                                GeometryRect::from_origin_size(top_left, node.size),
3600                                top_left,
3601                            ),
3602                        )
3603                    }
3604                    Err(err) => return Err(err),
3605                }
3606            }
3607            Err(err) => return Err(err),
3608        };
3609
3610    let mut children = Vec::with_capacity(node.children.len());
3611    for child in &node.children {
3612        children.push(build_semantics_node_from_live_nodes(
3613            applier,
3614            &child.node,
3615            Some(content),
3616        )?);
3617    }
3618
3619    Ok(semantics_node_from_parts(
3620        node.node_id,
3621        applier.node_generation(node.node_id),
3622        role,
3623        config,
3624        children,
3625        bounds,
3626    ))
3627}
3628
3629fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
3630    stats.semantics_node_count += 1;
3631    stats.semantics_action_count += node.actions.len();
3632    stats.semantics_action_capacity += node.actions.capacity();
3633    stats.semantics_child_count += node.children.len();
3634    stats.semantics_child_capacity += node.children.capacity();
3635    stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
3636    stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
3637
3638    if let Some(description) = &node.description {
3639        stats.semantics_description_count += 1;
3640        stats.semantics_description_bytes += description.capacity();
3641        stats.semantics_heap_bytes += description.capacity();
3642    }
3643
3644    for child in &node.children {
3645        record_semantics_allocation_stats(child, stats);
3646    }
3647}
3648
3649fn record_layout_box_allocation_stats(
3650    layout_box: &LayoutBox,
3651    stats: &mut LayoutAllocationDebugStats,
3652) {
3653    stats.layout_box_count += 1;
3654    stats.layout_box_child_count += layout_box.children.len();
3655    stats.layout_box_child_capacity += layout_box.children.capacity();
3656    stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
3657    stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
3658
3659    for child in &layout_box.children {
3660        record_layout_box_allocation_stats(child, stats);
3661    }
3662}
3663
3664fn build_layout_tree(
3665    applier: &mut MemoryApplier,
3666    node: &MeasuredNode,
3667) -> Result<LayoutTree, NodeError> {
3668    fn place(
3669        applier: &mut MemoryApplier,
3670        node: &MeasuredNode,
3671        origin: Point,
3672        parent_layer_translation: Point,
3673    ) -> Result<LayoutBox, NodeError> {
3674        let top_left = Point {
3675            x: origin.x + node.offset.x,
3676            y: origin.y + node.offset.y,
3677        };
3678        let rect = GeometryRect {
3679            x: top_left.x,
3680            y: top_left.y,
3681            width: node.size.width,
3682            height: node.size.height,
3683        };
3684        let (data, layer_translation) = snapshot_node_data(
3685            applier,
3686            node.node_id,
3687            top_left,
3688            node.size,
3689            parent_layer_translation,
3690        )?;
3691        let mut children = Vec::with_capacity(node.children.len());
3692        for child in &node.children {
3693            if crate::modifier::is_window_root(applier, child.node.node_id) {
3694                continue;
3695            }
3696            let child_origin = Point {
3697                x: top_left.x + child.offset.x,
3698                y: top_left.y + child.offset.y,
3699            };
3700            children.push(place(
3701                applier,
3702                &child.node,
3703                child_origin,
3704                layer_translation,
3705            )?);
3706        }
3707        Ok(LayoutBox {
3708            node_generation: applier.node_generation(node.node_id),
3709            ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
3710        })
3711    }
3712
3713    Ok(LayoutTree::new(place(
3714        applier,
3715        node,
3716        Point { x: 0.0, y: 0.0 },
3717        Point { x: 0.0, y: 0.0 },
3718    )?))
3719}
3720
3721fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
3722    match &layout_box.node_data.kind {
3723        LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
3724        LayoutNodeKind::Spacer => SemanticsRole::Spacer,
3725        LayoutNodeKind::Unknown => SemanticsRole::Unknown,
3726        LayoutNodeKind::Button { .. } => SemanticsRole::Button,
3727        LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
3728    }
3729}
3730
3731fn build_semantics_node_from_layout_box(layout_box: &LayoutBox) -> SemanticsNode {
3732    let children = layout_box
3733        .children
3734        .iter()
3735        .map(build_semantics_node_from_layout_box)
3736        .collect();
3737
3738    semantics_node_from_parts(
3739        layout_box.node_id,
3740        layout_box.node_generation,
3741        semantics_role_from_layout_box(layout_box),
3742        layout_box.node_data.semantics().cloned(),
3743        children,
3744        layout_box.rect,
3745    )
3746}
3747
3748fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
3749    match &info.role {
3750        SemanticsRole::Layout => LayoutNodeKind::Layout,
3751        SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
3752        SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
3753        SemanticsRole::Spacer => LayoutNodeKind::Spacer,
3754        SemanticsRole::Button => {
3755            let handler = info
3756                .button_handler
3757                .as_ref()
3758                .cloned()
3759                .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
3760            LayoutNodeKind::Button { on_click: handler }
3761        }
3762        SemanticsRole::Unknown => LayoutNodeKind::Unknown,
3763    }
3764}
3765
3766fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
3767    let horizontal = padding.horizontal_sum();
3768    let vertical = padding.vertical_sum();
3769    let min_width = (constraints.min_width - horizontal).max(0.0);
3770    let mut max_width = constraints.max_width;
3771    if max_width.is_finite() {
3772        max_width = (max_width - horizontal).max(0.0);
3773    }
3774    let min_height = (constraints.min_height - vertical).max(0.0);
3775    let mut max_height = constraints.max_height;
3776    if max_height.is_finite() {
3777        max_height = (max_height - vertical).max(0.0);
3778    }
3779    normalize_constraints(Constraints {
3780        min_width,
3781        max_width,
3782        min_height,
3783        max_height,
3784    })
3785}
3786
3787fn resolve_dimension(
3788    base: f32,
3789    explicit: DimensionConstraint,
3790    min_override: Option<f32>,
3791    max_override: Option<f32>,
3792    min_limit: f32,
3793    max_limit: f32,
3794) -> f32 {
3795    let mut min_bound = min_limit;
3796    if let Some(min_value) = min_override {
3797        min_bound = min_bound.max(min_value);
3798    }
3799
3800    let mut max_bound = if max_limit.is_finite() {
3801        max_limit
3802    } else {
3803        max_override.unwrap_or(max_limit)
3804    };
3805    if let Some(max_value) = max_override {
3806        if max_bound.is_finite() {
3807            max_bound = max_bound.min(max_value);
3808        } else {
3809            max_bound = max_value;
3810        }
3811    }
3812    if max_bound < min_bound {
3813        max_bound = min_bound;
3814    }
3815
3816    let mut size = match explicit {
3817        DimensionConstraint::Points(points) => points,
3818        DimensionConstraint::Fraction(fraction) => {
3819            if max_limit.is_finite() {
3820                max_limit * fraction.clamp(0.0, 1.0)
3821            } else {
3822                base
3823            }
3824        }
3825        DimensionConstraint::Unspecified => base,
3826        DimensionConstraint::Intrinsic(_) => base,
3827    };
3828
3829    size = clamp_dimension(size, min_bound, max_bound);
3830    size = clamp_dimension(size, min_limit, max_limit);
3831    size.max(0.0)
3832}
3833
3834fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
3835    let mut result = value.max(min);
3836    if max.is_finite() {
3837        result = result.min(max);
3838    }
3839    result
3840}
3841
3842fn normalize_constraints(mut constraints: Constraints) -> Constraints {
3843    if constraints.max_width < constraints.min_width {
3844        constraints.max_width = constraints.min_width;
3845    }
3846    if constraints.max_height < constraints.min_height {
3847        constraints.max_height = constraints.min_height;
3848    }
3849    constraints
3850}
3851
3852#[cfg(test)]
3853#[path = "tests/layout_tests.rs"]
3854mod tests;
3855
3856#[cfg(test)]
3857#[path = "tests/coordinator_geometry_tests.rs"]
3858mod coordinator_geometry_tests;