Skip to main content

cranpose_ui/layout/
mod.rs

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