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