Skip to main content

cranpose_ui/layout/
mod.rs

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