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