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