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