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