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