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