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