Skip to main content

cranpose_ui/layout/
mod.rs

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