Skip to main content

cranpose_ui/layout/
mod.rs

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