Skip to main content

cranpose_ui/layout/
mod.rs

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