Skip to main content

cranpose_ui/layout/
mod.rs

1pub mod core;
2pub mod policies;
3mod reveal;
4mod semantics_details;
5mod semantics_labels;
6mod semantics_update;
7
8use std::{
9    cell::{Cell, RefCell},
10    fmt,
11    mem::size_of,
12    rc::Rc,
13    sync::OnceLock,
14};
15
16use cranpose_core::{
17    Applier, ApplierHost, Composer, ConcreteApplierHost, MemoryApplier, Node, NodeError, NodeId,
18    Phase, RuntimeHandle, SlotTable, SlotsHost,
19};
20use cranpose_foundation::{
21    InvalidationKind, ModifierNodeContext, NodeCapabilities, SemanticsConfiguration,
22    SemanticsCustomAction, SemanticsWidgetRole,
23};
24use cranpose_ui_graphics::{ProjectiveTransform, layer_transform::layer_transform_to_window};
25use cranpose_ui_layout::{AlignmentLines, Constraints, MeasurePolicy, PlaceTarget, Placement};
26use web_time::Instant;
27
28#[cfg(test)]
29use self::core::{HorizontalAlignment, VerticalAlignment};
30use self::{
31    core::{Measurable, Placeable},
32    semantics_update::semantics_placement,
33};
34pub use self::{
35    reveal::can_skip_scroll_reveal_from_applier,
36    semantics_details::SemanticsDetails,
37    semantics_update::{build_semantics_tree_from_applier, update_semantics_tree_from_applier},
38};
39use crate::{
40    modifier::{
41        DimensionConstraint, EdgeInsets, Modifier, ModifierNodeSlices,
42        ModifierNodeSlicesDebugStats, Point, Rect as GeometryRect, ResolvedModifiers, Size,
43    },
44    subcompose_layout::{CachedBatchMeasureInputs, SubcomposeLayoutNode},
45    widgets::nodes::{
46        IntrinsicKind, LayoutNode, LayoutNodeCacheHandles, LayoutState, begin_placement_pass,
47        placement_pass_with_unplaced_nodes,
48    },
49};
50
51#[derive(Default)]
52pub(crate) struct LayoutNodeContext {
53    invalidations: Vec<InvalidationKind>,
54    update_requested: bool,
55    active_capabilities: Vec<NodeCapabilities>,
56    density: f32,
57}
58
59impl LayoutNodeContext {
60    pub(crate) fn new(density: f32) -> Self {
61        Self {
62            density,
63            ..Self::default()
64        }
65    }
66
67    pub(crate) fn take_invalidations(&mut self) -> Vec<InvalidationKind> {
68        std::mem::take(&mut self.invalidations)
69    }
70}
71
72impl ModifierNodeContext for LayoutNodeContext {
73    fn invalidate(&mut self, kind: InvalidationKind) {
74        if !self.invalidations.contains(&kind) {
75            self.invalidations.push(kind);
76        }
77    }
78
79    fn request_update(&mut self) {
80        self.update_requested = true;
81    }
82
83    fn push_active_capabilities(&mut self, capabilities: NodeCapabilities) {
84        self.active_capabilities.push(capabilities);
85    }
86
87    fn pop_active_capabilities(&mut self) {
88        self.active_capabilities.pop();
89    }
90
91    fn density(&self) -> f32 {
92        self.density
93    }
94}
95
96#[doc(hidden)]
97pub fn invalidate_all_layout_caches() {
98    crate::render_state::invalidate_layout_cache_epoch();
99}
100
101fn layout_measure_telemetry_threshold_ms() -> Option<f64> {
102    static THRESHOLD_MS: OnceLock<Option<f64>> = OnceLock::new();
103    *THRESHOLD_MS.get_or_init(|| {
104        std::env::var("CRANPOSE_LAYOUT_MEASURE_TELEMETRY_MS")
105            .ok()
106            .and_then(|value| value.parse::<f64>().ok())
107            .filter(|value| value.is_finite() && *value >= 0.0)
108            .or_else(|| {
109                std::env::var_os("CRANPOSE_LAYOUT_MEASURE_TELEMETRY")
110                    .is_some()
111                    .then_some(4.0)
112            })
113    })
114}
115
116struct LayoutMeasureTelemetry {
117    root: NodeId,
118    start: Instant,
119    after_repasses: Instant,
120    after_guard: Instant,
121    after_builder: Instant,
122    after_measure: Instant,
123    after_root_place: Instant,
124    after_aux: Instant,
125    after_builder_drop: Instant,
126    after_guard_drop: Instant,
127}
128
129fn log_layout_measure_telemetry(times: LayoutMeasureTelemetry) {
130    let Some(threshold_ms) = layout_measure_telemetry_threshold_ms() else {
131        return;
132    };
133
134    let total_ms = times
135        .after_guard_drop
136        .duration_since(times.start)
137        .as_secs_f64()
138        * 1000.0;
139    if total_ms < threshold_ms {
140        return;
141    }
142
143    let repass_ms = times
144        .after_repasses
145        .duration_since(times.start)
146        .as_secs_f64()
147        * 1000.0;
148    let guard_ms = times
149        .after_guard
150        .duration_since(times.after_repasses)
151        .as_secs_f64()
152        * 1000.0;
153    let builder_ms = times
154        .after_builder
155        .duration_since(times.after_guard)
156        .as_secs_f64()
157        * 1000.0;
158    let measure_ms = times
159        .after_measure
160        .duration_since(times.after_builder)
161        .as_secs_f64()
162        * 1000.0;
163    let root_place_ms = times
164        .after_root_place
165        .duration_since(times.after_measure)
166        .as_secs_f64()
167        * 1000.0;
168    let aux_ms = times
169        .after_aux
170        .duration_since(times.after_root_place)
171        .as_secs_f64()
172        * 1000.0;
173    let builder_drop_ms = times
174        .after_builder_drop
175        .duration_since(times.after_aux)
176        .as_secs_f64()
177        * 1000.0;
178    let guard_drop_ms = times
179        .after_guard_drop
180        .duration_since(times.after_builder_drop)
181        .as_secs_f64()
182        * 1000.0;
183    log::warn!(
184        "[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}",
185        times.root
186    );
187}
188
189fn log_node_measure_telemetry(
190    kind: &'static str,
191    node_id: NodeId,
192    constraints: Constraints,
193    measured: &MeasuredNode,
194    (threshold_ms, start): (f64, Instant),
195) {
196    let size = measured.size;
197    let children = measured.children.len();
198    let total_ms = start.elapsed().as_secs_f64() * 1000.0;
199    if total_ms < threshold_ms {
200        return;
201    }
202
203    log::warn!(
204        "[layout-node-telemetry] kind={kind} node={} total_ms={total_ms:.2} constraints=({:.1},{:.1},{:.1},{:.1}) size=({:.1},{:.1}) children={children}",
205        node_id,
206        constraints.min_width,
207        constraints.max_width,
208        constraints.min_height,
209        constraints.max_height,
210        size.width,
211        size.height,
212    );
213}
214
215struct ApplierSlotGuard<'a> {
216    target: &'a mut MemoryApplier,
217    host: Rc<ConcreteApplierHost<MemoryApplier>>,
218    slots: Rc<RefCell<SlotTable>>,
219}
220
221impl<'a> ApplierSlotGuard<'a> {
222    fn new(target: &'a mut MemoryApplier) -> Self {
223        let original_applier = std::mem::replace(target, MemoryApplier::new());
224        let host = Rc::new(ConcreteApplierHost::new(original_applier));
225
226        let slots = {
227            let mut applier_ref = host.borrow_typed();
228            std::mem::take(applier_ref.slots())
229        };
230        let slots = Rc::new(RefCell::new(slots));
231
232        Self {
233            target,
234            host,
235            slots,
236        }
237    }
238
239    fn host(&self) -> Rc<ConcreteApplierHost<MemoryApplier>> {
240        Rc::clone(&self.host)
241    }
242
243    fn slots_handle(&self) -> Rc<RefCell<SlotTable>> {
244        Rc::clone(&self.slots)
245    }
246}
247
248impl Drop for ApplierSlotGuard<'_> {
249    fn drop(&mut self) {
250        {
251            let mut applier_ref = self.host.borrow_typed();
252            *applier_ref.slots() = std::mem::take(&mut *self.slots.borrow_mut());
253        }
254
255        {
256            let mut applier_ref = self.host.borrow_typed();
257            let original_applier = std::mem::take(&mut *applier_ref);
258            let _ = std::mem::replace(self.target, original_applier);
259        }
260    }
261}
262
263#[derive(Clone, Copy, PartialEq)]
264struct ModifierChainInputs {
265    density: crate::density::Density,
266    window_root: bool,
267    offset: Point,
268    uses_chain: bool,
269}
270
271struct ModifierChainMeasurement {
272    size: Size,
273    alignment_lines: AlignmentLines,
274    content_offset: Point,
275    offset: Point,
276    window_root: bool,
277    uses_chain: bool,
278}
279
280struct ScratchVecPool<T> {
281    available: Vec<Vec<T>>,
282}
283
284impl<T> ScratchVecPool<T> {
285    fn acquire(&mut self) -> Vec<T> {
286        self.available.pop().unwrap_or_default()
287    }
288
289    fn release(&mut self, mut values: Vec<T>) {
290        values.clear();
291        self.available.push(values);
292    }
293
294    #[cfg(test)]
295    fn available_count(&self) -> usize {
296        self.available.len()
297    }
298}
299
300impl<T> Default for ScratchVecPool<T> {
301    fn default() -> Self {
302        Self {
303            available: Vec::new(),
304        }
305    }
306}
307
308#[derive(Default)]
309pub(crate) struct FrameLayoutArena {
310    tmp_child_ids: ScratchVecPool<NodeId>,
311    tmp_placements: ScratchVecPool<Placement>,
312}
313
314#[cfg(test)]
315impl FrameLayoutArena {
316    pub(crate) fn available_placement_scratch_count(&self) -> usize {
317        self.tmp_placements.available_count()
318    }
319
320    pub(crate) fn seed_placement_scratch_for_test(&mut self) {
321        self.tmp_placements.release(Vec::with_capacity(1));
322    }
323}
324
325/// Discrete event callback reference produced during semantics extraction.
326#[derive(Clone, Debug, PartialEq, Eq)]
327pub struct SemanticsCallback {
328    node_id: NodeId,
329}
330
331impl SemanticsCallback {
332    pub fn new(node_id: NodeId) -> Self {
333        Self { node_id }
334    }
335
336    pub fn node_id(&self) -> NodeId {
337        self.node_id
338    }
339}
340
341/// Semantics action exposed to the input system.
342#[derive(Clone, Debug, PartialEq, Eq)]
343pub enum SemanticsAction {
344    Click { handler: SemanticsCallback },
345}
346
347/// A text node's text in the semantics tree, shared with the node's modifier
348/// slices rather than copied out of them.
349#[derive(Clone, Debug)]
350pub struct SemanticsText(Rc<crate::text::AnnotatedString>);
351
352impl SemanticsText {
353    /// The text the node shows.
354    pub fn as_str(&self) -> &str {
355        &self.0.text
356    }
357}
358
359impl PartialEq for SemanticsText {
360    fn eq(&self, other: &Self) -> bool {
361        self.as_str() == other.as_str()
362    }
363}
364
365impl Eq for SemanticsText {}
366
367impl From<&str> for SemanticsText {
368    fn from(text: &str) -> Self {
369        Self(Rc::new(crate::text::AnnotatedString::from(text)))
370    }
371}
372
373/// Semantic role describing how a node should participate in accessibility and hit testing.
374/// Roles are now derived from SemanticsConfiguration rather than widget types.
375#[derive(Clone, Debug, PartialEq, Eq)]
376pub enum SemanticsRole {
377    /// Generic container or layout node
378    Layout,
379    /// Subcomposition boundary
380    Subcompose,
381    /// Text content derived from the text node semantics payload.
382    Text { value: SemanticsText },
383    /// Spacer (non-interactive)
384    Spacer,
385    /// Button (derived from the semantics `role`)
386    Button,
387    /// Unknown or unspecified role
388    Unknown,
389}
390
391/// A single node within the semantics tree.
392///
393/// Not `Eq`: a node now carries drawn-control bounds, which are floats. The
394/// tree is compared for "did anything a screen reader can see change", and
395/// that comparison is `PartialEq` everywhere else on the accessibility path
396/// (`AccessibilityRect`, `AccessibilityElement`) for the same reason.
397#[derive(Clone, Debug, PartialEq)]
398pub struct SemanticsNode {
399    pub node_id: NodeId,
400    /// Where the node lies in the root's coordinates: its layout rect,
401    /// before graphics-layer transforms, as [`LayoutBox::rect`] holds it.
402    pub bounds: GeometryRect,
403    /// Where layout put the node inside its parent's content, from which a
404    /// tree update moves a node that did not change along with its parent.
405    pub placement: SemanticsPlacement,
406    /// Incarnation of the runtime node, incremented when its storage is recycled.
407    pub node_generation: u32,
408    /// Where this node sits in the tree (layout, text, subcomposition …).
409    pub role: SemanticsRole,
410    /// What kind of control this node is, as a screen reader announces it —
411    /// Compose's `Role`. Separate from [`SemanticsNode::role`] because a
412    /// clickable `Row` is structurally a layout node and semantically a button.
413    pub widget_role: Option<SemanticsWidgetRole>,
414    pub actions: Vec<SemanticsAction>,
415    pub children: Vec<SemanticsNode>,
416    pub description: Option<String>,
417    /// Direct activation callback for keyboard and assistive technology.
418    pub on_click: Option<SemanticsCustomAction>,
419    pub selected: Option<bool>,
420    pub toggled: Option<bool>,
421    pub enabled: bool,
422    /// Whether a screen reader skips this node and everything under it.
423    pub hidden: bool,
424    /// Whether a screen reader takes this node and the text under it as one
425    /// stop.
426    pub merge_descendants: bool,
427    /// Where a screen reader visits this node among the ones beside it.
428    pub traversal_index: f32,
429    /// The text an editable field holds.
430    pub text: Option<String>,
431    /// Whether this node registered a focus target, so focus can land on it.
432    /// Compose's `SemanticsProperties.Focused` companion.
433    pub focusable: bool,
434    /// Whether focus sits on this node right now.
435    pub focused: bool,
436    /// The properties few nodes set, held only when the node sets one: read
437    /// them through [`SemanticsNode::details`] and write them through
438    /// [`SemanticsNode::set_details`].
439    pub details: Option<Box<SemanticsDetails>>,
440}
441
442impl Default for SemanticsNode {
443    fn default() -> Self {
444        Self {
445            node_id: 0,
446            bounds: GeometryRect::EMPTY,
447            placement: SemanticsPlacement::default(),
448            node_generation: 0,
449            role: SemanticsRole::Unknown,
450            widget_role: None,
451            actions: Vec::new(),
452            children: Vec::new(),
453            description: None,
454            on_click: None,
455            selected: None,
456            toggled: None,
457            enabled: true,
458            hidden: false,
459            merge_descendants: false,
460            traversal_index: 0.0,
461            text: None,
462            focusable: false,
463            focused: false,
464            details: None,
465        }
466    }
467}
468
469/// Where layout put a semantics node: its offset inside its parent's
470/// content and where its own content starts inside it.
471#[derive(Clone, Copy, Debug, Default, PartialEq)]
472pub struct SemanticsPlacement {
473    position: Point,
474    content_offset: Point,
475}
476
477impl SemanticsPlacement {
478    fn of(state: &LayoutState) -> Self {
479        Self {
480            position: state.position(),
481            content_offset: state.content_offset(),
482        }
483    }
484
485    fn place(self, origin: Point) -> (Point, Point) {
486        let top_left = Point {
487            x: origin.x + self.position.x,
488            y: origin.y + self.position.y,
489        };
490        let content = Point {
491            x: top_left.x + self.content_offset.x,
492            y: top_left.y + self.content_offset.y,
493        };
494        (top_left, content)
495    }
496}
497
498/// Rooted semantics tree extracted after layout.
499///
500/// Two trees are equal when they hold the same nodes; what a tree keeps to
501/// bring itself up to date is not compared.
502#[derive(Clone, Debug)]
503pub struct SemanticsTree {
504    full: SemanticsNode,
505    modal: Option<Vec<usize>>,
506    tracking: semantics_update::SemanticsTracking,
507}
508
509impl PartialEq for SemanticsTree {
510    fn eq(&self, other: &Self) -> bool {
511        self.full == other.full && self.modal == other.modal
512    }
513}
514
515impl SemanticsTree {
516    fn new(full: SemanticsNode) -> Self {
517        let modal = top_modal_path(&full);
518        Self {
519            full,
520            modal,
521            tracking: semantics_update::SemanticsTracking::default(),
522        }
523    }
524
525    /// Returns the top visible modal subtree, or the full root when no modal is open.
526    pub fn root(&self) -> &SemanticsNode {
527        self.modal.as_deref().map_or(&self.full, |path| {
528            path.iter()
529                .try_fold(&self.full, |node, &index| node.children.get(index))
530                .unwrap_or(&self.full)
531        })
532    }
533}
534
535/// Whether a modal of `size` takes space; a zero-sized modal hides nothing.
536fn modal_takes_space(size: Size) -> bool {
537    size.width > 0.0 && size.height > 0.0 && size.width.is_finite() && size.height.is_finite()
538}
539
540/// The path to the modal drawn on top: the last visible modal in document
541/// order, a modal inside another preferred to it.
542fn top_modal_path(root: &SemanticsNode) -> Option<Vec<usize>> {
543    fn search(node: &SemanticsNode, path: &mut Vec<usize>) -> bool {
544        if node.hidden {
545            return false;
546        }
547        for (index, child) in node.children.iter().enumerate().rev() {
548            path.push(index);
549            if search(child, path) {
550                return true;
551            }
552            path.pop();
553        }
554        node.details().is_modal
555    }
556    let mut path = Vec::new();
557    search(root, &mut path).then_some(path)
558}
559
560#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
561pub struct LayoutAllocationDebugStats {
562    pub layout_box_count: usize,
563    pub layout_box_child_count: usize,
564    pub layout_box_child_capacity: usize,
565    pub layout_box_heap_bytes: usize,
566    pub modifier_slice_count: usize,
567    pub modifier_slice_heap_bytes: usize,
568    pub modifier_draw_command_count: usize,
569    pub modifier_draw_command_capacity: usize,
570    pub modifier_pointer_input_count: usize,
571    pub modifier_pointer_input_capacity: usize,
572    pub modifier_text_content_count: usize,
573    pub modifier_text_style_count: usize,
574    pub modifier_text_layout_options_count: usize,
575    pub modifier_prepared_text_layout_count: usize,
576    pub modifier_graphics_layer_count: usize,
577    pub modifier_graphics_layer_resolver_count: usize,
578    pub semantics_node_count: usize,
579    pub semantics_action_count: usize,
580    pub semantics_action_capacity: usize,
581    pub semantics_child_count: usize,
582    pub semantics_child_capacity: usize,
583    pub semantics_description_count: usize,
584    pub semantics_description_bytes: usize,
585    pub semantics_heap_bytes: usize,
586}
587
588impl LayoutAllocationDebugStats {
589    fn add_modifier_slice(&mut self, stats: ModifierNodeSlicesDebugStats) {
590        self.modifier_slice_count += 1;
591        self.modifier_slice_heap_bytes += stats.heap_bytes;
592        self.modifier_draw_command_count += stats.draw_command_count;
593        self.modifier_draw_command_capacity += stats.draw_command_capacity;
594        self.modifier_pointer_input_count += stats.pointer_input_count;
595        self.modifier_pointer_input_capacity += stats.pointer_input_capacity;
596        self.modifier_text_content_count += usize::from(stats.has_text_content);
597        self.modifier_text_style_count += usize::from(stats.has_text_style);
598        self.modifier_text_layout_options_count += usize::from(stats.has_text_layout_options);
599        self.modifier_prepared_text_layout_count += usize::from(stats.has_prepared_text_layout);
600        self.modifier_graphics_layer_count += usize::from(stats.has_graphics_layer);
601        self.modifier_graphics_layer_resolver_count +=
602            usize::from(stats.has_graphics_layer_resolver);
603    }
604}
605
606/// Result of running layout for a Compose tree.
607#[derive(Debug, Clone)]
608pub struct LayoutTree {
609    root: LayoutBox,
610}
611
612impl LayoutTree {
613    pub fn new(root: LayoutBox) -> Self {
614        Self { root }
615    }
616
617    pub fn root(&self) -> &LayoutBox {
618        &self.root
619    }
620
621    pub fn root_mut(&mut self) -> &mut LayoutBox {
622        &mut self.root
623    }
624
625    pub fn into_root(self) -> LayoutBox {
626        self.root
627    }
628
629    pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
630        let mut stats = LayoutAllocationDebugStats::default();
631        record_layout_box_allocation_stats(&self.root, &mut stats);
632        stats
633    }
634}
635
636/// Layout information for a single node.
637#[derive(Debug, Clone)]
638pub struct LayoutBox {
639    pub node_id: NodeId,
640    /// Incarnation of the runtime node captured with these layout bounds.
641    pub node_generation: u32,
642    pub rect: GeometryRect,
643    /// Content offset for scroll/inner transforms (applies to children, NOT this node's position)
644    pub content_offset: Point,
645    pub node_data: LayoutNodeData,
646    pub children: Vec<LayoutBox>,
647}
648
649impl LayoutBox {
650    pub fn new(
651        node_id: NodeId,
652        rect: GeometryRect,
653        content_offset: Point,
654        node_data: LayoutNodeData,
655        children: Vec<LayoutBox>,
656    ) -> Self {
657        Self {
658            node_id,
659            node_generation: 0,
660            rect,
661            content_offset,
662            node_data,
663            children,
664        }
665    }
666}
667
668/// Snapshot of the data required to render a layout node.
669#[derive(Debug, Clone)]
670pub struct LayoutNodeData {
671    /// Composable origins retained by an inspection-enabled layout node.
672    #[cfg(feature = "inspection")]
673    pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
674    pub modifier: Modifier,
675    pub resolved_modifiers: ResolvedModifiers,
676    pub modifier_slices: Rc<ModifierNodeSlices>,
677    pub semantics: Option<Rc<SemanticsConfiguration>>,
678    pub kind: LayoutNodeKind,
679}
680
681impl LayoutNodeData {
682    pub fn new(
683        modifier: Modifier,
684        resolved_modifiers: ResolvedModifiers,
685        modifier_slices: Rc<ModifierNodeSlices>,
686        semantics: Option<Rc<SemanticsConfiguration>>,
687        kind: LayoutNodeKind,
688    ) -> Self {
689        Self {
690            #[cfg(feature = "inspection")]
691            source_trace: Rc::default(),
692            modifier,
693            resolved_modifiers,
694            modifier_slices,
695            semantics,
696            kind,
697        }
698    }
699
700    /// Semantics the node's live modifier chain reported when the snapshot
701    /// was taken.
702    pub fn semantics(&self) -> Option<&SemanticsConfiguration> {
703        self.semantics.as_deref()
704    }
705
706    pub fn resolved_modifiers(&self) -> ResolvedModifiers {
707        self.resolved_modifiers
708    }
709
710    pub fn modifier_slices(&self) -> &ModifierNodeSlices {
711        &self.modifier_slices
712    }
713}
714
715/// Classification of the node captured inside a [`LayoutBox`].
716///
717/// Note: Text content is no longer represented as a distinct LayoutNodeKind.
718/// Text nodes now use `LayoutNodeKind::Layout` with their content stored in
719/// `modifier_slices.text_content()` via TextModifierNode, following Jetpack
720/// Compose's pattern where text is a modifier node capability.
721#[derive(Clone)]
722pub enum LayoutNodeKind {
723    Layout,
724    Subcompose,
725    Spacer,
726    Button { on_click: Rc<RefCell<dyn FnMut()>> },
727    Unknown,
728}
729
730impl fmt::Debug for LayoutNodeKind {
731    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
732        match self {
733            LayoutNodeKind::Layout => f.write_str("Layout"),
734            LayoutNodeKind::Subcompose => f.write_str("Subcompose"),
735            LayoutNodeKind::Spacer => f.write_str("Spacer"),
736            LayoutNodeKind::Button { .. } => f.write_str("Button"),
737            LayoutNodeKind::Unknown => f.write_str("Unknown"),
738        }
739    }
740}
741
742/// Extension trait that equips `MemoryApplier` with layout computation.
743pub trait LayoutEngine {
744    fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError>;
745}
746
747impl LayoutEngine for MemoryApplier {
748    fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError> {
749        let measurements = measure_layout(self, root, max_size)?;
750        measurements
751            .into_layout_tree()
752            .ok_or(NodeError::MissingContext {
753                id: root,
754                reason: "layout tree was not requested",
755            })
756    }
757}
758
759/// Result of running the measure pass for a Compose layout tree.
760#[derive(Debug, Clone)]
761pub struct LayoutMeasurements {
762    root: Rc<MeasuredNode>,
763    semantics: Option<SemanticsTree>,
764    layout_tree: Option<LayoutTree>,
765}
766
767impl LayoutMeasurements {
768    fn new(
769        root: Rc<MeasuredNode>,
770        semantics: Option<SemanticsTree>,
771        layout_tree: Option<LayoutTree>,
772    ) -> Self {
773        Self {
774            root,
775            semantics,
776            layout_tree,
777        }
778    }
779
780    /// Returns the measured size of the root node.
781    pub fn root_size(&self) -> Size {
782        self.root.size
783    }
784
785    pub fn semantics_tree(&self) -> Option<&SemanticsTree> {
786        self.semantics.as_ref()
787    }
788
789    pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
790        let mut stats = self
791            .layout_tree
792            .as_ref()
793            .map(LayoutTree::debug_allocation_stats)
794            .unwrap_or_default();
795        if let Some(semantics) = &self.semantics {
796            record_semantics_allocation_stats(semantics.root(), &mut stats);
797        }
798        stats
799    }
800
801    /// Consumes the measurements and returns the built [`LayoutTree`], if requested.
802    pub fn into_layout_tree(self) -> Option<LayoutTree> {
803        self.layout_tree
804    }
805
806    /// Returns a cloned [`LayoutTree`] for rendering/debug consumers, if requested.
807    pub fn layout_tree(&self) -> Option<LayoutTree> {
808        self.layout_tree.clone()
809    }
810}
811
812/// Builds a semantics tree from an existing [`LayoutTree`].
813///
814/// This is useful for consumers that need semantics on demand without forcing
815/// every layout pass to eagerly allocate a full [`SemanticsTree`].
816pub fn build_semantics_tree_from_layout_tree(layout_tree: &LayoutTree) -> SemanticsTree {
817    SemanticsTree::new(build_semantics_node_from_layout_box(
818        layout_tree.root(),
819        Point::default(),
820    ))
821}
822
823/// Builds a layout snapshot from retained layout state in the live applier tree.
824///
825/// Renderers use retained node state directly. This function exists for debug,
826/// robot, and tests that need an owned [`LayoutTree`] without forcing every
827/// layout pass to allocate one.
828pub fn build_layout_tree_from_applier(
829    applier: &mut MemoryApplier,
830    root: NodeId,
831) -> Result<Option<LayoutTree>, NodeError> {
832    let origin = layout_tree_origin(read_layout_node(applier, root, |state, _| state)?);
833    let mut child_stack = Vec::new();
834    place_layout_box(
835        applier,
836        root,
837        origin,
838        ProjectiveTransform::identity(),
839        &mut child_stack,
840    )
841    .map(|root| root.map(LayoutTree::new))
842}
843
844/// Whether any box placed under `root` has area, as
845/// [`build_layout_tree_from_applier`] would place it: the walk stops at the
846/// first such box and reads no node's modifiers or semantics.
847pub fn has_placed_content(applier: &mut MemoryApplier, root: NodeId) -> Result<bool, NodeError> {
848    walk_placed_boxes(applier, root, |_| std::ops::ControlFlow::Break(()))
849}
850
851/// The far right and bottom edges of the boxes with area placed under `root`,
852/// measured from the root's origin as [`build_layout_tree_from_applier`]
853/// places them, reading no node's modifiers or semantics. `None` while
854/// nothing under `root` has area.
855pub fn placed_content_extent(
856    applier: &mut MemoryApplier,
857    root: NodeId,
858) -> Result<Option<Size>, NodeError> {
859    let mut extent: Option<Size> = None;
860    walk_placed_boxes(applier, root, |rect| {
861        let (right, bottom) = (rect.x + rect.width, rect.y + rect.height);
862        extent = Some(extent.map_or_else(
863            || Size::new(right, bottom),
864            |extent| Size::new(extent.width.max(right), extent.height.max(bottom)),
865        ));
866        std::ops::ControlFlow::Continue(())
867    })?;
868    Ok(extent)
869}
870
871/// Hands `visit` every box with area placed under `root`, the root left out,
872/// at the rect [`build_layout_tree_from_applier`] gives it, skipping a window
873/// root's subtree and an unplaced node's, until `visit` breaks. Answers
874/// whether it did.
875fn walk_placed_boxes(
876    applier: &mut MemoryApplier,
877    root: NodeId,
878    mut visit: impl FnMut(GeometryRect) -> std::ops::ControlFlow<()>,
879) -> Result<bool, NodeError> {
880    let mut pending: Vec<(NodeId, Point)> = Vec::new();
881    let placed = read_layout_node(applier, root, |state, children| {
882        if state.is_placed() {
883            pending.extend(
884                children
885                    .iter()
886                    .map(|&child| (child, state.content_offset())),
887            );
888        }
889    })?;
890    if placed.is_none() {
891        return Ok(false);
892    }
893    while let Some((node_id, origin)) = pending.pop() {
894        if crate::modifier::is_window_root(applier, node_id) {
895            continue;
896        }
897        let rect = read_layout_node(applier, node_id, |state, children| {
898            if !state.is_placed() {
899                return None;
900            }
901            let (rect, content) = semantics_placement(&state, Some(origin));
902            pending.extend(children.iter().map(|&child| (child, content)));
903            Some(rect)
904        })?
905        .flatten();
906        if let Some(rect) = rect
907            && rect.width > 0.0
908            && rect.height > 0.0
909            && visit(rect).is_break()
910        {
911            return Ok(true);
912        }
913    }
914    Ok(false)
915}
916
917fn layout_tree_origin(root: Option<LayoutState>) -> Point {
918    let Some(state) = root else {
919        return Point::default();
920    };
921    let position = state.position();
922    Point {
923        x: -position.x,
924        y: -position.y,
925    }
926}
927
928/// Places the root at the origin, the one placement no parent makes, and
929/// names what the pass left unplaced.
930fn finish_placement(applier: &mut MemoryApplier, root: NodeId) {
931    if applier
932        .with_node::<LayoutNode, _>(root, |node| node.set_position(Point::default()))
933        .is_err()
934    {
935        let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
936            node.set_position(Point::default());
937        });
938    }
939    if let Some(pass) = placement_pass_with_unplaced_nodes() {
940        record_unplaced_parents(applier, root, pass);
941    }
942}
943
944/// Names to the scene phase the parent of every node `pass` found placed and
945/// left unplaced: nothing about the node itself changed, so the geometry
946/// setters had nothing to report, yet its layer has to leave the scene. A
947/// pass unplaces nodes rarely, so it pays for this walk only when it did.
948fn record_unplaced_parents(applier: &mut MemoryApplier, root: NodeId, pass: u64) {
949    let mut parents = vec![root];
950    let mut children = Vec::new();
951    while let Some(parent) = parents.pop() {
952        children.clear();
953        if !matches!(
954            read_layout_node(applier, parent, |_, ids| children.extend_from_slice(ids)),
955            Ok(Some(()))
956        ) {
957            continue;
958        }
959        for &child in &children {
960            match read_layout_node(applier, child, |state, _| state.unplaced_in(pass)) {
961                Ok(Some(true)) => crate::render_state::record_geometry_scene_node(parent),
962                Ok(Some(false)) => parents.push(child),
963                Ok(None) | Err(_) => {}
964            }
965        }
966    }
967}
968
969fn read_layout_node<R>(
970    applier: &mut MemoryApplier,
971    node_id: NodeId,
972    mut read: impl FnMut(LayoutState, &[NodeId]) -> R,
973) -> Result<Option<R>, NodeError> {
974    read_live_layout_node(applier, node_id, |node| {
975        node.with_children(|children| read(node.state(), children))
976    })
977}
978
979enum LiveLayoutNode<'a> {
980    Layout(&'a LayoutNode),
981    Subcompose(&'a SubcomposeLayoutNode),
982}
983
984impl LiveLayoutNode<'_> {
985    fn state(&self) -> LayoutState {
986        match self {
987            Self::Layout(node) => node.layout_state(),
988            Self::Subcompose(node) => node.layout_state(),
989        }
990    }
991
992    fn parent(&self) -> Option<NodeId> {
993        match self {
994            Self::Layout(node) => node.parent(),
995            Self::Subcompose(node) => node.parent(),
996        }
997    }
998
999    fn is_window_root(&self) -> bool {
1000        matches!(self, Self::Layout(node) if node.is_window_root())
1001    }
1002
1003    fn with_children<R>(&self, read: impl FnOnce(&[NodeId]) -> R) -> R {
1004        match self {
1005            Self::Layout(node) => read(&node.children),
1006            Self::Subcompose(node) => node.with_active_children(read),
1007        }
1008    }
1009
1010    fn semantics(&self) -> Option<SemanticsConfiguration> {
1011        match self {
1012            Self::Layout(node) => node.semantics_configuration(),
1013            Self::Subcompose(node) => node.semantics_configuration(),
1014        }
1015    }
1016}
1017
1018fn read_live_layout_node<R>(
1019    applier: &mut MemoryApplier,
1020    node_id: NodeId,
1021    mut read: impl FnMut(LiveLayoutNode<'_>) -> R,
1022) -> Result<Option<R>, NodeError> {
1023    match applier.with_node::<LayoutNode, _>(node_id, |node| read(LiveLayoutNode::Layout(node))) {
1024        Ok(value) => return Ok(Some(value)),
1025        Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {}
1026        Err(err) => return Err(err),
1027    }
1028    match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1029        read(LiveLayoutNode::Subcompose(node))
1030    }) {
1031        Ok(value) => Ok(Some(value)),
1032        Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1033        Err(err) => Err(err),
1034    }
1035}
1036
1037fn snapshot_node_data(
1038    applier: &mut MemoryApplier,
1039    node_id: NodeId,
1040    top_left: Point,
1041    size: Size,
1042    parent_transform: ProjectiveTransform,
1043) -> Result<(LayoutNodeData, ProjectiveTransform), NodeError> {
1044    let info = runtime_metadata_for(applier, node_id)?;
1045    let kind = layout_kind_from_metadata(node_id, &info);
1046    let RuntimeNodeMetadata {
1047        modifier,
1048        resolved_modifiers,
1049        modifier_slices,
1050        semantics,
1051        ..
1052    } = info;
1053
1054    let window_transform = modifier_slices
1055        .graphics_layer()
1056        .map_or(parent_transform, |layer| {
1057            layer_transform_to_window(
1058                parent_transform,
1059                top_left,
1060                modifier_slices.layer_bounds(size),
1061                &layer,
1062            )
1063        });
1064    modifier_slices.publish_window_geometry(top_left, window_transform, size);
1065
1066    let data = LayoutNodeData::new(
1067        modifier,
1068        resolved_modifiers,
1069        modifier_slices,
1070        semantics,
1071        kind,
1072    );
1073    #[cfg(feature = "inspection")]
1074    let data = {
1075        let mut data = data;
1076        data.source_trace = applier
1077            .with_node::<LayoutNode, _>(node_id, |node| node.source_trace.clone())
1078            .unwrap_or_default();
1079        data
1080    };
1081    Ok((data, window_transform))
1082}
1083
1084/// Places `node_id`'s box and its subtree. `child_stack` is shared by the
1085/// whole walk: each node pushes its children above its parent's, reads them
1086/// from there while its descendants push and pop above them, and pops them
1087/// when done, so no node's child list is copied out of the applier.
1088fn place_layout_box(
1089    applier: &mut MemoryApplier,
1090    node_id: NodeId,
1091    parent_content_origin: Point,
1092    parent_transform: ProjectiveTransform,
1093    child_stack: &mut Vec<NodeId>,
1094) -> Result<Option<LayoutBox>, NodeError> {
1095    let first_child = child_stack.len();
1096    let Some(state) = read_layout_node(applier, node_id, |state, children| {
1097        if state.is_placed() {
1098            child_stack.extend_from_slice(children);
1099        }
1100        state
1101    })?
1102    else {
1103        return Ok(None);
1104    };
1105    if !state.is_placed() {
1106        return Ok(None);
1107    }
1108
1109    let top_left = Point {
1110        x: parent_content_origin.x + state.position().x,
1111        y: parent_content_origin.y + state.position().y,
1112    };
1113    let rect = GeometryRect {
1114        x: top_left.x,
1115        y: top_left.y,
1116        width: state.size().width,
1117        height: state.size().height,
1118    };
1119    let (data, window_transform) =
1120        snapshot_node_data(applier, node_id, top_left, state.size(), parent_transform)?;
1121    let child_origin = Point {
1122        x: top_left.x + state.content_offset().x,
1123        y: top_left.y + state.content_offset().y,
1124    };
1125    let end = child_stack.len();
1126    let mut children = Vec::with_capacity(end - first_child);
1127    for index in first_child..end {
1128        let child_id = child_stack[index];
1129        if crate::modifier::is_window_root(applier, child_id) {
1130            continue;
1131        }
1132        if let Some(child) = place_layout_box(
1133            applier,
1134            child_id,
1135            child_origin,
1136            window_transform,
1137            child_stack,
1138        )? {
1139            children.push(child);
1140        }
1141    }
1142    child_stack.truncate(first_child);
1143
1144    Ok(Some(LayoutBox {
1145        node_generation: applier.node_generation(node_id),
1146        ..LayoutBox::new(node_id, rect, state.content_offset(), data, children)
1147    }))
1148}
1149
1150/// The modal the semantics tree of `root` would be rooted at, found without
1151/// building the tree: the topmost placed, visible modal that takes space,
1152/// or `None` when no modal is open. Unlike a tree update, it leaves the
1153/// nodes' semantics dirty flags alone.
1154pub fn top_modal_from_applier(
1155    applier: &mut MemoryApplier,
1156    root: NodeId,
1157) -> Result<Option<NodeId>, NodeError> {
1158    // One depth-first walk on a shared stack, last child first, with a
1159    // modal's own step below its children's: the first modal step popped is
1160    // the modal drawn on top. It runs every frame, so no node's children are
1161    // copied and each node is looked up once.
1162    enum Step {
1163        Enter { node: NodeId, child: bool },
1164        Modal(NodeId),
1165    }
1166
1167    fn push_placed(
1168        steps: &mut Vec<Step>,
1169        node: NodeId,
1170        reach: cranpose_foundation::SemanticsReach,
1171        size: Size,
1172        children: impl IntoIterator<Item = NodeId>,
1173    ) {
1174        if reach.hidden {
1175            return;
1176        }
1177        if reach.is_modal && modal_takes_space(size) {
1178            steps.push(Step::Modal(node));
1179        }
1180        steps.extend(
1181            children
1182                .into_iter()
1183                .map(|node| Step::Enter { node, child: true }),
1184        );
1185    }
1186
1187    let mut steps = vec![Step::Enter {
1188        node: root,
1189        child: false,
1190    }];
1191    while let Some(step) = steps.pop() {
1192        let (node_id, child) = match step {
1193            Step::Modal(node) => return Ok(Some(node)),
1194            Step::Enter { node, child } => (node, child),
1195        };
1196        let node = match applier.get_mut(node_id) {
1197            Ok(node) => node.as_any_mut(),
1198            Err(NodeError::Missing { .. }) => continue,
1199            Err(error) => return Err(error),
1200        };
1201        if let Some(layout) = node.downcast_mut::<LayoutNode>() {
1202            let state = layout.layout_state();
1203            if state.is_placed() && !(child && layout.is_window_root()) {
1204                let children = layout.children.iter().copied();
1205                push_placed(
1206                    &mut steps,
1207                    node_id,
1208                    layout.semantics_reach(),
1209                    state.size(),
1210                    children,
1211                );
1212            }
1213        } else if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
1214            let state = subcompose.layout_state();
1215            if state.is_placed() {
1216                let reach = subcompose.semantics_reach();
1217                subcompose.with_active_children(|children| {
1218                    push_placed(
1219                        &mut steps,
1220                        node_id,
1221                        reach,
1222                        state.size(),
1223                        children.iter().copied(),
1224                    );
1225                });
1226            }
1227        }
1228    }
1229    Ok(None)
1230}
1231
1232#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1233pub struct MeasureLayoutOptions {
1234    pub collect_semantics: bool,
1235    pub build_layout_tree: bool,
1236}
1237
1238impl Default for MeasureLayoutOptions {
1239    fn default() -> Self {
1240        Self {
1241            collect_semantics: true,
1242            build_layout_tree: true,
1243        }
1244    }
1245}
1246
1247/// Check if a node or any of its descendants needs measure (selective measure optimization).
1248/// This can be used by the app shell to skip layout when the tree is clean.
1249///
1250/// O(1) check - just looks at root's dirty flag.
1251/// Works because all mutation paths bubble dirty flags to root via composer commands.
1252///
1253/// Returns Result to force caller to handle errors explicitly. No more unwrap_or(true) safety net.
1254pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1255    Ok(applier.get_mut(root)?.layout_dirty())
1256}
1257
1258/// Check if the root semantics snapshot is dirty.
1259///
1260/// Semantics invalidations bubble to the root the same way layout invalidations do,
1261/// so a root check is sufficient to determine whether the next layout pass needs to
1262/// rebuild semantic data even when geometry is otherwise unchanged.
1263pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1264    Ok(applier.get_mut(root)?.needs_semantics())
1265}
1266
1267#[cfg(test)]
1268pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
1269    cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1270}
1271
1272/// Runs the measure phase for the subtree rooted at `root`.
1273pub fn measure_layout(
1274    applier: &mut MemoryApplier,
1275    root: NodeId,
1276    max_size: Size,
1277) -> Result<LayoutMeasurements, NodeError> {
1278    measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
1279}
1280
1281pub fn measure_layout_with_options(
1282    applier: &mut MemoryApplier,
1283    root: NodeId,
1284    max_size: Size,
1285    options: MeasureLayoutOptions,
1286) -> Result<LayoutMeasurements, NodeError> {
1287    let telemetry_start = Instant::now();
1288    crate::render_state::begin_text_layout_pass();
1289    begin_placement_pass();
1290    process_pending_layout_repasses(applier, root)?;
1291    let after_repasses = Instant::now();
1292
1293    let constraints = Constraints {
1294        min_width: 0.0,
1295        max_width: max_size.width,
1296        min_height: 0.0,
1297        max_height: max_size.height,
1298    };
1299
1300    let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
1301        .with_node::<LayoutNode, _>(root, |node| {
1302            (
1303                node.needs_measure() || Node::descendant_needs_measure(node),
1304                node.needs_semantics(),
1305                node.cache_handles().epoch(),
1306            )
1307        }) {
1308        Ok(tuple) => tuple,
1309        Err(NodeError::TypeMismatch { .. }) => {
1310            let node = applier.get_mut(root)?;
1311            let measure_dirty = node.needs_measure() || node.descendant_needs_measure();
1312            let semantics_dirty = node.needs_semantics();
1313            (measure_dirty, semantics_dirty, 0)
1314        }
1315        Err(err) => return Err(err),
1316    };
1317
1318    let epoch = if needs_remeasure {
1319        crate::render_state::next_layout_cache_epoch()
1320    } else if cached_epoch != 0 {
1321        cached_epoch
1322    } else {
1323        crate::render_state::current_layout_cache_epoch()
1324    };
1325
1326    let guard = ApplierSlotGuard::new(applier);
1327    let applier_host = guard.host();
1328    let slots_handle = guard.slots_handle();
1329    let after_guard = Instant::now();
1330
1331    let frame_arena = crate::render_state::take_layout_frame_arena();
1332    let builder = LayoutBuilder::new_with_epoch(
1333        Rc::clone(&applier_host),
1334        epoch,
1335        Rc::clone(&slots_handle),
1336        frame_arena,
1337    );
1338    let after_builder = Instant::now();
1339
1340    let measured = builder
1341        .state
1342        .measure_node(root, normalize_constraints(constraints))?;
1343    let after_measure = Instant::now();
1344
1345    if let Ok(mut applier) = applier_host.try_borrow_typed() {
1346        finish_placement(&mut applier, root);
1347    }
1348    let after_root_place = Instant::now();
1349
1350    let (layout_tree, semantics) = {
1351        let mut applier_ref = applier_host.borrow_typed();
1352        let layout_tree = if options.build_layout_tree {
1353            Some(build_layout_tree(&mut applier_ref, &measured)?)
1354        } else {
1355            None
1356        };
1357        let semantics = if options.collect_semantics {
1358            let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
1359                clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
1360                build_semantics_tree_from_layout_tree(layout_tree)
1361            } else {
1362                build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
1363            };
1364            Some(semantics_tree)
1365        } else {
1366            None
1367        };
1368        (layout_tree, semantics)
1369    };
1370    let after_aux = Instant::now();
1371
1372    drop(builder);
1373    let after_builder_drop = Instant::now();
1374
1375    drop(guard);
1376    let after_guard_drop = Instant::now();
1377
1378    log_layout_measure_telemetry(LayoutMeasureTelemetry {
1379        root,
1380        start: telemetry_start,
1381        after_repasses,
1382        after_guard,
1383        after_builder,
1384        after_measure,
1385        after_root_place,
1386        after_aux,
1387        after_builder_drop,
1388        after_guard_drop,
1389    });
1390
1391    Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
1392}
1393
1394fn process_pending_layout_repasses(
1395    applier: &mut MemoryApplier,
1396    root: NodeId,
1397) -> Result<(), NodeError> {
1398    for node_id in crate::render_state::take_modifier_slice_repass_nodes() {
1399        if let Ok(node) = applier.get_mut(node_id) {
1400            let any = node.as_any_mut();
1401            if let Some(layout) = any.downcast_mut::<crate::widgets::nodes::LayoutNode>() {
1402                layout.mark_modifier_slices_dirty();
1403            } else if let Some(subcompose) =
1404                any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1405            {
1406                subcompose.mark_modifier_slices_dirty();
1407            }
1408        }
1409    }
1410    let measure_repass_nodes = crate::take_measure_repass_nodes();
1411    let repass_nodes = crate::take_layout_repass_nodes();
1412    if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
1413        return Ok(());
1414    }
1415    for node_id in measure_repass_nodes {
1416        cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
1417    }
1418    for node_id in repass_nodes {
1419        cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1420    }
1421    applier.get_mut(root)?.mark_descendant_needs_layout(false);
1422    Ok(())
1423}
1424
1425struct LayoutBuilder {
1426    state: Rc<LayoutBuilderState>,
1427}
1428
1429impl LayoutBuilder {
1430    fn new_with_epoch(
1431        applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1432        epoch: u64,
1433        slots: Rc<RefCell<SlotTable>>,
1434        frame_arena: FrameLayoutArena,
1435    ) -> Self {
1436        Self {
1437            state: Rc::new(LayoutBuilderState::new_with_epoch(
1438                applier,
1439                epoch,
1440                slots,
1441                frame_arena,
1442            )),
1443        }
1444    }
1445}
1446
1447impl Drop for LayoutBuilder {
1448    fn drop(&mut self) {
1449        if Rc::strong_count(&self.state) != 1 {
1450            return;
1451        }
1452        let Ok(mut frame_arena) = self.state.frame_arena.try_borrow_mut() else {
1453            return;
1454        };
1455        crate::render_state::replace_layout_frame_arena(std::mem::take(&mut *frame_arena));
1456    }
1457}
1458
1459struct LayoutBuilderState {
1460    applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1461    runtime_handle: RefCell<Option<RuntimeHandle>>,
1462    slots: Rc<RefCell<SlotTable>>,
1463    cache_epoch: u64,
1464    cache_floor: u64,
1465    frame_arena: RefCell<FrameLayoutArena>,
1466}
1467
1468struct LayoutRuntimeFrameBindingCleanup<'a> {
1469    state: &'a RefCell<LayoutRuntimeState>,
1470}
1471
1472impl Drop for LayoutRuntimeFrameBindingCleanup<'_> {
1473    fn drop(&mut self) {
1474        self.state.borrow().frame.unbind();
1475    }
1476}
1477
1478enum LayoutNodeVisit<'a> {
1479    Cached(Rc<MeasuredNode>),
1480    /// Only nodes below this one changed: its measurement holds unless a
1481    /// changed child measures differently.
1482    Keep(Rc<MeasuredNode>),
1483    Measure(LayoutNodeMeasure<'a>),
1484}
1485
1486/// What measuring one changed child of a kept node again needs.
1487#[derive(Clone, Copy)]
1488struct ChangedChild {
1489    constraints: Constraints,
1490    placed: bool,
1491}
1492
1493/// What the walk over a kept measurement finds of one child.
1494enum ChildChange {
1495    Unchanged,
1496    Changed(ChangedChild),
1497    /// The node has to run its policy.
1498    Refuse,
1499}
1500
1501/// A changed child of a kept node as a layout pass finds it, from a layout
1502/// node or a subcompose node.
1503struct ChildView {
1504    self_dirty: bool,
1505    /// The child's parent data, read only when the child changed itself:
1506    /// only its own modifiers give it.
1507    parent_data: Option<cranpose_ui_layout::ParentData>,
1508    /// The constraints the child's own cache holds.
1509    cached_constraints: Option<Constraints>,
1510    read_intrinsics: bool,
1511    placed: bool,
1512}
1513
1514impl ChildView {
1515    /// The child, when it or a node below it changed.
1516    fn of(
1517        node: &dyn Node,
1518        cache: &LayoutNodeCacheHandles,
1519        props: &dyn Fn() -> crate::modifier::LayoutProperties,
1520        placed: bool,
1521    ) -> Option<Self> {
1522        let self_dirty = node.needs_measure() || node.needs_layout();
1523        if !self_dirty && !node.descendant_needs_layout() {
1524            return None;
1525        }
1526        Some(Self {
1527            self_dirty,
1528            parent_data: self_dirty.then(|| parent_data_of(props())),
1529            cached_constraints: cache.measured_constraints(),
1530            read_intrinsics: cache.has_intrinsics(),
1531            placed,
1532        })
1533    }
1534}
1535
1536struct LayoutNodeMeasure<'a> {
1537    runtime_state: Rc<RefCell<LayoutRuntimeState>>,
1538    chain: ModifierChainInputs,
1539    pools: VecPools<'a>,
1540}
1541
1542impl LayoutBuilderState {
1543    fn new_with_epoch(
1544        applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1545        epoch: u64,
1546        slots: Rc<RefCell<SlotTable>>,
1547        frame_arena: FrameLayoutArena,
1548    ) -> Self {
1549        let runtime_handle = applier.borrow_typed().runtime_handle();
1550
1551        Self {
1552            applier,
1553            runtime_handle: RefCell::new(runtime_handle),
1554            slots,
1555            cache_epoch: epoch,
1556            cache_floor: crate::render_state::layout_cache_floor(),
1557            frame_arena: RefCell::new(frame_arena),
1558        }
1559    }
1560
1561    fn with_applier_result<R>(
1562        &self,
1563        f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1564    ) -> Result<R, NodeError> {
1565        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1566            return Err(NodeError::MissingContext {
1567                id: NodeId::default(),
1568                reason: "applier already borrowed",
1569            });
1570        };
1571        f(&mut applier)
1572    }
1573
1574    fn measure_subcompose_node(
1575        self: &Rc<Self>,
1576        node_id: NodeId,
1577        constraints: Constraints,
1578    ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1579        self.clear_subcompose_placed(node_id);
1580        if let Some(cached) = self.subcompose_measurement_to_keep(node_id, constraints)
1581            && let Some(kept) = self.keep_subcompose_measurement(node_id, constraints, &cached)?
1582        {
1583            return Ok(("subcompose kept", kept));
1584        }
1585        Ok(match self.try_measure_subcompose(node_id, constraints)? {
1586            Some(measured) => {
1587                self.store_subcompose_measurement(node_id, constraints, &measured);
1588                ("subcompose", measured)
1589            }
1590            None => (
1591                "fallback",
1592                Rc::new(MeasuredNode::new(
1593                    node_id,
1594                    Size::default(),
1595                    Point { x: 0.0, y: 0.0 },
1596                    Point::default(),
1597                    Vec::new(),
1598                )),
1599            ),
1600        })
1601    }
1602
1603    /// The measurement of a subcompose node whose own inputs did not change
1604    /// since it measured at `constraints`, as for a layout node: its policy
1605    /// does not run, so its content does not compose again.
1606    fn subcompose_measurement_to_keep(
1607        &self,
1608        node_id: NodeId,
1609        constraints: Constraints,
1610    ) -> Option<Rc<MeasuredNode>> {
1611        let mut applier = self.applier.try_borrow_typed().ok()?;
1612        applier
1613            .with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1614                if node.needs_measure() || Node::needs_layout(node) {
1615                    return None;
1616                }
1617                let cache = node.cache_handles();
1618                if cache.epoch() < self.cache_floor {
1619                    return None;
1620                }
1621                cache.get_measurement(constraints)
1622            })
1623            .ok()
1624            .flatten()
1625    }
1626
1627    /// Keeps a subcompose node's measurement as [`Self::keep_measurement`]
1628    /// keeps a layout node's. A subcompose node holds no record of what its
1629    /// policy gave each child, so a changed child must have only changed
1630    /// nodes below it, and measures again at the constraints its own cache
1631    /// holds.
1632    fn keep_subcompose_measurement(
1633        self: &Rc<Self>,
1634        node_id: NodeId,
1635        constraints: Constraints,
1636        cached: &Rc<MeasuredNode>,
1637    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1638        let kept = self.remeasure_changed_children(
1639            cached,
1640            |child_id| self.subcompose_child_change(child_id),
1641            |child_id| self.mark_measured_without_policy(child_id),
1642        )?;
1643        let Some(kept) = kept else {
1644            return Ok(None);
1645        };
1646        self.with_applier_result(|applier| {
1647            applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1648                node.cache_handles()
1649                    .store_measurement(constraints, Rc::clone(&kept));
1650                node.clear_needs_layout();
1651            })
1652        })?;
1653        Ok(Some(kept))
1654    }
1655
1656    /// What the walk over a kept subcompose node finds of a child. A changed
1657    /// child refuses the keep when it changed itself, a parent read its
1658    /// intrinsic sizes, or its cache holds no constraints.
1659    fn subcompose_child_change(&self, child_id: NodeId) -> Result<ChildChange, NodeError> {
1660        self.child_change(child_id, |view| {
1661            match view.cached_constraints.filter(|_| !view.self_dirty) {
1662                Some(constraints) if !view.read_intrinsics => ChildChange::Changed(ChangedChild {
1663                    constraints,
1664                    placed: view.placed,
1665                }),
1666                _ => ChildChange::Refuse,
1667            }
1668        })
1669    }
1670
1671    /// What the walk finds of a child: unchanged when neither it nor a node
1672    /// below it changed, otherwise what `decide` makes of it.
1673    fn child_change(
1674        &self,
1675        child_id: NodeId,
1676        decide: impl FnOnce(ChildView) -> ChildChange,
1677    ) -> Result<ChildChange, NodeError> {
1678        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1679            return Ok(ChildChange::Refuse);
1680        };
1681        Ok(
1682            match read_layout_child(&mut applier, child_id, ChildView::of)? {
1683                None => ChildChange::Refuse,
1684                Some(None) => ChildChange::Unchanged,
1685                Some(Some(view)) => decide(view),
1686            },
1687        )
1688    }
1689
1690    /// Marks a child a subcompose node measured again without its policy, so
1691    /// the policy checks it when it next runs: it may keep measurements of
1692    /// its own, as a lazy list keeps its items'.
1693    fn mark_measured_without_policy(&self, child_id: NodeId) {
1694        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1695            return;
1696        };
1697        if let Ok(node) = applier.get_mut(child_id) {
1698            node.mark_descendant_needs_layout(true);
1699        }
1700    }
1701
1702    fn clear_subcompose_placed(&self, node_id: NodeId) {
1703        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1704            return;
1705        };
1706        let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1707            node.clear_placed();
1708        });
1709    }
1710
1711    fn measure_node(
1712        self: &Rc<Self>,
1713        node_id: NodeId,
1714        constraints: Constraints,
1715    ) -> Result<Rc<MeasuredNode>, NodeError> {
1716        let telemetry = layout_measure_telemetry_threshold_ms().map(|ms| (ms, Instant::now()));
1717        let (kind, measured) =
1718            if let Some(measured) = self.measure_layout_node(node_id, constraints)? {
1719                ("layout", measured)
1720            } else {
1721                self.measure_subcompose_node(node_id, constraints)?
1722            };
1723        if let Some(telemetry) = telemetry {
1724            log_node_measure_telemetry(kind, node_id, constraints, &measured, telemetry);
1725        }
1726        Ok(measured)
1727    }
1728
1729    /// The measurement `node_id` cached for `constraints` in layout cache
1730    /// epoch `current_epoch`, the one the app context holds.
1731    fn cached_measure_node_with_applier(
1732        applier: &mut MemoryApplier,
1733        node_id: NodeId,
1734        constraints: Constraints,
1735        current_epoch: u64,
1736    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1737        let served = |cache: &LayoutNodeCacheHandles, dirty: bool| {
1738            let epoch = cache.epoch();
1739            if dirty || epoch == 0 || epoch != current_epoch {
1740                return None;
1741            }
1742            cache.get_measurement(constraints)
1743        };
1744
1745        match applier.with_node::<LayoutNode, _>(node_id, |node| {
1746            let measured = served(node.cache_handles(), Node::layout_dirty(node))?;
1747            node.set_measured_size(measured.size);
1748            Some(measured)
1749        }) {
1750            Ok(measured) => Ok(measured),
1751            Err(NodeError::TypeMismatch { .. }) => {
1752                match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1753                    let measured = served(node.cache_handles(), Node::layout_dirty(node))?;
1754                    node.set_measured_size(measured.size);
1755                    Some(measured)
1756                }) {
1757                    Ok(measured) => Ok(measured),
1758                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1759                    Err(err) => Err(err),
1760                }
1761            }
1762            Err(NodeError::Missing { .. }) => Ok(None),
1763            Err(err) => Err(err),
1764        }
1765    }
1766
1767    fn try_measure_subcompose(
1768        self: &Rc<Self>,
1769        node_id: NodeId,
1770        constraints: Constraints,
1771    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1772        let (node_handle, resolved_modifiers) = {
1773            let Ok(mut applier) = self.applier.try_borrow_typed() else {
1774                return Ok(None);
1775            };
1776            let node = match applier.get_mut(node_id) {
1777                Ok(node) => node,
1778                Err(NodeError::Missing { .. }) => return Ok(None),
1779                Err(err) => return Err(err),
1780            };
1781            let any = node.as_any_mut();
1782            if let Some(subcompose) =
1783                any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1784            {
1785                let handle = subcompose.handle();
1786                let resolved_modifiers = handle
1787                    .resolved_modifiers()
1788                    .on_device_grid(subcompose.density().density());
1789                (handle, resolved_modifiers)
1790            } else {
1791                return Ok(None);
1792            }
1793        };
1794
1795        let runtime_handle = {
1796            let mut runtime_handle = self.runtime_handle.borrow_mut();
1797            if runtime_handle.is_none()
1798                && let Ok(applier) = self.applier.try_borrow_typed()
1799            {
1800                *runtime_handle = applier.runtime_handle();
1801            }
1802            runtime_handle.clone().ok_or(NodeError::MissingContext {
1803                id: node_id,
1804                reason: "runtime handle required for subcomposition",
1805            })?
1806        };
1807
1808        let props = resolved_modifiers.layout_properties();
1809        let padding = resolved_modifiers.padding();
1810        let offset = resolved_modifiers.offset();
1811        let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1812
1813        if let DimensionConstraint::Points(width) = props.width() {
1814            let constrained_width = width - padding.horizontal_sum();
1815            inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1816            inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1817        }
1818        if let DimensionConstraint::Points(height) = props.height() {
1819            let constrained_height = height - padding.vertical_sum();
1820            inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1821            inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1822        }
1823
1824        let mut slots_guard = SlotsGuard::take(&self.slots);
1825        let slots_host = slots_guard.host();
1826        let applier_host_dyn: Rc<dyn ApplierHost> = Rc::clone(&self.applier) as Rc<dyn ApplierHost>;
1827        let composer = Composer::new(
1828            Rc::clone(&slots_host),
1829            applier_host_dyn,
1830            runtime_handle,
1831            Some(node_id),
1832        );
1833        composer.enter_phase(Phase::Measure);
1834
1835        let measure_error = RefCell::new(None);
1836        let measured_children = node_handle.measured_children_scratch();
1837
1838        let measure_result = node_handle.measure_with_cached_batch(
1839            &composer,
1840            node_id,
1841            inner_constraints,
1842            CachedBatchMeasureInputs {
1843                measurer: Box::new(|child_id: NodeId, child_constraints: Constraints| {
1844                    match self.measure_node(child_id, child_constraints) {
1845                        Ok(measured) => {
1846                            measured_children
1847                                .borrow_mut()
1848                                .insert(child_id, Rc::clone(&measured));
1849                            let size = measured.size_for_parent();
1850                            Placeable::value(size.width, size.height, child_id)
1851                                .with_alignment_lines(measured.alignment_lines_for_parent())
1852                        }
1853                        Err(err) => {
1854                            let mut slot = measure_error.borrow_mut();
1855                            if slot.is_none() {
1856                                *slot = Some(err);
1857                            }
1858                            Placeable::value(0.0, 0.0, child_id)
1859                        }
1860                    }
1861                }),
1862                cached_measure_batch_registrar: Box::new(
1863                    |child_ids: &[NodeId],
1864                     child_constraints: Constraints,
1865                     out: &mut Vec<Option<Size>>| {
1866                        out.clear();
1867                        out.resize(child_ids.len(), None);
1868
1869                        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1870                            return;
1871                        };
1872
1873                        let mut measured_children = measured_children.borrow_mut();
1874                        let current_epoch = crate::render_state::current_layout_cache_epoch();
1875                        for (index, &child_id) in child_ids.iter().enumerate() {
1876                            match Self::cached_measure_node_with_applier(
1877                                &mut applier,
1878                                child_id,
1879                                child_constraints,
1880                                current_epoch,
1881                            ) {
1882                                Ok(Some(measured)) => {
1883                                    out[index] = Some(measured.size);
1884                                    measured_children.insert(child_id, Rc::clone(&measured));
1885                                }
1886                                Ok(None) => {}
1887                                Err(err) => {
1888                                    let mut slot = measure_error.borrow_mut();
1889                                    if slot.is_none() {
1890                                        *slot = Some(err);
1891                                    }
1892                                    break;
1893                                }
1894                            }
1895                        }
1896                    },
1897                ),
1898                retained_measure_lookup: Box::new(|child_id| {
1899                    measured_children.borrow().get(&child_id).cloned()
1900                }),
1901                retained_measure_registrar: Box::new(|measurements| {
1902                    let mut measured_children = measured_children.borrow_mut();
1903                    for measured in measurements {
1904                        measured_children.insert(measured.node_id(), Rc::clone(measured));
1905                    }
1906                }),
1907                error: &measure_error,
1908            },
1909        )?;
1910        drop(composer);
1911        slots_guard.restore(slots_host.into_table()?);
1912
1913        if let Some(err) = measure_error.borrow_mut().take() {
1914            return Err(err);
1915        }
1916
1917        let cranpose_ui_layout::MeasureResult {
1918            size: measured_size,
1919            alignment_lines: explicit_lines,
1920            placements,
1921        } = measure_result;
1922
1923        let mut width = measured_size.width + padding.horizontal_sum();
1924        let mut height = measured_size.height + padding.vertical_sum();
1925
1926        width = resolve_dimension(
1927            width,
1928            props.width(),
1929            props.min_width(),
1930            props.max_width(),
1931            constraints.min_width,
1932            constraints.max_width,
1933        );
1934        height = resolve_dimension(
1935            height,
1936            props.height(),
1937            props.min_height(),
1938            props.max_height(),
1939            constraints.min_height,
1940            constraints.max_height,
1941        );
1942
1943        let mut children = Vec::with_capacity(placements.len());
1944        let mut alignment_lines = AlignmentLines::default();
1945        let mut measured_children_by_id = measured_children.borrow_mut();
1946
1947        if let Ok(mut applier) = self.applier.try_borrow_typed() {
1948            let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1949                parent_node.set_measured_size(Size { width, height });
1950                parent_node.clear_needs_measure();
1951                parent_node.clear_needs_layout();
1952            });
1953        }
1954
1955        for placement in &placements {
1956            let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1957                measured
1958            } else {
1959                self.measure_node(placement.node_id, inner_constraints)?
1960            };
1961            let policy_position = Point {
1962                x: padding.left + placement.x,
1963                y: padding.top + placement.y,
1964            };
1965            let retained_position = Point {
1966                x: policy_position.x + child.offset.x,
1967                y: policy_position.y + child.offset.y,
1968            };
1969            alignment_lines.merge(
1970                child
1971                    .alignment_lines_for_parent()
1972                    .translated(policy_position.y),
1973            );
1974
1975            if let Ok(mut applier) = self.applier.try_borrow_typed()
1976                && applier
1977                    .with_node::<LayoutNode, _>(placement.node_id, |node| {
1978                        node.set_position(retained_position);
1979                    })
1980                    .is_err()
1981            {
1982                let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1983                    node.set_position(retained_position);
1984                });
1985            }
1986
1987            children.push(MeasuredChild {
1988                node: RefCell::new(child),
1989                offset: policy_position,
1990            });
1991        }
1992
1993        node_handle.set_active_children(children.iter().map(|c| c.node.borrow().node_id));
1994        node_handle.recycle_placement_scratch(placements);
1995
1996        Ok(Some(Rc::new(
1997            MeasuredNode::new(
1998                node_id,
1999                Size { width, height },
2000                offset,
2001                Point::default(),
2002                children,
2003            )
2004            .with_alignment_lines(
2005                alignment_lines
2006                    .with_overrides(explicit_lines.translated(padding.top))
2007                    .translated(offset.y),
2008            ),
2009        )))
2010    }
2011
2012    fn measure_layout_node(
2013        self: &Rc<Self>,
2014        node_id: NodeId,
2015        constraints: Constraints,
2016    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2017        let mut keep = true;
2018        let (mut applier, measure) = loop {
2019            let Ok(mut applier) = self.applier.try_borrow_typed() else {
2020                return Ok(None);
2021            };
2022            match applier.with_node::<LayoutNode, _>(node_id, |node| {
2023                self.visit_layout_node(node, constraints, keep)
2024            }) {
2025                Ok(LayoutNodeVisit::Measure(measure)) => break (applier, measure),
2026                Ok(LayoutNodeVisit::Cached(measured)) => return Ok(Some(measured)),
2027                Ok(LayoutNodeVisit::Keep(cached)) => {
2028                    drop(applier);
2029                    if let Some(kept) = self.keep_measurement(node_id, constraints, &cached)? {
2030                        return Ok(Some(kept));
2031                    }
2032                    keep = false;
2033                }
2034                Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
2035                Err(err) => return Err(err),
2036            }
2037        };
2038        let LayoutNodeMeasure {
2039            runtime_state,
2040            chain,
2041            mut pools,
2042        } = measure;
2043
2044        let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
2045            state: &runtime_state,
2046        };
2047        self.bind_layout_children(
2048            &mut applier,
2049            &runtime_state,
2050            &pools.child_ids,
2051            ChildPass::Measure,
2052        )?;
2053        drop(applier);
2054        pools.child_ids.clear();
2055
2056        let runtime_state = runtime_state.borrow();
2057        let measurement = self.measure_through_modifier_chain(
2058            node_id,
2059            &runtime_state,
2060            chain,
2061            constraints,
2062            &mut pools.placements,
2063            &mut pools.child_ids,
2064        );
2065
2066        if let Some(err) = runtime_state.frame.error.borrow_mut().take() {
2067            for child_state in &runtime_state.child_states {
2068                child_state.measured.borrow_mut().take();
2069            }
2070            self.with_applier_result(|applier| {
2071                applier.with_node::<LayoutNode, _>(node_id, |node| {
2072                    runtime_state.write_node_geometry(node_id, node, &measurement);
2073                })
2074            })
2075            .ok();
2076            return Err(err);
2077        }
2078
2079        let measured = Rc::new(
2080            MeasuredNode::new(
2081                node_id,
2082                measurement.size,
2083                measurement.offset,
2084                measurement.content_offset,
2085                runtime_state.measured_children(
2086                    &pools.placements,
2087                    &pools.child_ids,
2088                    measurement.content_offset,
2089                ),
2090            )
2091            .with_alignment_lines(measurement.alignment_lines)
2092            .with_window_root(measurement.window_root),
2093        );
2094
2095        self.with_applier_result(|applier| {
2096            applier.with_node::<LayoutNode, _>(node_id, |node| {
2097                node.cache_handles()
2098                    .store_measurement(constraints, Rc::clone(&measured));
2099                runtime_state.write_node_geometry(node_id, node, &measurement);
2100                node.clear_needs_measure();
2101                node.clear_needs_layout();
2102                node.set_measured_size(measurement.size);
2103                node.set_content_offset(measurement.content_offset);
2104            })
2105        })
2106        .ok();
2107
2108        Ok(Some(measured))
2109    }
2110
2111    /// The intrinsic size `kind` names of a layout node, through its modifier
2112    /// chain and measure policy, as Compose computes it: no measure, no
2113    /// placement, and the node's measurement cache stays as it was. `None`
2114    /// when the node is not a layout node.
2115    fn layout_node_intrinsic(
2116        self: &Rc<Self>,
2117        node_id: NodeId,
2118        kind: IntrinsicKind,
2119    ) -> Result<Option<f32>, NodeError> {
2120        let Ok(mut applier) = self.applier.try_borrow_typed() else {
2121            return Ok(None);
2122        };
2123        let mut pools = VecPools::acquire(&self.frame_arena);
2124        let bound = applier.with_node::<LayoutNode, _>(node_id, |node| {
2125            let runtime_state = node.layout_runtime_state_handle();
2126            let chain = runtime_state.borrow_mut().bind_node(node);
2127            pools.child_ids.extend_from_slice(&node.children);
2128            (runtime_state, chain)
2129        });
2130        let (runtime_state, chain) = match bound {
2131            Ok(bound) => bound,
2132            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
2133            Err(err) => return Err(err),
2134        };
2135        let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
2136            state: &runtime_state,
2137        };
2138        self.bind_layout_children(
2139            &mut applier,
2140            &runtime_state,
2141            &pools.child_ids,
2142            ChildPass::Intrinsics,
2143        )?;
2144        drop(applier);
2145        pools.child_ids.clear();
2146
2147        let runtime_state = runtime_state.borrow();
2148        let value = if chain.uses_chain {
2149            let scope = crate::density::DensityMeasureScope::new(chain.density);
2150            let frame = CoordinatorFrame::new(
2151                &runtime_state.measure_policy,
2152                &scope,
2153                runtime_state.child_measurables.as_slice(),
2154                &runtime_state.child_states,
2155                &mut pools.placements,
2156                &mut pools.child_ids,
2157            );
2158            runtime_state
2159                .coordinator_chain
2160                .intrinsic_from(0, &frame, kind)
2161        } else {
2162            policy_intrinsic(
2163                runtime_state.measure_policy.as_ref(),
2164                runtime_state.child_measurables.as_slice(),
2165                kind,
2166            )
2167        };
2168        match runtime_state.frame.error.borrow_mut().take() {
2169            Some(err) => Err(err),
2170            None => Ok(Some(value)),
2171        }
2172    }
2173
2174    /// The measurement of a node whose own inputs did not change since it
2175    /// measured at `constraints`: only nodes below it are dirty.
2176    fn measurement_to_keep(
2177        &self,
2178        node: &LayoutNode,
2179        constraints: Constraints,
2180    ) -> Option<Rc<MeasuredNode>> {
2181        if node.needs_measure() || node.needs_layout() || node.is_virtual() {
2182            return None;
2183        }
2184        let cache = node.cache_handles();
2185        if cache.epoch() < self.cache_floor {
2186            return None;
2187        }
2188        cache.get_measurement(constraints)
2189    }
2190
2191    /// Measures again the children of `node_id` that changed, at the
2192    /// constraints they last had, and keeps the node's measurement when each
2193    /// of them measures the same for its parent: the node's policy would
2194    /// place them as before. `None` when one of them changed, and the node
2195    /// has to measure again.
2196    fn keep_measurement(
2197        self: &Rc<Self>,
2198        node_id: NodeId,
2199        constraints: Constraints,
2200        cached: &Rc<MeasuredNode>,
2201    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2202        let runtime_state = self.with_applier_result(|applier| {
2203            applier.with_node::<LayoutNode, _>(node_id, |node| node.layout_runtime_state_handle())
2204        })?;
2205        let mut next_state = 0;
2206        let kept = self.remeasure_changed_children(
2207            cached,
2208            |child_id| self.layout_child_change(&runtime_state, &mut next_state, child_id),
2209            |_| {},
2210        )?;
2211        let Some(kept) = kept else {
2212            return Ok(None);
2213        };
2214        self.with_applier_result(|applier| {
2215            applier.with_node::<LayoutNode, _>(node_id, |node| {
2216                node.cache_handles()
2217                    .store_measurement(constraints, Rc::clone(&kept));
2218                node.clear_needs_layout();
2219            })
2220        })?;
2221        Ok(Some(kept))
2222    }
2223
2224    /// Walks the children of a kept measurement, measures again each one
2225    /// `change` finds changed, at the constraints it last had, and keeps the
2226    /// measurement with their new measurements swapped in. `None` as soon as
2227    /// `change` refuses a child or one measures differently for its parent;
2228    /// the measurement is then left as it was.
2229    fn remeasure_changed_children(
2230        self: &Rc<Self>,
2231        cached: &Rc<MeasuredNode>,
2232        mut change: impl FnMut(NodeId) -> Result<ChildChange, NodeError>,
2233        mut remeasured: impl FnMut(NodeId),
2234    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2235        let mut replaced = smallvec::SmallVec::<[(usize, Rc<MeasuredNode>); 4]>::new();
2236        for (index, previous) in cached.children.iter().enumerate() {
2237            let previous = Rc::clone(&previous.node.borrow());
2238            let child_id = previous.node_id();
2239            let child = match change(child_id)? {
2240                ChildChange::Unchanged => continue,
2241                ChildChange::Refuse => return Ok(None),
2242                ChildChange::Changed(child) => child,
2243            };
2244            let measured = self.measure_node(child_id, child.constraints)?;
2245            remeasured(child_id);
2246            if !measures_the_same_for_parent(&previous, &measured) {
2247                return Ok(None);
2248            }
2249            if child.placed {
2250                self.place_where_it_was(child_id);
2251            }
2252            if !Rc::ptr_eq(&previous, &measured) {
2253                replaced.push((index, measured));
2254            }
2255        }
2256        for (index, measured) in replaced {
2257            if let Some(child) = cached.children.get(index) {
2258                *child.node.borrow_mut() = measured;
2259            }
2260        }
2261        Ok(Some(Rc::clone(cached)))
2262    }
2263
2264    /// What the walk over a kept layout node finds of a child, from the
2265    /// record of the node's last measure, which `next_state` walks in order.
2266    /// A changed child refuses the keep when its parent data changed, the
2267    /// node read its intrinsic sizes, or the node did not measure it.
2268    fn layout_child_change(
2269        &self,
2270        runtime_state: &RefCell<LayoutRuntimeState>,
2271        next_state: &mut usize,
2272        child_id: NodeId,
2273    ) -> Result<ChildChange, NodeError> {
2274        let runtime_state = runtime_state.borrow();
2275        let Some((offset, state)) = runtime_state
2276            .child_states
2277            .get(*next_state..)
2278            .unwrap_or_default()
2279            .iter()
2280            .enumerate()
2281            .find(|(_, state)| state.node_id == child_id)
2282        else {
2283            return Ok(ChildChange::Refuse);
2284        };
2285        *next_state += offset + 1;
2286        self.child_change(child_id, |view| {
2287            let parent_data_changed = view
2288                .parent_data
2289                .is_some_and(|data| Some(data) != state.parent_data.get());
2290            match state.measured_constraints.get() {
2291                Some(constraints) if !state.read_intrinsics.get() && !parent_data_changed => {
2292                    ChildChange::Changed(ChangedChild {
2293                        constraints,
2294                        placed: view.placed,
2295                    })
2296                }
2297                _ => ChildChange::Refuse,
2298            }
2299        })
2300    }
2301
2302    /// Stores a subcompose node's new measurement in its cache, as a layout
2303    /// node's measure stores its own.
2304    fn store_subcompose_measurement(
2305        &self,
2306        node_id: NodeId,
2307        constraints: Constraints,
2308        measured: &Rc<MeasuredNode>,
2309    ) {
2310        let Ok(mut applier) = self.applier.try_borrow_typed() else {
2311            return;
2312        };
2313        let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
2314            node.cache_handles()
2315                .store_measurement(constraints, Rc::clone(measured));
2316        });
2317    }
2318
2319    /// Places a child of a kept node where it was: the node's policy would
2320    /// place it there again.
2321    fn place_where_it_was(&self, child_id: NodeId) {
2322        let Ok(mut applier) = self.applier.try_borrow_typed() else {
2323            return;
2324        };
2325        if applier
2326            .with_node::<LayoutNode, _>(child_id, |child| child.set_position(child.position()))
2327            .is_err()
2328        {
2329            let _ = applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2330                child.set_position(child.layout_state().position());
2331            });
2332        }
2333    }
2334
2335    fn visit_layout_node(
2336        &self,
2337        node: &mut LayoutNode,
2338        constraints: Constraints,
2339        keep: bool,
2340    ) -> LayoutNodeVisit<'_> {
2341        node.clear_placed();
2342        if keep && let Some(cached) = self.measurement_to_keep(node, constraints) {
2343            return LayoutNodeVisit::Keep(cached);
2344        }
2345        let cache = node.cache_handles();
2346        cache.activate(self.cache_epoch);
2347        if !Node::layout_dirty(node)
2348            && let Some(cached) = cache.get_measurement(constraints)
2349        {
2350            node.clear_needs_measure();
2351            node.clear_needs_layout();
2352            return LayoutNodeVisit::Cached(cached);
2353        }
2354
2355        let runtime_state = node.layout_runtime_state_handle();
2356        let chain = runtime_state.borrow_mut().bind_node(node);
2357        let mut pools = VecPools::acquire(&self.frame_arena);
2358        pools.child_ids.extend_from_slice(&node.children);
2359        LayoutNodeVisit::Measure(LayoutNodeMeasure {
2360            runtime_state,
2361            chain,
2362            pools,
2363        })
2364    }
2365
2366    fn bind_layout_children(
2367        self: &Rc<Self>,
2368        applier: &mut MemoryApplier,
2369        runtime_state: &RefCell<LayoutRuntimeState>,
2370        child_ids: &[NodeId],
2371        pass: ChildPass,
2372    ) -> Result<(), NodeError> {
2373        let mut runtime_state = runtime_state.borrow_mut();
2374        runtime_state.frame.bind(self, pass);
2375        let mut bound = 0;
2376        for &child_id in child_ids {
2377            if self.bind_layout_child(applier, &mut runtime_state, bound, child_id)? {
2378                bound += 1;
2379            }
2380        }
2381        runtime_state.truncate_children(bound);
2382        Ok(())
2383    }
2384
2385    fn bind_layout_child(
2386        &self,
2387        applier: &mut MemoryApplier,
2388        runtime_state: &mut LayoutRuntimeState,
2389        position: usize,
2390        child_id: NodeId,
2391    ) -> Result<bool, NodeError> {
2392        let bound = applier.with_node::<LayoutNode, _>(child_id, |child| {
2393            runtime_state.child_state_at(position, child_id).bind(
2394                LayoutChildBinding {
2395                    cache: child.cache_handles(),
2396                    layout_state: Some(child.layout_state_handle()),
2397                    parent_data: Some(parent_data_of(
2398                        child.resolved_modifiers().layout_properties(),
2399                    )),
2400                    dirty: child.needs_layout() || child.needs_measure(),
2401                    descendant_dirty: Node::descendant_needs_layout(child),
2402                },
2403                self,
2404            );
2405        });
2406        match bound {
2407            Ok(()) => Ok(true),
2408            Err(NodeError::TypeMismatch { .. }) => {
2409                match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2410                    runtime_state.child_state_at(position, child_id).bind(
2411                        LayoutChildBinding {
2412                            cache: child.cache_handles(),
2413                            layout_state: None,
2414                            parent_data: Some(parent_data_of(
2415                                child.resolved_modifiers().layout_properties(),
2416                            )),
2417                            dirty: child.needs_layout() || child.needs_measure(),
2418                            descendant_dirty: Node::descendant_needs_layout(child),
2419                        },
2420                        self,
2421                    );
2422                }) {
2423                    Ok(()) => Ok(true),
2424                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(false),
2425                    Err(err) => Err(err),
2426                }
2427            }
2428            Err(NodeError::Missing { .. }) => Ok(false),
2429            Err(err) => Err(err),
2430        }
2431    }
2432
2433    fn measure_through_modifier_chain(
2434        &self,
2435        node_id: NodeId,
2436        runtime_state: &LayoutRuntimeState,
2437        chain: ModifierChainInputs,
2438        constraints: Constraints,
2439        placements: &mut Vec<Placement>,
2440        placement_indices: &mut Vec<usize>,
2441    ) -> ModifierChainMeasurement {
2442        let scope = crate::density::DensityMeasureScope::new(chain.density);
2443
2444        if !chain.uses_chain {
2445            let measurement = runtime_state.measure_policy.measure_into(
2446                &scope,
2447                runtime_state.child_measurables.as_slice(),
2448                constraints,
2449                placements,
2450            );
2451            return ModifierChainMeasurement {
2452                size: measurement.size,
2453                alignment_lines: inherited_alignment_lines(
2454                    &runtime_state.child_states,
2455                    placements,
2456                    placement_indices,
2457                )
2458                .with_overrides(measurement.alignment_lines),
2459                content_offset: Point::default(),
2460                offset: chain.offset,
2461                window_root: chain.window_root,
2462                uses_chain: false,
2463            };
2464        }
2465
2466        let frame = CoordinatorFrame::new(
2467            &runtime_state.measure_policy,
2468            &scope,
2469            runtime_state.child_measurables.as_slice(),
2470            &runtime_state.child_states,
2471            placements,
2472            placement_indices,
2473        );
2474        let placeable = runtime_state
2475            .coordinator_chain
2476            .measure_from(0, &frame, constraints);
2477        let (content_x, content_y) = placeable.content_offset();
2478
2479        let invalidations = frame.take_invalidations();
2480        if !invalidations.is_empty() {
2481            self.with_applier_result(|applier| {
2482                applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2483                    for kind in invalidations {
2484                        match kind {
2485                            InvalidationKind::Layout => layout_node.mark_needs_measure(),
2486                            InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2487                            InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2488                            InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2489                            InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2490                        }
2491                    }
2492                })
2493            })
2494            .ok();
2495        }
2496
2497        ModifierChainMeasurement {
2498            alignment_lines: placeable.alignment_lines(),
2499            size: Size {
2500                width: placeable.width(),
2501                height: placeable.height(),
2502            },
2503            content_offset: Point {
2504                x: content_x - chain.offset.x,
2505                y: content_y - chain.offset.y,
2506            },
2507            offset: chain.offset,
2508            window_root: chain.window_root,
2509            uses_chain: true,
2510        }
2511    }
2512}
2513
2514struct VecPools<'a> {
2515    arena: &'a RefCell<FrameLayoutArena>,
2516    child_ids: Vec<NodeId>,
2517    placements: Vec<Placement>,
2518}
2519
2520impl<'a> VecPools<'a> {
2521    fn acquire(arena: &'a RefCell<FrameLayoutArena>) -> Self {
2522        let mut pools = arena.borrow_mut();
2523        let child_ids = pools.tmp_child_ids.acquire();
2524        let placements = pools.tmp_placements.acquire();
2525        Self {
2526            arena,
2527            child_ids,
2528            placements,
2529        }
2530    }
2531}
2532
2533impl Drop for VecPools<'_> {
2534    fn drop(&mut self) {
2535        let mut pools = self.arena.borrow_mut();
2536        pools
2537            .tmp_child_ids
2538            .release(std::mem::take(&mut self.child_ids));
2539        pools
2540            .tmp_placements
2541            .release(std::mem::take(&mut self.placements));
2542    }
2543}
2544
2545struct SlotsGuard<'a> {
2546    table: &'a RefCell<SlotTable>,
2547    slots: Option<SlotTable>,
2548}
2549
2550impl<'a> SlotsGuard<'a> {
2551    fn take(table: &'a RefCell<SlotTable>) -> Self {
2552        let slots = std::mem::take(&mut *table.borrow_mut());
2553        Self {
2554            table,
2555            slots: Some(slots),
2556        }
2557    }
2558
2559    fn host(&mut self) -> Rc<SlotsHost> {
2560        let slots = self.slots.take().unwrap_or_default();
2561        Rc::new(SlotsHost::new(slots))
2562    }
2563
2564    fn restore(&mut self, slots: SlotTable) {
2565        debug_assert!(self.slots.is_none());
2566        self.slots = Some(slots);
2567    }
2568}
2569
2570impl Drop for SlotsGuard<'_> {
2571    fn drop(&mut self) {
2572        if let Some(slots) = self.slots.take() {
2573            *self.table.borrow_mut() = slots;
2574        }
2575    }
2576}
2577
2578#[derive(Debug, Clone)]
2579pub(crate) struct MeasuredNode {
2580    node_id: NodeId,
2581    size: Size,
2582    offset: Point,
2583    content_offset: Point,
2584    alignment_lines: AlignmentLines,
2585    children: Vec<MeasuredChild>,
2586    window_root: bool,
2587}
2588
2589impl MeasuredNode {
2590    fn new(
2591        node_id: NodeId,
2592        size: Size,
2593        offset: Point,
2594        content_offset: Point,
2595        children: Vec<MeasuredChild>,
2596    ) -> Self {
2597        Self {
2598            node_id,
2599            size,
2600            offset,
2601            content_offset,
2602            alignment_lines: AlignmentLines::default(),
2603            children,
2604            window_root: false,
2605        }
2606    }
2607
2608    fn with_window_root(mut self, window_root: bool) -> Self {
2609        self.window_root = window_root;
2610        self
2611    }
2612
2613    fn with_alignment_lines(mut self, alignment_lines: AlignmentLines) -> Self {
2614        self.alignment_lines = alignment_lines;
2615        self
2616    }
2617
2618    pub(crate) fn alignment_lines_for_parent(&self) -> AlignmentLines {
2619        if self.window_root {
2620            AlignmentLines::default()
2621        } else {
2622            self.alignment_lines
2623        }
2624    }
2625
2626    pub(crate) fn size_for_parent(&self) -> Size {
2627        if self.window_root {
2628            Size::new(0.0, 0.0)
2629        } else {
2630            self.size
2631        }
2632    }
2633
2634    #[cfg(test)]
2635    pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2636        Self::new(
2637            node_id,
2638            size,
2639            Point::default(),
2640            Point::default(),
2641            Vec::new(),
2642        )
2643    }
2644
2645    pub(crate) fn node_id(&self) -> NodeId {
2646        self.node_id
2647    }
2648
2649    pub(crate) fn size(&self) -> Size {
2650        self.size
2651    }
2652}
2653
2654#[derive(Debug, Clone)]
2655struct MeasuredChild {
2656    /// The child's measurement. A kept parent swaps in the new measurement
2657    /// of a child that measures the same for it.
2658    node: RefCell<Rc<MeasuredNode>>,
2659    offset: Point,
2660}
2661
2662/// The intrinsic size `kind` names, of a measurable.
2663fn measurable_intrinsic(measurable: &dyn Measurable, kind: IntrinsicKind) -> f32 {
2664    match kind {
2665        IntrinsicKind::MinWidth(height) => measurable.min_intrinsic_width(height),
2666        IntrinsicKind::MaxWidth(height) => measurable.max_intrinsic_width(height),
2667        IntrinsicKind::MinHeight(width) => measurable.min_intrinsic_height(width),
2668        IntrinsicKind::MaxHeight(width) => measurable.max_intrinsic_height(width),
2669    }
2670}
2671
2672/// The intrinsic size `kind` names, of a measure policy over `measurables`.
2673fn policy_intrinsic(
2674    policy: &dyn MeasurePolicy,
2675    measurables: &[Box<dyn Measurable>],
2676    kind: IntrinsicKind,
2677) -> f32 {
2678    match kind {
2679        IntrinsicKind::MinWidth(height) => policy.min_intrinsic_width(measurables, height),
2680        IntrinsicKind::MaxWidth(height) => policy.max_intrinsic_width(measurables, height),
2681        IntrinsicKind::MinHeight(width) => policy.min_intrinsic_height(measurables, width),
2682        IntrinsicKind::MaxHeight(width) => policy.max_intrinsic_height(measurables, width),
2683    }
2684}
2685
2686/// The intrinsic size `kind` names, of a layout modifier around `wrapped`.
2687fn layout_modifier_intrinsic(
2688    node: &dyn cranpose_foundation::LayoutModifierNode,
2689    wrapped: &dyn Measurable,
2690    kind: IntrinsicKind,
2691    density: f32,
2692) -> f32 {
2693    match kind {
2694        IntrinsicKind::MinWidth(height) => node.min_intrinsic_width(wrapped, height, density),
2695        IntrinsicKind::MaxWidth(height) => node.max_intrinsic_width(wrapped, height, density),
2696        IntrinsicKind::MinHeight(width) => node.min_intrinsic_height(wrapped, width, density),
2697        IntrinsicKind::MaxHeight(width) => node.max_intrinsic_height(wrapped, width, density),
2698    }
2699}
2700
2701struct CoordinatorFrame<'a> {
2702    measure_policy: &'a Rc<dyn MeasurePolicy>,
2703    scope: &'a dyn cranpose_ui_layout::MeasureScope,
2704    measurables: &'a [Box<dyn Measurable>],
2705    child_states: &'a [Rc<LayoutChildMeasureState>],
2706    placements: RefCell<&'a mut Vec<Placement>>,
2707    placement_indices: RefCell<&'a mut Vec<usize>>,
2708    context: RefCell<LayoutNodeContext>,
2709}
2710
2711impl<'a> CoordinatorFrame<'a> {
2712    fn new(
2713        measure_policy: &'a Rc<dyn MeasurePolicy>,
2714        scope: &'a dyn cranpose_ui_layout::MeasureScope,
2715        measurables: &'a [Box<dyn Measurable>],
2716        child_states: &'a [Rc<LayoutChildMeasureState>],
2717        placements: &'a mut Vec<Placement>,
2718        placement_indices: &'a mut Vec<usize>,
2719    ) -> Self {
2720        Self {
2721            measure_policy,
2722            scope,
2723            measurables,
2724            child_states,
2725            placements: RefCell::new(placements),
2726            placement_indices: RefCell::new(placement_indices),
2727            context: RefCell::new(LayoutNodeContext::new(scope.density())),
2728        }
2729    }
2730
2731    fn take_invalidations(&self) -> Vec<InvalidationKind> {
2732        self.context.borrow_mut().take_invalidations()
2733    }
2734}
2735
2736struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2737    chain: &'chain CoordinatorChain,
2738    frame: &'frame_ref CoordinatorFrame<'frame_data>,
2739    index: usize,
2740    alignment_lines: Cell<AlignmentLines>,
2741}
2742
2743impl<'chain, 'frame_ref, 'frame_data> CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2744    fn new(
2745        chain: &'chain CoordinatorChain,
2746        frame: &'frame_ref CoordinatorFrame<'frame_data>,
2747        index: usize,
2748    ) -> Self {
2749        Self {
2750            chain,
2751            frame,
2752            index,
2753            alignment_lines: Cell::default(),
2754        }
2755    }
2756}
2757
2758impl Measurable for CoordinatorLink<'_, '_, '_> {
2759    fn measure(&self, constraints: Constraints) -> Placeable {
2760        let placeable = self.chain.measure_from(self.index, self.frame, constraints);
2761        self.alignment_lines.set(placeable.alignment_lines());
2762        placeable
2763    }
2764
2765    fn min_intrinsic_width(&self, height: f32) -> f32 {
2766        self.chain
2767            .intrinsic_from(self.index, self.frame, IntrinsicKind::MinWidth(height))
2768    }
2769
2770    fn max_intrinsic_width(&self, height: f32) -> f32 {
2771        self.chain
2772            .intrinsic_from(self.index, self.frame, IntrinsicKind::MaxWidth(height))
2773    }
2774
2775    fn min_intrinsic_height(&self, width: f32) -> f32 {
2776        self.chain
2777            .intrinsic_from(self.index, self.frame, IntrinsicKind::MinHeight(width))
2778    }
2779
2780    fn max_intrinsic_height(&self, width: f32) -> f32 {
2781        self.chain
2782            .intrinsic_from(self.index, self.frame, IntrinsicKind::MaxHeight(width))
2783    }
2784}
2785
2786struct CoordinatorNode {
2787    modifier_index: usize,
2788    node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2789    measured_size: Cell<Size>,
2790    accumulated_offset: Cell<Point>,
2791}
2792
2793impl CoordinatorNode {
2794    fn new(
2795        modifier_index: usize,
2796        node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2797    ) -> Self {
2798        Self {
2799            modifier_index,
2800            node,
2801            measured_size: Cell::new(Size::default()),
2802            accumulated_offset: Cell::new(Point::default()),
2803        }
2804    }
2805
2806    fn matches(
2807        &self,
2808        modifier_index: usize,
2809        node: &Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2810    ) -> bool {
2811        self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2812    }
2813
2814    #[cfg(test)]
2815    fn ptr(&self) -> usize {
2816        Rc::as_ptr(&self.node) as *const () as usize
2817    }
2818}
2819
2820#[derive(Default)]
2821struct CoordinatorChain {
2822    nodes: Vec<CoordinatorNode>,
2823    /// The size the node's own measure policy measured its content at.
2824    inner_size: Cell<Size>,
2825    /// The chain revision the nodes were last synced at and the inputs it
2826    /// gave: a node whose modifiers did not change since measures again
2827    /// without walking its chain.
2828    synced: Option<(u64, ModifierChainInputs)>,
2829}
2830
2831impl CoordinatorChain {
2832    fn sync(&mut self, node: &LayoutNode) -> ModifierChainInputs {
2833        let density = node.density();
2834        let revision = node.modifier_chain().revision();
2835        if let Some((synced, inputs)) = self.synced
2836            && synced == revision
2837            && inputs.density == density
2838        {
2839            return inputs;
2840        }
2841        let inputs = self.walk(node, density);
2842        self.synced = Some((revision, inputs));
2843        inputs
2844    }
2845
2846    /// Syncs the nodes with the layout nodes of `node`'s chain and sums
2847    /// its offsets.
2848    fn walk(&mut self, node: &LayoutNode, density: crate::density::Density) -> ModifierChainInputs {
2849        let mut inputs = ModifierChainInputs {
2850            density,
2851            window_root: node.is_window_root(),
2852            offset: Point::default(),
2853            uses_chain: false,
2854        };
2855        let chain_handle = node.modifier_chain();
2856        if !chain_handle.has_layout_nodes() {
2857            return inputs;
2858        }
2859
2860        let chain = chain_handle.chain();
2861        let mut len = 0;
2862        let mut matches = true;
2863        chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2864            let Some(index) = node_ref.entry_index() else {
2865                return;
2866            };
2867            if let Some(node) = chain.get_node_rc(index) {
2868                matches = matches
2869                    && self
2870                        .nodes
2871                        .get(len)
2872                        .is_some_and(|candidate| candidate.matches(index, node));
2873                len += 1;
2874            }
2875            node_ref.with_node(|node| {
2876                if let Some(offset_node) = node
2877                    .as_any()
2878                    .downcast_ref::<crate::modifier_nodes::OffsetNode>()
2879                {
2880                    let delta = offset_node.device_offset(density.density());
2881                    inputs.offset.x += delta.x;
2882                    inputs.offset.y += delta.y;
2883                }
2884            });
2885        });
2886
2887        inputs.uses_chain = len > 0;
2888        if inputs.uses_chain && !(matches && len == self.nodes.len()) {
2889            self.rebuild(chain, len);
2890        }
2891        inputs
2892    }
2893
2894    fn rebuild(&mut self, chain: &cranpose_foundation::ModifierNodeChain, len: usize) {
2895        let mut previous_nodes = std::mem::take(&mut self.nodes);
2896        self.nodes.reserve(len);
2897        chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2898            let Some((index, node)) = node_ref
2899                .entry_index()
2900                .and_then(|index| chain.get_node_rc(index).map(|node| (index, node)))
2901            else {
2902                return;
2903            };
2904            match previous_nodes
2905                .iter()
2906                .position(|candidate| candidate.matches(index, node))
2907            {
2908                Some(position) => self.nodes.push(previous_nodes.swap_remove(position)),
2909                None => self
2910                    .nodes
2911                    .push(CoordinatorNode::new(index, Rc::clone(node))),
2912            }
2913        });
2914    }
2915
2916    fn measure_from(
2917        &self,
2918        index: usize,
2919        frame: &CoordinatorFrame<'_>,
2920        constraints: Constraints,
2921    ) -> Placeable {
2922        let Some(node) = self.nodes.get(index) else {
2923            let mut placements = frame.placements.borrow_mut();
2924            let measurement = frame.measure_policy.measure_into(
2925                frame.scope,
2926                frame.measurables,
2927                constraints,
2928                &mut placements,
2929            );
2930            self.inner_size.set(measurement.size);
2931            return Placeable::value(
2932                measurement.size.width,
2933                measurement.size.height,
2934                NodeId::default(),
2935            )
2936            .with_alignment_lines(
2937                inherited_alignment_lines(
2938                    frame.child_states,
2939                    &placements,
2940                    &mut frame.placement_indices.borrow_mut(),
2941                )
2942                .with_overrides(measurement.alignment_lines),
2943            );
2944        };
2945
2946        let wrapped = CoordinatorLink::new(self, frame, index + 1);
2947        let node_borrow = node.node.borrow();
2948
2949        let Some(layout_node) = node_borrow.as_layout_node() else {
2950            let placeable = wrapped.measure(constraints);
2951            node.measured_size.set(Size {
2952                width: placeable.width(),
2953                height: placeable.height(),
2954            });
2955            let child_accumulated = self.total_content_offset_from(index + 1);
2956            node.accumulated_offset.set(child_accumulated);
2957            return placeable;
2958        };
2959
2960        let result = match frame.context.try_borrow_mut() {
2961            Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
2962            Err(_) => {
2963                let mut temp = LayoutNodeContext::new(frame.scope.density());
2964                let result = layout_node.measure(&mut temp, &wrapped, constraints);
2965                if let Ok(mut context) = frame.context.try_borrow_mut() {
2966                    for kind in temp.take_invalidations() {
2967                        context.invalidate(kind);
2968                    }
2969                }
2970                result
2971            }
2972        };
2973
2974        node.measured_size.set(result.size);
2975        let local_offset = Point {
2976            x: result.placement_offset_x,
2977            y: result.placement_offset_y,
2978        };
2979        let child_accumulated = self.total_content_offset_from(index + 1);
2980        let accumulated = Point {
2981            x: local_offset.x + child_accumulated.x,
2982            y: local_offset.y + child_accumulated.y,
2983        };
2984        node.accumulated_offset.set(accumulated);
2985
2986        Placeable::value_with_offset(
2987            result.size.width,
2988            result.size.height,
2989            NodeId::default(),
2990            (accumulated.x, accumulated.y),
2991        )
2992        .with_alignment_lines(
2993            wrapped
2994                .alignment_lines
2995                .get()
2996                .translated(local_offset.y)
2997                .with_overrides(result.alignment_lines),
2998        )
2999    }
3000
3001    fn intrinsic_from(
3002        &self,
3003        index: usize,
3004        frame: &CoordinatorFrame<'_>,
3005        kind: IntrinsicKind,
3006    ) -> f32 {
3007        let Some(node) = self.nodes.get(index) else {
3008            return policy_intrinsic(frame.measure_policy.as_ref(), frame.measurables, kind);
3009        };
3010        let wrapped = CoordinatorLink::new(self, frame, index + 1);
3011        let node_borrow = node.node.borrow();
3012        node_borrow.as_layout_node().map_or_else(
3013            || measurable_intrinsic(&wrapped, kind),
3014            |layout_node| {
3015                layout_modifier_intrinsic(layout_node, &wrapped, kind, frame.scope.density())
3016            },
3017        )
3018    }
3019
3020    /// Writes where each coordinator and the node's content ended up in the
3021    /// last measure, relative to the node drawn at its `node_offset`, and
3022    /// says whether any of them moved.
3023    fn write_geometry(
3024        &self,
3025        geometry: &crate::modifier::CoordinatorGeometry,
3026        node_offset: Point,
3027    ) -> bool {
3028        let content = self.total_content_offset_from(0);
3029        let placed = |inner_offset: Point, size: Size| GeometryRect {
3030            x: content.x - inner_offset.x - node_offset.x,
3031            y: content.y - inner_offset.y - node_offset.y,
3032            width: size.width,
3033            height: size.height,
3034        };
3035        geometry.replace(
3036            self.nodes
3037                .iter()
3038                .map(|node| placed(node.accumulated_offset.get(), node.measured_size.get()))
3039                .chain(std::iter::once(placed(
3040                    Point::default(),
3041                    self.inner_size.get(),
3042                ))),
3043        )
3044    }
3045
3046    fn total_content_offset_from(&self, index: usize) -> Point {
3047        self.nodes
3048            .get(index)
3049            .map(|node| node.accumulated_offset.get())
3050            .unwrap_or_default()
3051    }
3052
3053    #[cfg(test)]
3054    fn debug_ptrs(&self) -> Vec<usize> {
3055        self.nodes.iter().map(CoordinatorNode::ptr).collect()
3056    }
3057}
3058
3059fn inherited_alignment_lines(
3060    child_states: &[Rc<LayoutChildMeasureState>],
3061    placements: &[Placement],
3062    placement_indices: &mut Vec<usize>,
3063) -> AlignmentLines {
3064    placement_indices.clear();
3065    let in_child_order = placements.len() == child_states.len()
3066        && placements
3067            .iter()
3068            .zip(child_states)
3069            .all(|(placement, child)| placement.node_id == child.node_id);
3070    if !in_child_order {
3071        placement_indices.extend(0..placements.len());
3072        placement_indices.sort_unstable_by_key(|&index| (placements[index].node_id, index));
3073    }
3074    let mut lines = AlignmentLines::default();
3075    for (index, child) in child_states.iter().enumerate() {
3076        let placement = placement_for_child(placements, placement_indices, index, child.node_id);
3077        if let Some(measured) = child.measured.borrow().as_ref() {
3078            lines.merge(
3079                measured
3080                    .alignment_lines_for_parent()
3081                    .translated(child.placement_position(placement).y),
3082            );
3083        }
3084    }
3085    lines
3086}
3087
3088fn placement_for_child<'a>(
3089    placements: &'a [Placement],
3090    indices: &[usize],
3091    child_index: usize,
3092    node_id: NodeId,
3093) -> Option<&'a Placement> {
3094    let index = if indices.is_empty() {
3095        child_index
3096    } else {
3097        let first = indices.partition_point(|&index| placements[index].node_id < node_id);
3098        *indices.get(first)?
3099    };
3100    placements
3101        .get(index)
3102        .filter(|placement| placement.node_id == node_id)
3103}
3104
3105pub(crate) struct LayoutRuntimeState {
3106    child_ids: Vec<NodeId>,
3107    child_states: Vec<Rc<LayoutChildMeasureState>>,
3108    child_measurables: Vec<Box<dyn Measurable>>,
3109    coordinator_chain: CoordinatorChain,
3110    measure_policy: Rc<dyn MeasurePolicy>,
3111    frame: Rc<LayoutChildFrame>,
3112}
3113
3114impl LayoutRuntimeState {
3115    pub(crate) fn new(measure_policy: Rc<dyn MeasurePolicy>) -> Self {
3116        Self {
3117            child_ids: Vec::new(),
3118            child_states: Vec::new(),
3119            child_measurables: Vec::new(),
3120            coordinator_chain: CoordinatorChain::default(),
3121            measure_policy,
3122            frame: Rc::default(),
3123        }
3124    }
3125
3126    fn bind_node(&mut self, node: &LayoutNode) -> ModifierChainInputs {
3127        if !Rc::ptr_eq(&self.measure_policy, &node.measure_policy) {
3128            self.measure_policy = Rc::clone(&node.measure_policy);
3129        }
3130        self.coordinator_chain.sync(node)
3131    }
3132
3133    fn child_state_at(&mut self, position: usize, child_id: NodeId) -> &LayoutChildMeasureState {
3134        if self.child_ids.get(position) != Some(&child_id) {
3135            let from = match self.child_ids[position..]
3136                .iter()
3137                .position(|&id| id == child_id)
3138            {
3139                Some(offset) => position + offset,
3140                None => {
3141                    let state = LayoutChildMeasureState::new(child_id, Rc::clone(&self.frame));
3142                    self.child_ids.push(child_id);
3143                    self.child_states.push(Rc::clone(&state));
3144                    self.child_measurables
3145                        .push(Box::new(LayoutChildMeasurable::new(state)));
3146                    self.child_ids.len() - 1
3147                }
3148            };
3149            self.child_ids.swap(position, from);
3150            self.child_states.swap(position, from);
3151            self.child_measurables.swap(position, from);
3152        }
3153        &self.child_states[position]
3154    }
3155
3156    fn truncate_children(&mut self, len: usize) {
3157        self.child_ids.truncate(len);
3158        self.child_states.truncate(len);
3159        self.child_measurables.truncate(len);
3160    }
3161
3162    fn measured_children(
3163        &self,
3164        placements: &[Placement],
3165        placement_indices: &[usize],
3166        content_offset: Point,
3167    ) -> Vec<MeasuredChild> {
3168        let mut measured_children = Vec::with_capacity(self.child_states.len());
3169        for (index, child_state) in self.child_states.iter().enumerate() {
3170            let Some(measured) = child_state.measured.borrow_mut().take() else {
3171                continue;
3172            };
3173            let placement =
3174                placement_for_child(placements, placement_indices, index, child_state.node_id);
3175            let base_position = child_state.placement_position(placement);
3176            if placement.is_some() {
3177                child_state.place_retained(Point {
3178                    x: base_position.x + measured.offset.x,
3179                    y: base_position.y + measured.offset.y,
3180                });
3181            }
3182            measured_children.push(MeasuredChild {
3183                node: RefCell::new(measured),
3184                offset: Point {
3185                    x: content_offset.x + base_position.x,
3186                    y: content_offset.y + base_position.y,
3187                },
3188            });
3189        }
3190        measured_children
3191    }
3192
3193    fn write_node_geometry(
3194        &self,
3195        node_id: NodeId,
3196        node: &LayoutNode,
3197        measurement: &ModifierChainMeasurement,
3198    ) {
3199        let geometry = node.coordinator_geometry();
3200        let moved = if measurement.uses_chain {
3201            self.coordinator_chain
3202                .write_geometry(geometry, measurement.offset)
3203        } else {
3204            geometry.replace([GeometryRect {
3205                x: 0.0,
3206                y: 0.0,
3207                width: measurement.size.width,
3208                height: measurement.size.height,
3209            }])
3210        };
3211        if moved {
3212            crate::render_state::record_geometry_scene_node(node_id);
3213        }
3214    }
3215
3216    #[cfg(test)]
3217    pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
3218        LayoutRuntimeDebugStats {
3219            child_ids: self.child_ids.clone(),
3220            child_state_ptrs: self
3221                .child_states
3222                .iter()
3223                .map(|state| Rc::as_ptr(state) as *const () as usize)
3224                .collect(),
3225            child_measurable_ptrs: self
3226                .child_measurables
3227                .iter()
3228                .map(|measurable| {
3229                    measurable.as_ref() as *const dyn Measurable as *const () as usize
3230                })
3231                .collect(),
3232            child_measurable_count: self.child_measurables.len(),
3233            coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
3234            coordinator_node_count: self.coordinator_chain.nodes.len(),
3235        }
3236    }
3237}
3238
3239#[cfg(test)]
3240#[derive(Debug, Clone, PartialEq, Eq)]
3241pub(crate) struct LayoutRuntimeDebugStats {
3242    pub(crate) child_ids: Vec<NodeId>,
3243    pub(crate) child_state_ptrs: Vec<usize>,
3244    pub(crate) child_measurable_ptrs: Vec<usize>,
3245    pub(crate) child_measurable_count: usize,
3246    pub(crate) coordinator_node_ptrs: Vec<usize>,
3247    pub(crate) coordinator_node_count: usize,
3248}
3249
3250#[derive(Default)]
3251struct LayoutChildFrame {
3252    builder: RefCell<Option<Rc<LayoutBuilderState>>>,
3253    error: RefCell<Option<NodeError>>,
3254    pass: Cell<ChildPass>,
3255}
3256
3257/// What a node binds its children for.
3258#[derive(Clone, Copy, Default, PartialEq, Eq)]
3259enum ChildPass {
3260    /// A measure of the node, which records how it read each child, for a
3261    /// later keep of its measurement.
3262    #[default]
3263    Measure,
3264    /// An intrinsic size of the node, which its parent asks: the record of
3265    /// the node's last measure stays as it was.
3266    Intrinsics,
3267}
3268
3269impl LayoutChildFrame {
3270    fn bind(&self, builder: &Rc<LayoutBuilderState>, pass: ChildPass) {
3271        self.error.borrow_mut().take();
3272        *self.builder.borrow_mut() = Some(Rc::clone(builder));
3273        self.pass.set(pass);
3274    }
3275
3276    fn measures(&self) -> bool {
3277        self.pass.get() == ChildPass::Measure
3278    }
3279
3280    fn unbind(&self) {
3281        self.builder.borrow_mut().take();
3282    }
3283
3284    fn record_error(&self, err: NodeError) {
3285        if self.builder.borrow().is_none() {
3286            return;
3287        }
3288        let mut slot = self.error.borrow_mut();
3289        if slot.is_none() {
3290            *slot = Some(err);
3291        }
3292    }
3293}
3294
3295struct LayoutChildBinding<'a> {
3296    cache: &'a LayoutNodeCacheHandles,
3297    layout_state: Option<&'a Rc<RefCell<LayoutState>>>,
3298    parent_data: Option<cranpose_ui_layout::ParentData>,
3299    /// The child's own layout or measure is dirty.
3300    dirty: bool,
3301    /// Only nodes below the child are dirty.
3302    descendant_dirty: bool,
3303}
3304
3305/// Reads a child that is a layout node or a subcompose node: its flags,
3306/// cache, layout properties and placement. `None` for another node type.
3307fn read_layout_child<R>(
3308    applier: &mut MemoryApplier,
3309    child_id: NodeId,
3310    read: impl Fn(
3311        &dyn Node,
3312        &LayoutNodeCacheHandles,
3313        &dyn Fn() -> crate::modifier::LayoutProperties,
3314        bool,
3315    ) -> R,
3316) -> Result<Option<R>, NodeError> {
3317    match applier.with_node::<LayoutNode, _>(child_id, |child| {
3318        read(
3319            &*child,
3320            child.cache_handles(),
3321            &|| child.resolved_modifiers().layout_properties(),
3322            child.is_placed(),
3323        )
3324    }) {
3325        Ok(value) => Ok(Some(value)),
3326        Err(NodeError::TypeMismatch { .. }) => {
3327            match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
3328                read(
3329                    &*child,
3330                    child.cache_handles(),
3331                    &|| child.resolved_modifiers().layout_properties(),
3332                    child.layout_state().is_placed(),
3333                )
3334            }) {
3335                Ok(value) => Ok(Some(value)),
3336                Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
3337                Err(err) => Err(err),
3338            }
3339        }
3340        Err(NodeError::Missing { .. }) => Ok(None),
3341        Err(err) => Err(err),
3342    }
3343}
3344
3345/// Whether a parent's policy, given `measured` in place of `previous`, would
3346/// lay its children out the same: the size and baselines it reads and the
3347/// offset it places the child by are equal.
3348fn measures_the_same_for_parent(previous: &MeasuredNode, measured: &MeasuredNode) -> bool {
3349    let (before, after) = (
3350        previous.alignment_lines_for_parent(),
3351        measured.alignment_lines_for_parent(),
3352    );
3353    previous.size_for_parent() == measured.size_for_parent()
3354        && previous.offset == measured.offset
3355        && before.first_baseline() == after.first_baseline()
3356        && before.last_baseline() == after.last_baseline()
3357}
3358
3359fn parent_data_of(props: crate::modifier::LayoutProperties) -> cranpose_ui_layout::ParentData {
3360    let weight = props.weight().unwrap_or_default();
3361    cranpose_ui_layout::ParentData {
3362        weight: weight.weight,
3363        fill: weight.fill,
3364        box_alignment: props.box_alignment(),
3365        row_alignment: props.row_alignment(),
3366        row_baseline: props.row_baseline(),
3367        column_alignment: props.column_alignment(),
3368    }
3369}
3370
3371struct LayoutChildMeasureState {
3372    node_id: NodeId,
3373    frame: Rc<LayoutChildFrame>,
3374    cache: RefCell<LayoutNodeCacheHandles>,
3375    cache_epoch: Cell<u64>,
3376    force_remeasure: Cell<bool>,
3377    parent_data: Cell<Option<cranpose_ui_layout::ParentData>>,
3378    /// The constraints the parent's last measure gave the child.
3379    measured_constraints: Cell<Option<Constraints>>,
3380    /// Whether the parent's last measure read the child's intrinsic sizes.
3381    read_intrinsics: Cell<bool>,
3382    measured: RefCell<Option<Rc<MeasuredNode>>>,
3383    last_position: Cell<Option<Point>>,
3384    layout_state: RefCell<Option<Rc<RefCell<LayoutState>>>>,
3385}
3386
3387impl LayoutChildMeasureState {
3388    fn placement_position(&self, placement: Option<&Placement>) -> Point {
3389        placement
3390            .map(|placement| Point {
3391                x: placement.x,
3392                y: placement.y,
3393            })
3394            .or_else(|| self.last_position.get())
3395            .unwrap_or_default()
3396    }
3397
3398    fn new(node_id: NodeId, frame: Rc<LayoutChildFrame>) -> Rc<Self> {
3399        Rc::new(Self {
3400            node_id,
3401            frame,
3402            cache: RefCell::new(LayoutNodeCacheHandles::default()),
3403            cache_epoch: Cell::new(0),
3404            force_remeasure: Cell::new(true),
3405            parent_data: Cell::new(None),
3406            measured_constraints: Cell::new(None),
3407            read_intrinsics: Cell::new(false),
3408            measured: RefCell::new(None),
3409            last_position: Cell::new(None),
3410            layout_state: RefCell::new(None),
3411        })
3412    }
3413
3414    /// Binds the child for a measure of its parent. A child with only dirty
3415    /// nodes below it keeps its cache: it measures again, but may keep its
3416    /// measurement.
3417    fn bind(&self, binding: LayoutChildBinding<'_>, pass: &LayoutBuilderState) {
3418        let child_epoch = binding.cache.epoch();
3419        let stale = binding.dirty || child_epoch < pass.cache_floor;
3420        let cache_epoch = if stale { pass.cache_epoch } else { child_epoch };
3421        binding.cache.activate(cache_epoch);
3422        self.cache.borrow_mut().clone_from(binding.cache);
3423        self.cache_epoch.set(cache_epoch);
3424        self.force_remeasure.set(stale || binding.descendant_dirty);
3425        self.parent_data.set(binding.parent_data);
3426        if self.frame.measures() {
3427            self.measured.borrow_mut().take();
3428            self.last_position.set(None);
3429            self.measured_constraints.set(None);
3430            self.read_intrinsics.set(false);
3431        }
3432        let mut layout_state = self.layout_state.borrow_mut();
3433        let shared = layout_state
3434            .as_ref()
3435            .zip(binding.layout_state)
3436            .is_some_and(|(current, bound)| Rc::ptr_eq(current, bound));
3437        if !shared {
3438            *layout_state = binding.layout_state.cloned();
3439        }
3440    }
3441
3442    fn place_retained(&self, position: Point) {
3443        self.last_position.set(Some(position));
3444        let builder = self.frame.builder.borrow();
3445        let Some(builder) = builder.as_ref() else {
3446            return;
3447        };
3448        if let Some(layout_state) = self.layout_state.borrow().as_ref() {
3449            layout_state.borrow_mut().place(position);
3450            return;
3451        }
3452        let Ok(mut applier) = builder.applier.try_borrow_typed() else {
3453            return;
3454        };
3455        let _ = applier.with_node::<SubcomposeLayoutNode, _>(self.node_id, |node| {
3456            node.set_position(position);
3457        });
3458    }
3459
3460    /// The intrinsic size of a child that is a layout node, from its
3461    /// modifier chain and measure policy, without measuring it. `None` for
3462    /// another node type, which measures for it.
3463    fn layout_intrinsic(&self, kind: IntrinsicKind) -> Option<f32> {
3464        let builder = self.frame.builder.borrow();
3465        let builder = builder.as_ref()?;
3466        match builder.layout_node_intrinsic(self.node_id, kind) {
3467            Ok(value) => value,
3468            Err(err) => {
3469                self.frame.record_error(err);
3470                Some(0.0)
3471            }
3472        }
3473    }
3474
3475    fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
3476        let builder = self.frame.builder.borrow();
3477        let builder = builder.as_ref().ok_or(NodeError::MissingContext {
3478            id: self.node_id,
3479            reason: "layout child applier not configured",
3480        })?;
3481        builder.measure_node(self.node_id, constraints)
3482    }
3483
3484    fn measure_cached(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
3485        if self.frame.measures() {
3486            self.measured_constraints.set(Some(constraints));
3487        }
3488        let cache = self.cache.borrow();
3489        cache.activate(self.cache_epoch.get());
3490        if !self.force_remeasure.get()
3491            && let Some(cached) = cache.get_measurement(constraints)
3492        {
3493            return Some(cached);
3494        }
3495
3496        match self.perform_measure(constraints) {
3497            Ok(measured) => {
3498                self.force_remeasure.set(false);
3499                cache.store_measurement(constraints, Rc::clone(&measured));
3500                Some(measured)
3501            }
3502            Err(err) => {
3503                self.frame.record_error(err);
3504                None
3505            }
3506        }
3507    }
3508}
3509
3510struct LayoutChildMeasurable {
3511    state: Rc<LayoutChildMeasureState>,
3512}
3513
3514impl LayoutChildMeasurable {
3515    fn new(state: Rc<LayoutChildMeasureState>) -> Self {
3516        Self { state }
3517    }
3518
3519    fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
3520        if self.state.frame.builder.borrow().is_none() {
3521            return None;
3522        }
3523        self.state.parent_data.get()
3524    }
3525
3526    fn intrinsic(
3527        &self,
3528        kind: IntrinsicKind,
3529        constraints: Constraints,
3530        extent: fn(Size) -> f32,
3531    ) -> f32 {
3532        let state = &self.state;
3533        if state.frame.measures() {
3534            state.read_intrinsics.set(true);
3535        }
3536        let cache = state.cache.borrow();
3537        cache.activate(state.cache_epoch.get());
3538        if !state.force_remeasure.get()
3539            && let Some(value) = cache.get_intrinsic(&kind)
3540        {
3541            return value;
3542        }
3543        let value = match state.layout_intrinsic(kind) {
3544            Some(value) => value,
3545            None => state
3546                .measure_cached(constraints)
3547                .map_or(0.0, |node| extent(node.size_for_parent())),
3548        };
3549        cache.store_intrinsic(kind, value);
3550        value
3551    }
3552}
3553
3554impl PlaceTarget for LayoutChildMeasureState {
3555    fn place(&self, x: f32, y: f32) {
3556        let internal_offset = self
3557            .measured
3558            .borrow()
3559            .as_ref()
3560            .map(|measured| measured.offset)
3561            .unwrap_or_default();
3562        self.place_retained(Point {
3563            x: x + internal_offset.x,
3564            y: y + internal_offset.y,
3565        });
3566    }
3567}
3568
3569impl Measurable for LayoutChildMeasurable {
3570    fn measure(&self, constraints: Constraints) -> Placeable {
3571        let state = &self.state;
3572        let measured = state.measure_cached(constraints);
3573        let (measured_size, size_for_parent) = measured.as_ref().map_or(
3574            (
3575                Size {
3576                    width: 0.0,
3577                    height: 0.0,
3578                },
3579                Size {
3580                    width: 0.0,
3581                    height: 0.0,
3582                },
3583            ),
3584            |measured| (measured.size, measured.size_for_parent()),
3585        );
3586        if let Some(layout_state) = state.layout_state.borrow().as_ref() {
3587            layout_state.borrow_mut().set_size(measured_size);
3588        }
3589        let alignment_lines = measured
3590            .as_ref()
3591            .map_or_else(AlignmentLines::default, |node| {
3592                node.alignment_lines_for_parent()
3593            });
3594        *state.measured.borrow_mut() = measured;
3595
3596        Placeable::with_place_target(
3597            size_for_parent.width,
3598            size_for_parent.height,
3599            state.node_id,
3600            Rc::clone(&self.state) as Rc<dyn PlaceTarget>,
3601        )
3602        .with_alignment_lines(alignment_lines)
3603    }
3604
3605    fn min_intrinsic_width(&self, height: f32) -> f32 {
3606        self.intrinsic(
3607            IntrinsicKind::MinWidth(height),
3608            Constraints {
3609                min_width: 0.0,
3610                max_width: f32::INFINITY,
3611                min_height: height,
3612                max_height: height,
3613            },
3614            |size| size.width,
3615        )
3616    }
3617
3618    fn max_intrinsic_width(&self, height: f32) -> f32 {
3619        self.intrinsic(
3620            IntrinsicKind::MaxWidth(height),
3621            Constraints {
3622                min_width: 0.0,
3623                max_width: f32::INFINITY,
3624                min_height: 0.0,
3625                max_height: height,
3626            },
3627            |size| size.width,
3628        )
3629    }
3630
3631    fn min_intrinsic_height(&self, width: f32) -> f32 {
3632        self.intrinsic(
3633            IntrinsicKind::MinHeight(width),
3634            Constraints {
3635                min_width: width,
3636                max_width: width,
3637                min_height: 0.0,
3638                max_height: f32::INFINITY,
3639            },
3640            |size| size.height,
3641        )
3642    }
3643
3644    fn max_intrinsic_height(&self, width: f32) -> f32 {
3645        self.intrinsic(
3646            IntrinsicKind::MaxHeight(width),
3647            Constraints {
3648                min_width: 0.0,
3649                max_width: width,
3650                min_height: 0.0,
3651                max_height: f32::INFINITY,
3652            },
3653            |size| size.height,
3654        )
3655    }
3656
3657    fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3658        let parent_data = self.resolved_parent_data()?;
3659        if !parent_data.has_weight() {
3660            return None;
3661        }
3662        Some(cranpose_ui_layout::FlexParentData::new(
3663            parent_data.weight,
3664            parent_data.fill,
3665        ))
3666    }
3667
3668    fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3669        self.resolved_parent_data().unwrap_or_default()
3670    }
3671}
3672
3673#[derive(Clone)]
3674struct RuntimeNodeMetadata {
3675    modifier: Modifier,
3676    resolved_modifiers: ResolvedModifiers,
3677    modifier_slices: Rc<ModifierNodeSlices>,
3678    semantics: Option<Rc<SemanticsConfiguration>>,
3679    role: SemanticsRole,
3680    button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3681}
3682
3683impl Default for RuntimeNodeMetadata {
3684    fn default() -> Self {
3685        Self {
3686            modifier: Modifier::empty(),
3687            resolved_modifiers: ResolvedModifiers::default(),
3688            modifier_slices: Rc::default(),
3689            semantics: None,
3690            role: SemanticsRole::Unknown,
3691            button_handler: None,
3692        }
3693    }
3694}
3695
3696fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3697    modifier_slices
3698        .annotated_text()
3699        .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3700            value: SemanticsText(Rc::clone(text)),
3701        })
3702}
3703
3704fn runtime_metadata_for(
3705    applier: &mut MemoryApplier,
3706    node_id: NodeId,
3707) -> Result<RuntimeNodeMetadata, NodeError> {
3708    if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3709        let modifier = layout.modifier.clone();
3710        let resolved_modifiers = layout.resolved_modifiers();
3711        let modifier_slices = layout.modifier_slices_snapshot();
3712        let role = role_from_modifier_slices(&modifier_slices);
3713
3714        RuntimeNodeMetadata {
3715            modifier,
3716            resolved_modifiers,
3717            modifier_slices,
3718            semantics: layout.semantics_configuration().map(Rc::new),
3719            role,
3720            button_handler: None,
3721        }
3722    }) {
3723        return Ok(meta);
3724    }
3725
3726    if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
3727        applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3728            (
3729                node.modifier(),
3730                node.resolved_modifiers(),
3731                node.modifier_slices_snapshot(),
3732                node.semantics_configuration().map(Rc::new),
3733            )
3734        })
3735    {
3736        return Ok(RuntimeNodeMetadata {
3737            modifier,
3738            resolved_modifiers,
3739            modifier_slices,
3740            semantics,
3741            role: SemanticsRole::Subcompose,
3742            button_handler: None,
3743        });
3744    }
3745    Ok(RuntimeNodeMetadata::default())
3746}
3747
3748fn clear_semantics_dirty_flags(
3749    applier: &mut MemoryApplier,
3750    node: &MeasuredNode,
3751) -> Result<(), NodeError> {
3752    match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3753        layout.clear_needs_semantics();
3754    }) {
3755        Ok(()) => {}
3756        Err(NodeError::Missing { .. }) => {}
3757        Err(NodeError::TypeMismatch { .. }) => {
3758            match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3759                subcompose.clear_needs_semantics();
3760            }) {
3761                Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
3762                Err(err) => return Err(err),
3763            }
3764        }
3765        Err(err) => return Err(err),
3766    }
3767
3768    for child in &node.children {
3769        clear_semantics_dirty_flags(applier, &child.node.borrow())?;
3770    }
3771
3772    Ok(())
3773}
3774
3775fn build_semantics_tree_from_live_nodes(
3776    applier: &mut MemoryApplier,
3777    node: &MeasuredNode,
3778) -> Result<SemanticsTree, NodeError> {
3779    Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3780        applier, node, None,
3781    )?))
3782}
3783
3784fn semantics_node_from_parts(
3785    node_id: NodeId,
3786    node_generation: u32,
3787    mut role: SemanticsRole,
3788    config: Option<SemanticsConfiguration>,
3789    children: Vec<SemanticsNode>,
3790    held_details: Option<Box<SemanticsDetails>>,
3791    bounds: GeometryRect,
3792) -> SemanticsNode {
3793    let focusable = crate::focus_dispatch::has_focus_target(node_id);
3794    let mut node = SemanticsNode {
3795        node_id,
3796        bounds,
3797        node_generation,
3798        children,
3799        focusable,
3800        focused: focusable && crate::focus_dispatch::active_focus_target() == Some(node_id),
3801        details: held_details,
3802        ..SemanticsNode::default()
3803    };
3804    let details = match config {
3805        Some(mut config) => {
3806            if config.role == Some(SemanticsWidgetRole::Button) {
3807                role = SemanticsRole::Button;
3808            }
3809            if config.is_activatable() {
3810                node.actions.push(SemanticsAction::Click {
3811                    handler: SemanticsCallback::new(node_id),
3812                });
3813            }
3814            let on_click_label = config.on_click_label.take().or_else(|| {
3815                config
3816                    .on_click
3817                    .as_ref()
3818                    .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
3819            });
3820            node.widget_role = config.role;
3821            node.description = config.content_description.take();
3822            node.on_click = config.on_click.take();
3823            node.selected = config.selected;
3824            node.toggled = config.toggled;
3825            node.enabled = config.enabled;
3826            node.hidden = config.hidden;
3827            node.merge_descendants = config.merge_descendants;
3828            node.traversal_index = config.traversal_index;
3829            node.text = config.text.take();
3830            let is_modal = config.is_modal
3831                && modal_takes_space(Size {
3832                    width: bounds.width,
3833                    height: bounds.height,
3834                });
3835            SemanticsDetails::from_configuration(config, on_click_label, is_modal)
3836        }
3837        None => SemanticsDetails::NONE,
3838    };
3839    node.set_details(details);
3840    node.role = role;
3841    node
3842}
3843
3844fn build_semantics_node_from_live_nodes(
3845    applier: &mut MemoryApplier,
3846    node: &MeasuredNode,
3847    origin: Option<Point>,
3848) -> Result<SemanticsNode, NodeError> {
3849    let (role, config, (bounds, content), placement) =
3850        match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3851            let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
3852            let config = layout.semantics_configuration();
3853            layout.clear_needs_semantics();
3854            let state = layout.layout_state();
3855            (
3856                role,
3857                config,
3858                semantics_placement(&state, origin),
3859                SemanticsPlacement::of(&state),
3860            )
3861        }) {
3862            Ok(data) => data,
3863            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3864                match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3865                    subcompose.clear_needs_semantics();
3866                    let state = subcompose.layout_state();
3867                    (
3868                        SemanticsRole::Subcompose,
3869                        subcompose.semantics_configuration(),
3870                        semantics_placement(&state, origin),
3871                        SemanticsPlacement::of(&state),
3872                    )
3873                }) {
3874                    Ok(data) => data,
3875                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3876                        let top_left = origin.unwrap_or_default();
3877                        (
3878                            SemanticsRole::Unknown,
3879                            None,
3880                            (
3881                                GeometryRect::from_origin_size(top_left, node.size),
3882                                top_left,
3883                            ),
3884                            SemanticsPlacement::default(),
3885                        )
3886                    }
3887                    Err(err) => return Err(err),
3888                }
3889            }
3890            Err(err) => return Err(err),
3891        };
3892
3893    let mut children = Vec::with_capacity(node.children.len());
3894    for child in &node.children {
3895        children.push(build_semantics_node_from_live_nodes(
3896            applier,
3897            &child.node.borrow(),
3898            Some(content),
3899        )?);
3900    }
3901
3902    Ok(SemanticsNode {
3903        placement,
3904        ..semantics_node_from_parts(
3905            node.node_id,
3906            applier.node_generation(node.node_id),
3907            role,
3908            config,
3909            children,
3910            None,
3911            bounds,
3912        )
3913    })
3914}
3915
3916fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
3917    stats.semantics_node_count += 1;
3918    stats.semantics_action_count += node.actions.len();
3919    stats.semantics_action_capacity += node.actions.capacity();
3920    stats.semantics_child_count += node.children.len();
3921    stats.semantics_child_capacity += node.children.capacity();
3922    stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
3923    stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
3924    if node.details.is_some() {
3925        stats.semantics_heap_bytes += size_of::<SemanticsDetails>();
3926    }
3927
3928    if let Some(description) = &node.description {
3929        stats.semantics_description_count += 1;
3930        stats.semantics_description_bytes += description.capacity();
3931        stats.semantics_heap_bytes += description.capacity();
3932    }
3933
3934    for child in &node.children {
3935        record_semantics_allocation_stats(child, stats);
3936    }
3937}
3938
3939fn record_layout_box_allocation_stats(
3940    layout_box: &LayoutBox,
3941    stats: &mut LayoutAllocationDebugStats,
3942) {
3943    stats.layout_box_count += 1;
3944    stats.layout_box_child_count += layout_box.children.len();
3945    stats.layout_box_child_capacity += layout_box.children.capacity();
3946    stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
3947    stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
3948
3949    for child in &layout_box.children {
3950        record_layout_box_allocation_stats(child, stats);
3951    }
3952}
3953
3954fn build_layout_tree(
3955    applier: &mut MemoryApplier,
3956    node: &MeasuredNode,
3957) -> Result<LayoutTree, NodeError> {
3958    fn place(
3959        applier: &mut MemoryApplier,
3960        node: &MeasuredNode,
3961        origin: Point,
3962        parent_transform: ProjectiveTransform,
3963    ) -> Result<LayoutBox, NodeError> {
3964        let top_left = Point {
3965            x: origin.x + node.offset.x,
3966            y: origin.y + node.offset.y,
3967        };
3968        let rect = GeometryRect {
3969            x: top_left.x,
3970            y: top_left.y,
3971            width: node.size.width,
3972            height: node.size.height,
3973        };
3974        let (data, window_transform) =
3975            snapshot_node_data(applier, node.node_id, top_left, node.size, parent_transform)?;
3976        let mut children = Vec::with_capacity(node.children.len());
3977        for child in &node.children {
3978            let child_node = child.node.borrow();
3979            if crate::modifier::is_window_root(applier, child_node.node_id) {
3980                continue;
3981            }
3982            let child_origin = Point {
3983                x: top_left.x + child.offset.x,
3984                y: top_left.y + child.offset.y,
3985            };
3986            children.push(place(applier, &child_node, child_origin, window_transform)?);
3987        }
3988        Ok(LayoutBox {
3989            node_generation: applier.node_generation(node.node_id),
3990            ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
3991        })
3992    }
3993
3994    Ok(LayoutTree::new(place(
3995        applier,
3996        node,
3997        Point { x: 0.0, y: 0.0 },
3998        ProjectiveTransform::identity(),
3999    )?))
4000}
4001
4002fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
4003    match &layout_box.node_data.kind {
4004        LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
4005        LayoutNodeKind::Spacer => SemanticsRole::Spacer,
4006        LayoutNodeKind::Unknown => SemanticsRole::Unknown,
4007        LayoutNodeKind::Button { .. } => SemanticsRole::Button,
4008        LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
4009    }
4010}
4011
4012fn build_semantics_node_from_layout_box(layout_box: &LayoutBox, origin: Point) -> SemanticsNode {
4013    let rect = layout_box.rect;
4014    let content = Point {
4015        x: rect.x + layout_box.content_offset.x,
4016        y: rect.y + layout_box.content_offset.y,
4017    };
4018    let children = layout_box
4019        .children
4020        .iter()
4021        .map(|child| build_semantics_node_from_layout_box(child, content))
4022        .collect();
4023
4024    SemanticsNode {
4025        placement: SemanticsPlacement {
4026            position: Point {
4027                x: rect.x - origin.x,
4028                y: rect.y - origin.y,
4029            },
4030            content_offset: layout_box.content_offset,
4031        },
4032        ..semantics_node_from_parts(
4033            layout_box.node_id,
4034            layout_box.node_generation,
4035            semantics_role_from_layout_box(layout_box),
4036            layout_box.node_data.semantics().cloned(),
4037            children,
4038            None,
4039            rect,
4040        )
4041    }
4042}
4043
4044fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
4045    match &info.role {
4046        SemanticsRole::Layout => LayoutNodeKind::Layout,
4047        SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
4048        SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
4049        SemanticsRole::Spacer => LayoutNodeKind::Spacer,
4050        SemanticsRole::Button => {
4051            let handler = info
4052                .button_handler
4053                .as_ref()
4054                .cloned()
4055                .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
4056            LayoutNodeKind::Button { on_click: handler }
4057        }
4058        SemanticsRole::Unknown => LayoutNodeKind::Unknown,
4059    }
4060}
4061
4062fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
4063    let horizontal = padding.horizontal_sum();
4064    let vertical = padding.vertical_sum();
4065    let min_width = (constraints.min_width - horizontal).max(0.0);
4066    let mut max_width = constraints.max_width;
4067    if max_width.is_finite() {
4068        max_width = (max_width - horizontal).max(0.0);
4069    }
4070    let min_height = (constraints.min_height - vertical).max(0.0);
4071    let mut max_height = constraints.max_height;
4072    if max_height.is_finite() {
4073        max_height = (max_height - vertical).max(0.0);
4074    }
4075    normalize_constraints(Constraints {
4076        min_width,
4077        max_width,
4078        min_height,
4079        max_height,
4080    })
4081}
4082
4083fn resolve_dimension(
4084    base: f32,
4085    explicit: DimensionConstraint,
4086    min_override: Option<f32>,
4087    max_override: Option<f32>,
4088    min_limit: f32,
4089    max_limit: f32,
4090) -> f32 {
4091    let mut min_bound = min_limit;
4092    if let Some(min_value) = min_override {
4093        min_bound = min_bound.max(min_value);
4094    }
4095
4096    let mut max_bound = if max_limit.is_finite() {
4097        max_limit
4098    } else {
4099        max_override.unwrap_or(max_limit)
4100    };
4101    if let Some(max_value) = max_override {
4102        if max_bound.is_finite() {
4103            max_bound = max_bound.min(max_value);
4104        } else {
4105            max_bound = max_value;
4106        }
4107    }
4108    if max_bound < min_bound {
4109        max_bound = min_bound;
4110    }
4111
4112    let mut size = match explicit {
4113        DimensionConstraint::Points(points) => points,
4114        DimensionConstraint::Fraction(fraction) => {
4115            if max_limit.is_finite() {
4116                max_limit * fraction.clamp(0.0, 1.0)
4117            } else {
4118                base
4119            }
4120        }
4121        DimensionConstraint::Unspecified => base,
4122        DimensionConstraint::Intrinsic(_) => base,
4123    };
4124
4125    size = clamp_dimension(size, min_bound, max_bound);
4126    size = clamp_dimension(size, min_limit, max_limit);
4127    size.max(0.0)
4128}
4129
4130fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
4131    let mut result = value.max(min);
4132    if max.is_finite() {
4133        result = result.min(max);
4134    }
4135    result
4136}
4137
4138fn normalize_constraints(mut constraints: Constraints) -> Constraints {
4139    if constraints.max_width < constraints.min_width {
4140        constraints.max_width = constraints.min_width;
4141    }
4142    if constraints.max_height < constraints.min_height {
4143        constraints.max_height = constraints.min_height;
4144    }
4145    constraints
4146}
4147
4148#[cfg(test)]
4149#[path = "tests/layout_tests.rs"]
4150mod tests;
4151
4152#[cfg(test)]
4153#[path = "tests/semantics_update_tests.rs"]
4154mod semantics_update_tests;
4155
4156#[cfg(test)]
4157#[path = "tests/coordinator_geometry_tests.rs"]
4158mod coordinator_geometry_tests;