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 observer = node_handle.observer_for_measure();
1669        let composer = Composer::new(
1670            Rc::clone(&slots_host),
1671            applier_host_dyn,
1672            runtime_handle,
1673            observer,
1674            Some(node_id),
1675        );
1676        composer.enter_phase(Phase::Measure);
1677
1678        let measure_error = RefCell::new(None);
1679        let measured_children = node_handle.measured_children_scratch();
1680
1681        let measure_result = node_handle.measure_with_cached_batch(
1682            &composer,
1683            node_id,
1684            inner_constraints,
1685            CachedBatchMeasureInputs {
1686                measurer: Box::new(|child_id: NodeId, child_constraints: Constraints| {
1687                    match self.measure_node(child_id, child_constraints) {
1688                        Ok(measured) => {
1689                            measured_children
1690                                .borrow_mut()
1691                                .insert(child_id, Rc::clone(&measured));
1692                            let size = measured.size_for_parent();
1693                            Placeable::value(size.width, size.height, child_id)
1694                                .with_alignment_lines(measured.alignment_lines_for_parent())
1695                        }
1696                        Err(err) => {
1697                            let mut slot = measure_error.borrow_mut();
1698                            if slot.is_none() {
1699                                *slot = Some(err);
1700                            }
1701                            Placeable::value(0.0, 0.0, child_id)
1702                        }
1703                    }
1704                }),
1705                cached_measure_batch_registrar: Box::new(
1706                    |child_ids: &[NodeId],
1707                     child_constraints: Constraints,
1708                     out: &mut Vec<Option<Size>>| {
1709                        out.clear();
1710                        out.resize(child_ids.len(), None);
1711
1712                        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1713                            return;
1714                        };
1715
1716                        let mut measured_children = measured_children.borrow_mut();
1717                        for (index, &child_id) in child_ids.iter().enumerate() {
1718                            match Self::cached_measure_node_with_applier(
1719                                &mut applier,
1720                                child_id,
1721                                child_constraints,
1722                            ) {
1723                                Ok(Some(measured)) => {
1724                                    out[index] = Some(measured.size);
1725                                    measured_children.insert(child_id, Rc::clone(&measured));
1726                                }
1727                                Ok(None) => {}
1728                                Err(err) => {
1729                                    let mut slot = measure_error.borrow_mut();
1730                                    if slot.is_none() {
1731                                        *slot = Some(err);
1732                                    }
1733                                    break;
1734                                }
1735                            }
1736                        }
1737                    },
1738                ),
1739                retained_measure_lookup: Box::new(|child_id| {
1740                    measured_children.borrow().get(&child_id).cloned()
1741                }),
1742                retained_measure_registrar: Box::new(|measurements| {
1743                    let mut measured_children = measured_children.borrow_mut();
1744                    for measured in measurements {
1745                        measured_children.insert(measured.node_id(), Rc::clone(measured));
1746                    }
1747                }),
1748                error: &measure_error,
1749            },
1750        )?;
1751        drop(composer);
1752        slots_guard.restore(slots_host.into_table()?);
1753
1754        if let Some(err) = measure_error.borrow_mut().take() {
1755            return Err(err);
1756        }
1757
1758        let cranpose_ui_layout::MeasureResult {
1759            size: measured_size,
1760            alignment_lines: explicit_lines,
1761            placements,
1762        } = measure_result;
1763
1764        let mut width = measured_size.width + padding.horizontal_sum();
1765        let mut height = measured_size.height + padding.vertical_sum();
1766
1767        width = resolve_dimension(
1768            width,
1769            props.width(),
1770            props.min_width(),
1771            props.max_width(),
1772            constraints.min_width,
1773            constraints.max_width,
1774        );
1775        height = resolve_dimension(
1776            height,
1777            props.height(),
1778            props.min_height(),
1779            props.max_height(),
1780            constraints.min_height,
1781            constraints.max_height,
1782        );
1783
1784        let mut children = Vec::with_capacity(placements.len());
1785        let mut alignment_lines = AlignmentLines::default();
1786        let mut measured_children_by_id = measured_children.borrow_mut();
1787
1788        if let Ok(mut applier) = self.applier.try_borrow_typed() {
1789            let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1790                parent_node.set_measured_size(Size { width, height });
1791                parent_node.clear_needs_measure();
1792                parent_node.clear_needs_layout();
1793            });
1794        }
1795
1796        for placement in &placements {
1797            let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1798                measured
1799            } else {
1800                self.measure_node(placement.node_id, inner_constraints)?
1801            };
1802            let policy_position = Point {
1803                x: padding.left + placement.x,
1804                y: padding.top + placement.y,
1805            };
1806            let retained_position = Point {
1807                x: policy_position.x + child.offset.x,
1808                y: policy_position.y + child.offset.y,
1809            };
1810            alignment_lines.merge(
1811                child
1812                    .alignment_lines_for_parent()
1813                    .translated(policy_position.y),
1814            );
1815
1816            if let Ok(mut applier) = self.applier.try_borrow_typed()
1817                && applier
1818                    .with_node::<LayoutNode, _>(placement.node_id, |node| {
1819                        node.set_position(retained_position);
1820                    })
1821                    .is_err()
1822            {
1823                let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1824                    node.set_position(retained_position);
1825                });
1826            }
1827
1828            children.push(MeasuredChild {
1829                node: child,
1830                offset: policy_position,
1831            });
1832        }
1833
1834        node_handle.set_active_children(children.iter().map(|c| c.node.node_id));
1835        node_handle.recycle_placement_scratch(placements);
1836
1837        Ok(Some(Rc::new(
1838            MeasuredNode::new(
1839                node_id,
1840                Size { width, height },
1841                offset,
1842                Point::default(),
1843                children,
1844            )
1845            .with_alignment_lines(
1846                alignment_lines
1847                    .with_overrides(explicit_lines.translated(padding.top))
1848                    .translated(offset.y),
1849            ),
1850        )))
1851    }
1852
1853    fn measure_layout_node(
1854        self: &Rc<Self>,
1855        node_id: NodeId,
1856        constraints: Constraints,
1857    ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1858        let Ok(mut applier) = self.applier.try_borrow_typed() else {
1859            return Ok(None);
1860        };
1861        let LayoutNodeMeasure {
1862            runtime_state,
1863            chain,
1864            mut pools,
1865        } = match applier
1866            .with_node::<LayoutNode, _>(node_id, |node| self.visit_layout_node(node, constraints))
1867        {
1868            Ok(LayoutNodeVisit::Measure(measure)) => measure,
1869            Ok(LayoutNodeVisit::Cached(measured)) => return Ok(Some(measured)),
1870            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
1871            Err(err) => return Err(err),
1872        };
1873
1874        let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
1875            state: &runtime_state,
1876        };
1877        self.bind_layout_children(&mut applier, &runtime_state, &pools.child_ids)?;
1878        drop(applier);
1879        pools.child_ids.clear();
1880
1881        let runtime_state = runtime_state.borrow();
1882        let measurement = self.measure_through_modifier_chain(
1883            node_id,
1884            &runtime_state,
1885            chain,
1886            constraints,
1887            &mut pools.placements,
1888            &mut pools.child_ids,
1889        );
1890
1891        if let Some(err) = runtime_state.frame.error.borrow_mut().take() {
1892            for child_state in &runtime_state.child_states {
1893                child_state.measured.borrow_mut().take();
1894            }
1895            self.with_applier_result(|applier| {
1896                applier.with_node::<LayoutNode, _>(node_id, |node| {
1897                    runtime_state.write_node_geometry(node_id, node, &measurement);
1898                })
1899            })
1900            .ok();
1901            return Err(err);
1902        }
1903
1904        let measured = Rc::new(
1905            MeasuredNode::new(
1906                node_id,
1907                measurement.size,
1908                measurement.offset,
1909                measurement.content_offset,
1910                runtime_state.measured_children(
1911                    &pools.placements,
1912                    &pools.child_ids,
1913                    measurement.content_offset,
1914                ),
1915            )
1916            .with_alignment_lines(measurement.alignment_lines)
1917            .with_window_root(measurement.window_root),
1918        );
1919
1920        self.with_applier_result(|applier| {
1921            applier.with_node::<LayoutNode, _>(node_id, |node| {
1922                node.cache_handles()
1923                    .store_measurement(constraints, Rc::clone(&measured));
1924                runtime_state.write_node_geometry(node_id, node, &measurement);
1925                node.clear_needs_measure();
1926                node.clear_needs_layout();
1927                node.set_measured_size(measurement.size);
1928                node.set_content_offset(measurement.content_offset);
1929            })
1930        })
1931        .ok();
1932
1933        Ok(Some(measured))
1934    }
1935
1936    fn visit_layout_node(
1937        &self,
1938        node: &mut LayoutNode,
1939        constraints: Constraints,
1940    ) -> LayoutNodeVisit<'_> {
1941        node.clear_placed();
1942        let cache = node.cache_handles();
1943        cache.activate(self.cache_epoch);
1944        if !node.needs_measure()
1945            && !node.needs_layout()
1946            && let Some(cached) = cache.get_measurement(constraints)
1947        {
1948            node.clear_needs_measure();
1949            node.clear_needs_layout();
1950            return LayoutNodeVisit::Cached(cached);
1951        }
1952
1953        let runtime_state = node.layout_runtime_state_handle();
1954        let chain = runtime_state.borrow_mut().bind_node(node);
1955        let mut pools = VecPools::acquire(&self.frame_arena);
1956        pools.child_ids.extend_from_slice(&node.children);
1957        LayoutNodeVisit::Measure(LayoutNodeMeasure {
1958            runtime_state,
1959            chain,
1960            pools,
1961        })
1962    }
1963
1964    fn bind_layout_children(
1965        self: &Rc<Self>,
1966        applier: &mut MemoryApplier,
1967        runtime_state: &RefCell<LayoutRuntimeState>,
1968        child_ids: &[NodeId],
1969    ) -> Result<(), NodeError> {
1970        let mut runtime_state = runtime_state.borrow_mut();
1971        runtime_state.frame.bind(self);
1972        let mut bound = 0;
1973        for &child_id in child_ids {
1974            if self.bind_layout_child(applier, &mut runtime_state, bound, child_id)? {
1975                bound += 1;
1976            }
1977        }
1978        runtime_state.truncate_children(bound);
1979        Ok(())
1980    }
1981
1982    fn bind_layout_child(
1983        &self,
1984        applier: &mut MemoryApplier,
1985        runtime_state: &mut LayoutRuntimeState,
1986        position: usize,
1987        child_id: NodeId,
1988    ) -> Result<bool, NodeError> {
1989        let bound = applier.with_node::<LayoutNode, _>(child_id, |child| {
1990            runtime_state.child_state_at(position, child_id).bind(
1991                LayoutChildBinding {
1992                    cache: child.cache_handles(),
1993                    layout_state: Some(child.layout_state_handle()),
1994                    parent_data: Some(parent_data_of(
1995                        child.resolved_modifiers().layout_properties(),
1996                    )),
1997                    dirty: child.needs_layout() || child.needs_measure(),
1998                },
1999                self,
2000            );
2001        });
2002        match bound {
2003            Ok(()) => Ok(true),
2004            Err(NodeError::TypeMismatch { .. }) => {
2005                match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2006                    runtime_state.child_state_at(position, child_id).bind(
2007                        LayoutChildBinding {
2008                            cache: child.cache_handles(),
2009                            layout_state: None,
2010                            parent_data: Some(parent_data_of(
2011                                child.resolved_modifiers().layout_properties(),
2012                            )),
2013                            dirty: child.needs_layout() || child.needs_measure(),
2014                        },
2015                        self,
2016                    );
2017                }) {
2018                    Ok(()) => Ok(true),
2019                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(false),
2020                    Err(err) => Err(err),
2021                }
2022            }
2023            Err(NodeError::Missing { .. }) => Ok(false),
2024            Err(err) => Err(err),
2025        }
2026    }
2027
2028    fn measure_through_modifier_chain(
2029        &self,
2030        node_id: NodeId,
2031        runtime_state: &LayoutRuntimeState,
2032        chain: ModifierChainInputs,
2033        constraints: Constraints,
2034        placements: &mut Vec<Placement>,
2035        placement_indices: &mut Vec<usize>,
2036    ) -> ModifierChainMeasurement {
2037        let scope = crate::density::DensityMeasureScope::new(chain.density);
2038
2039        if !chain.uses_chain {
2040            let measurement = runtime_state.measure_policy.measure_into(
2041                &scope,
2042                runtime_state.child_measurables.as_slice(),
2043                constraints,
2044                placements,
2045            );
2046            return ModifierChainMeasurement {
2047                size: measurement.size,
2048                alignment_lines: inherited_alignment_lines(
2049                    &runtime_state.child_states,
2050                    placements,
2051                    placement_indices,
2052                )
2053                .with_overrides(measurement.alignment_lines),
2054                content_offset: Point::default(),
2055                offset: chain.offset,
2056                window_root: chain.window_root,
2057                uses_chain: false,
2058            };
2059        }
2060
2061        let frame = CoordinatorFrame::new(
2062            &runtime_state.measure_policy,
2063            &scope,
2064            runtime_state.child_measurables.as_slice(),
2065            &runtime_state.child_states,
2066            placements,
2067            placement_indices,
2068        );
2069        let placeable = runtime_state
2070            .coordinator_chain
2071            .measure_from(0, &frame, constraints);
2072        let (content_x, content_y) = placeable.content_offset();
2073
2074        let invalidations = frame.take_invalidations();
2075        if !invalidations.is_empty() {
2076            self.with_applier_result(|applier| {
2077                applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2078                    for kind in invalidations {
2079                        match kind {
2080                            InvalidationKind::Layout => layout_node.mark_needs_measure(),
2081                            InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2082                            InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2083                            InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2084                            InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2085                        }
2086                    }
2087                })
2088            })
2089            .ok();
2090        }
2091
2092        ModifierChainMeasurement {
2093            alignment_lines: placeable.alignment_lines(),
2094            size: Size {
2095                width: placeable.width(),
2096                height: placeable.height(),
2097            },
2098            content_offset: Point {
2099                x: content_x - chain.offset.x,
2100                y: content_y - chain.offset.y,
2101            },
2102            offset: chain.offset,
2103            window_root: chain.window_root,
2104            uses_chain: true,
2105        }
2106    }
2107}
2108
2109struct VecPools<'a> {
2110    arena: &'a RefCell<FrameLayoutArena>,
2111    child_ids: Vec<NodeId>,
2112    placements: Vec<Placement>,
2113}
2114
2115impl<'a> VecPools<'a> {
2116    fn acquire(arena: &'a RefCell<FrameLayoutArena>) -> Self {
2117        let mut pools = arena.borrow_mut();
2118        let child_ids = pools.tmp_child_ids.acquire();
2119        let placements = pools.tmp_placements.acquire();
2120        Self {
2121            arena,
2122            child_ids,
2123            placements,
2124        }
2125    }
2126}
2127
2128impl Drop for VecPools<'_> {
2129    fn drop(&mut self) {
2130        let mut pools = self.arena.borrow_mut();
2131        pools
2132            .tmp_child_ids
2133            .release(std::mem::take(&mut self.child_ids));
2134        pools
2135            .tmp_placements
2136            .release(std::mem::take(&mut self.placements));
2137    }
2138}
2139
2140struct SlotsGuard<'a> {
2141    table: &'a RefCell<SlotTable>,
2142    slots: Option<SlotTable>,
2143}
2144
2145impl<'a> SlotsGuard<'a> {
2146    fn take(table: &'a RefCell<SlotTable>) -> Self {
2147        let slots = std::mem::take(&mut *table.borrow_mut());
2148        Self {
2149            table,
2150            slots: Some(slots),
2151        }
2152    }
2153
2154    fn host(&mut self) -> Rc<SlotsHost> {
2155        let slots = self.slots.take().unwrap_or_default();
2156        Rc::new(SlotsHost::new(slots))
2157    }
2158
2159    fn restore(&mut self, slots: SlotTable) {
2160        debug_assert!(self.slots.is_none());
2161        self.slots = Some(slots);
2162    }
2163}
2164
2165impl Drop for SlotsGuard<'_> {
2166    fn drop(&mut self) {
2167        if let Some(slots) = self.slots.take() {
2168            *self.table.borrow_mut() = slots;
2169        }
2170    }
2171}
2172
2173#[derive(Debug, Clone)]
2174pub(crate) struct MeasuredNode {
2175    node_id: NodeId,
2176    size: Size,
2177    offset: Point,
2178    content_offset: Point,
2179    alignment_lines: AlignmentLines,
2180    children: Vec<MeasuredChild>,
2181    window_root: bool,
2182}
2183
2184impl MeasuredNode {
2185    fn new(
2186        node_id: NodeId,
2187        size: Size,
2188        offset: Point,
2189        content_offset: Point,
2190        children: Vec<MeasuredChild>,
2191    ) -> Self {
2192        Self {
2193            node_id,
2194            size,
2195            offset,
2196            content_offset,
2197            alignment_lines: AlignmentLines::default(),
2198            children,
2199            window_root: false,
2200        }
2201    }
2202
2203    fn with_window_root(mut self, window_root: bool) -> Self {
2204        self.window_root = window_root;
2205        self
2206    }
2207
2208    fn with_alignment_lines(mut self, alignment_lines: AlignmentLines) -> Self {
2209        self.alignment_lines = alignment_lines;
2210        self
2211    }
2212
2213    pub(crate) fn alignment_lines_for_parent(&self) -> AlignmentLines {
2214        if self.window_root {
2215            AlignmentLines::default()
2216        } else {
2217            self.alignment_lines
2218        }
2219    }
2220
2221    pub(crate) fn size_for_parent(&self) -> Size {
2222        if self.window_root {
2223            Size::new(0.0, 0.0)
2224        } else {
2225            self.size
2226        }
2227    }
2228
2229    #[cfg(test)]
2230    pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2231        Self::new(
2232            node_id,
2233            size,
2234            Point::default(),
2235            Point::default(),
2236            Vec::new(),
2237        )
2238    }
2239
2240    pub(crate) fn node_id(&self) -> NodeId {
2241        self.node_id
2242    }
2243
2244    pub(crate) fn size(&self) -> Size {
2245        self.size
2246    }
2247}
2248
2249#[derive(Debug, Clone)]
2250struct MeasuredChild {
2251    node: Rc<MeasuredNode>,
2252    offset: Point,
2253}
2254
2255struct CoordinatorFrame<'a> {
2256    measure_policy: &'a Rc<dyn MeasurePolicy>,
2257    scope: &'a dyn cranpose_ui_layout::MeasureScope,
2258    measurables: &'a [Box<dyn Measurable>],
2259    child_states: &'a [Rc<LayoutChildMeasureState>],
2260    placements: RefCell<&'a mut Vec<Placement>>,
2261    placement_indices: RefCell<&'a mut Vec<usize>>,
2262    context: RefCell<LayoutNodeContext>,
2263}
2264
2265impl<'a> CoordinatorFrame<'a> {
2266    fn new(
2267        measure_policy: &'a Rc<dyn MeasurePolicy>,
2268        scope: &'a dyn cranpose_ui_layout::MeasureScope,
2269        measurables: &'a [Box<dyn Measurable>],
2270        child_states: &'a [Rc<LayoutChildMeasureState>],
2271        placements: &'a mut Vec<Placement>,
2272        placement_indices: &'a mut Vec<usize>,
2273    ) -> Self {
2274        Self {
2275            measure_policy,
2276            scope,
2277            measurables,
2278            child_states,
2279            placements: RefCell::new(placements),
2280            placement_indices: RefCell::new(placement_indices),
2281            context: RefCell::new(LayoutNodeContext::new(scope.density())),
2282        }
2283    }
2284
2285    fn take_invalidations(&self) -> Vec<InvalidationKind> {
2286        self.context.borrow_mut().take_invalidations()
2287    }
2288}
2289
2290struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2291    chain: &'chain CoordinatorChain,
2292    frame: &'frame_ref CoordinatorFrame<'frame_data>,
2293    index: usize,
2294    alignment_lines: Cell<AlignmentLines>,
2295}
2296
2297impl<'chain, 'frame_ref, 'frame_data> CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2298    fn new(
2299        chain: &'chain CoordinatorChain,
2300        frame: &'frame_ref CoordinatorFrame<'frame_data>,
2301        index: usize,
2302    ) -> Self {
2303        Self {
2304            chain,
2305            frame,
2306            index,
2307            alignment_lines: Cell::default(),
2308        }
2309    }
2310}
2311
2312impl Measurable for CoordinatorLink<'_, '_, '_> {
2313    fn measure(&self, constraints: Constraints) -> Placeable {
2314        let placeable = self.chain.measure_from(self.index, self.frame, constraints);
2315        self.alignment_lines.set(placeable.alignment_lines());
2316        placeable
2317    }
2318
2319    fn min_intrinsic_width(&self, height: f32) -> f32 {
2320        self.chain
2321            .min_intrinsic_width_from(self.index, self.frame, height)
2322    }
2323
2324    fn max_intrinsic_width(&self, height: f32) -> f32 {
2325        self.chain
2326            .max_intrinsic_width_from(self.index, self.frame, height)
2327    }
2328
2329    fn min_intrinsic_height(&self, width: f32) -> f32 {
2330        self.chain
2331            .min_intrinsic_height_from(self.index, self.frame, width)
2332    }
2333
2334    fn max_intrinsic_height(&self, width: f32) -> f32 {
2335        self.chain
2336            .max_intrinsic_height_from(self.index, self.frame, width)
2337    }
2338}
2339
2340struct CoordinatorNode {
2341    modifier_index: usize,
2342    node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2343    measured_size: Cell<Size>,
2344    accumulated_offset: Cell<Point>,
2345}
2346
2347impl CoordinatorNode {
2348    fn new(
2349        modifier_index: usize,
2350        node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2351    ) -> Self {
2352        Self {
2353            modifier_index,
2354            node,
2355            measured_size: Cell::new(Size::default()),
2356            accumulated_offset: Cell::new(Point::default()),
2357        }
2358    }
2359
2360    fn matches(
2361        &self,
2362        modifier_index: usize,
2363        node: &Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2364    ) -> bool {
2365        self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2366    }
2367
2368    #[cfg(test)]
2369    fn ptr(&self) -> usize {
2370        Rc::as_ptr(&self.node) as *const () as usize
2371    }
2372}
2373
2374#[derive(Default)]
2375struct CoordinatorChain {
2376    nodes: Vec<CoordinatorNode>,
2377    /// The size the node's own measure policy measured its content at.
2378    inner_size: Cell<Size>,
2379}
2380
2381impl CoordinatorChain {
2382    fn sync(&mut self, node: &LayoutNode) -> ModifierChainInputs {
2383        let density = node.density();
2384        let mut inputs = ModifierChainInputs {
2385            density,
2386            window_root: node.is_window_root(),
2387            offset: Point::default(),
2388            uses_chain: false,
2389        };
2390        let chain_handle = node.modifier_chain();
2391        if !chain_handle.has_layout_nodes() {
2392            return inputs;
2393        }
2394
2395        let chain = chain_handle.chain();
2396        let mut len = 0;
2397        let mut matches = true;
2398        chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2399            let Some(index) = node_ref.entry_index() else {
2400                return;
2401            };
2402            if let Some(node) = chain.get_node_rc(index) {
2403                matches = matches
2404                    && self
2405                        .nodes
2406                        .get(len)
2407                        .is_some_and(|candidate| candidate.matches(index, node));
2408                len += 1;
2409            }
2410            node_ref.with_node(|node| {
2411                if let Some(offset_node) = node
2412                    .as_any()
2413                    .downcast_ref::<crate::modifier_nodes::OffsetNode>()
2414                {
2415                    let delta = offset_node.device_offset(density.density());
2416                    inputs.offset.x += delta.x;
2417                    inputs.offset.y += delta.y;
2418                }
2419            });
2420        });
2421
2422        inputs.uses_chain = len > 0;
2423        if inputs.uses_chain && !(matches && len == self.nodes.len()) {
2424            self.rebuild(chain, len);
2425        }
2426        inputs
2427    }
2428
2429    fn rebuild(&mut self, chain: &cranpose_foundation::ModifierNodeChain, len: usize) {
2430        let mut previous_nodes = std::mem::take(&mut self.nodes);
2431        self.nodes.reserve(len);
2432        chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2433            let Some((index, node)) = node_ref
2434                .entry_index()
2435                .and_then(|index| chain.get_node_rc(index).map(|node| (index, node)))
2436            else {
2437                return;
2438            };
2439            match previous_nodes
2440                .iter()
2441                .position(|candidate| candidate.matches(index, node))
2442            {
2443                Some(position) => self.nodes.push(previous_nodes.swap_remove(position)),
2444                None => self
2445                    .nodes
2446                    .push(CoordinatorNode::new(index, Rc::clone(node))),
2447            }
2448        });
2449    }
2450
2451    fn measure_from(
2452        &self,
2453        index: usize,
2454        frame: &CoordinatorFrame<'_>,
2455        constraints: Constraints,
2456    ) -> Placeable {
2457        let Some(node) = self.nodes.get(index) else {
2458            let mut placements = frame.placements.borrow_mut();
2459            let measurement = frame.measure_policy.measure_into(
2460                frame.scope,
2461                frame.measurables,
2462                constraints,
2463                &mut placements,
2464            );
2465            self.inner_size.set(measurement.size);
2466            return Placeable::value(
2467                measurement.size.width,
2468                measurement.size.height,
2469                NodeId::default(),
2470            )
2471            .with_alignment_lines(
2472                inherited_alignment_lines(
2473                    frame.child_states,
2474                    &placements,
2475                    &mut frame.placement_indices.borrow_mut(),
2476                )
2477                .with_overrides(measurement.alignment_lines),
2478            );
2479        };
2480
2481        let wrapped = CoordinatorLink::new(self, frame, index + 1);
2482        let node_borrow = node.node.borrow();
2483
2484        let Some(layout_node) = node_borrow.as_layout_node() else {
2485            let placeable = wrapped.measure(constraints);
2486            node.measured_size.set(Size {
2487                width: placeable.width(),
2488                height: placeable.height(),
2489            });
2490            let child_accumulated = self.total_content_offset_from(index + 1);
2491            node.accumulated_offset.set(child_accumulated);
2492            return placeable;
2493        };
2494
2495        let result = match frame.context.try_borrow_mut() {
2496            Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
2497            Err(_) => {
2498                let mut temp = LayoutNodeContext::new(frame.scope.density());
2499                let result = layout_node.measure(&mut temp, &wrapped, constraints);
2500                if let Ok(mut context) = frame.context.try_borrow_mut() {
2501                    for kind in temp.take_invalidations() {
2502                        context.invalidate(kind);
2503                    }
2504                }
2505                result
2506            }
2507        };
2508
2509        node.measured_size.set(result.size);
2510        let local_offset = Point {
2511            x: result.placement_offset_x,
2512            y: result.placement_offset_y,
2513        };
2514        let child_accumulated = self.total_content_offset_from(index + 1);
2515        let accumulated = Point {
2516            x: local_offset.x + child_accumulated.x,
2517            y: local_offset.y + child_accumulated.y,
2518        };
2519        node.accumulated_offset.set(accumulated);
2520
2521        Placeable::value_with_offset(
2522            result.size.width,
2523            result.size.height,
2524            NodeId::default(),
2525            (accumulated.x, accumulated.y),
2526        )
2527        .with_alignment_lines(
2528            wrapped
2529                .alignment_lines
2530                .get()
2531                .translated(local_offset.y)
2532                .with_overrides(result.alignment_lines),
2533        )
2534    }
2535
2536    fn min_intrinsic_width_from(
2537        &self,
2538        index: usize,
2539        frame: &CoordinatorFrame<'_>,
2540        height: f32,
2541    ) -> f32 {
2542        let Some(node) = self.nodes.get(index) else {
2543            return frame
2544                .measure_policy
2545                .min_intrinsic_width(frame.measurables, height);
2546        };
2547        let wrapped = CoordinatorLink::new(self, frame, index + 1);
2548        let node_borrow = node.node.borrow();
2549        node_borrow.as_layout_node().map_or_else(
2550            || wrapped.min_intrinsic_width(height),
2551            |layout_node| layout_node.min_intrinsic_width(&wrapped, height, frame.scope.density()),
2552        )
2553    }
2554
2555    fn max_intrinsic_width_from(
2556        &self,
2557        index: usize,
2558        frame: &CoordinatorFrame<'_>,
2559        height: f32,
2560    ) -> f32 {
2561        let Some(node) = self.nodes.get(index) else {
2562            return frame
2563                .measure_policy
2564                .max_intrinsic_width(frame.measurables, height);
2565        };
2566        let wrapped = CoordinatorLink::new(self, frame, index + 1);
2567        let node_borrow = node.node.borrow();
2568        node_borrow.as_layout_node().map_or_else(
2569            || wrapped.max_intrinsic_width(height),
2570            |layout_node| layout_node.max_intrinsic_width(&wrapped, height, frame.scope.density()),
2571        )
2572    }
2573
2574    fn min_intrinsic_height_from(
2575        &self,
2576        index: usize,
2577        frame: &CoordinatorFrame<'_>,
2578        width: f32,
2579    ) -> f32 {
2580        let Some(node) = self.nodes.get(index) else {
2581            return frame
2582                .measure_policy
2583                .min_intrinsic_height(frame.measurables, width);
2584        };
2585        let wrapped = CoordinatorLink::new(self, frame, index + 1);
2586        let node_borrow = node.node.borrow();
2587        node_borrow.as_layout_node().map_or_else(
2588            || wrapped.min_intrinsic_height(width),
2589            |layout_node| layout_node.min_intrinsic_height(&wrapped, width, frame.scope.density()),
2590        )
2591    }
2592
2593    fn max_intrinsic_height_from(
2594        &self,
2595        index: usize,
2596        frame: &CoordinatorFrame<'_>,
2597        width: f32,
2598    ) -> f32 {
2599        let Some(node) = self.nodes.get(index) else {
2600            return frame
2601                .measure_policy
2602                .max_intrinsic_height(frame.measurables, width);
2603        };
2604        let wrapped = CoordinatorLink::new(self, frame, index + 1);
2605        let node_borrow = node.node.borrow();
2606        node_borrow.as_layout_node().map_or_else(
2607            || wrapped.max_intrinsic_height(width),
2608            |layout_node| layout_node.max_intrinsic_height(&wrapped, width, frame.scope.density()),
2609        )
2610    }
2611
2612    /// Writes where each coordinator and the node's content ended up in the
2613    /// last measure, relative to the node drawn at its `node_offset`, and
2614    /// says whether any of them moved.
2615    fn write_geometry(
2616        &self,
2617        geometry: &crate::modifier::CoordinatorGeometry,
2618        node_offset: Point,
2619    ) -> bool {
2620        let content = self.total_content_offset_from(0);
2621        let placed = |inner_offset: Point, size: Size| GeometryRect {
2622            x: content.x - inner_offset.x - node_offset.x,
2623            y: content.y - inner_offset.y - node_offset.y,
2624            width: size.width,
2625            height: size.height,
2626        };
2627        geometry.replace(
2628            self.nodes
2629                .iter()
2630                .map(|node| placed(node.accumulated_offset.get(), node.measured_size.get()))
2631                .chain(std::iter::once(placed(
2632                    Point::default(),
2633                    self.inner_size.get(),
2634                ))),
2635        )
2636    }
2637
2638    fn total_content_offset_from(&self, index: usize) -> Point {
2639        self.nodes
2640            .get(index)
2641            .map(|node| node.accumulated_offset.get())
2642            .unwrap_or_default()
2643    }
2644
2645    #[cfg(test)]
2646    fn debug_ptrs(&self) -> Vec<usize> {
2647        self.nodes.iter().map(CoordinatorNode::ptr).collect()
2648    }
2649}
2650
2651fn inherited_alignment_lines(
2652    child_states: &[Rc<LayoutChildMeasureState>],
2653    placements: &[Placement],
2654    placement_indices: &mut Vec<usize>,
2655) -> AlignmentLines {
2656    placement_indices.clear();
2657    let in_child_order = placements.len() == child_states.len()
2658        && placements
2659            .iter()
2660            .zip(child_states)
2661            .all(|(placement, child)| placement.node_id == child.node_id);
2662    if !in_child_order {
2663        placement_indices.extend(0..placements.len());
2664        placement_indices.sort_unstable_by_key(|&index| (placements[index].node_id, index));
2665    }
2666    let mut lines = AlignmentLines::default();
2667    for (index, child) in child_states.iter().enumerate() {
2668        let placement = placement_for_child(placements, placement_indices, index, child.node_id);
2669        if let Some(measured) = child.measured.borrow().as_ref() {
2670            lines.merge(
2671                measured
2672                    .alignment_lines_for_parent()
2673                    .translated(child.placement_position(placement).y),
2674            );
2675        }
2676    }
2677    lines
2678}
2679
2680fn placement_for_child<'a>(
2681    placements: &'a [Placement],
2682    indices: &[usize],
2683    child_index: usize,
2684    node_id: NodeId,
2685) -> Option<&'a Placement> {
2686    let index = if indices.is_empty() {
2687        child_index
2688    } else {
2689        let first = indices.partition_point(|&index| placements[index].node_id < node_id);
2690        *indices.get(first)?
2691    };
2692    placements
2693        .get(index)
2694        .filter(|placement| placement.node_id == node_id)
2695}
2696
2697pub(crate) struct LayoutRuntimeState {
2698    child_ids: Vec<NodeId>,
2699    child_states: Vec<Rc<LayoutChildMeasureState>>,
2700    child_measurables: Vec<Box<dyn Measurable>>,
2701    coordinator_chain: CoordinatorChain,
2702    measure_policy: Rc<dyn MeasurePolicy>,
2703    frame: Rc<LayoutChildFrame>,
2704}
2705
2706impl LayoutRuntimeState {
2707    pub(crate) fn new(measure_policy: Rc<dyn MeasurePolicy>) -> Self {
2708        Self {
2709            child_ids: Vec::new(),
2710            child_states: Vec::new(),
2711            child_measurables: Vec::new(),
2712            coordinator_chain: CoordinatorChain::default(),
2713            measure_policy,
2714            frame: Rc::default(),
2715        }
2716    }
2717
2718    fn bind_node(&mut self, node: &LayoutNode) -> ModifierChainInputs {
2719        if !Rc::ptr_eq(&self.measure_policy, &node.measure_policy) {
2720            self.measure_policy = Rc::clone(&node.measure_policy);
2721        }
2722        self.coordinator_chain.sync(node)
2723    }
2724
2725    fn child_state_at(&mut self, position: usize, child_id: NodeId) -> &LayoutChildMeasureState {
2726        if self.child_ids.get(position) != Some(&child_id) {
2727            let from = match self.child_ids[position..]
2728                .iter()
2729                .position(|&id| id == child_id)
2730            {
2731                Some(offset) => position + offset,
2732                None => {
2733                    let state = LayoutChildMeasureState::new(child_id, Rc::clone(&self.frame));
2734                    self.child_ids.push(child_id);
2735                    self.child_states.push(Rc::clone(&state));
2736                    self.child_measurables
2737                        .push(Box::new(LayoutChildMeasurable::new(state)));
2738                    self.child_ids.len() - 1
2739                }
2740            };
2741            self.child_ids.swap(position, from);
2742            self.child_states.swap(position, from);
2743            self.child_measurables.swap(position, from);
2744        }
2745        &self.child_states[position]
2746    }
2747
2748    fn truncate_children(&mut self, len: usize) {
2749        self.child_ids.truncate(len);
2750        self.child_states.truncate(len);
2751        self.child_measurables.truncate(len);
2752    }
2753
2754    fn measured_children(
2755        &self,
2756        placements: &[Placement],
2757        placement_indices: &[usize],
2758        content_offset: Point,
2759    ) -> Vec<MeasuredChild> {
2760        let mut measured_children = Vec::with_capacity(self.child_states.len());
2761        for (index, child_state) in self.child_states.iter().enumerate() {
2762            let Some(measured) = child_state.measured.borrow_mut().take() else {
2763                continue;
2764            };
2765            let placement =
2766                placement_for_child(placements, placement_indices, index, child_state.node_id);
2767            let base_position = child_state.placement_position(placement);
2768            if placement.is_some() {
2769                child_state.place_retained(Point {
2770                    x: base_position.x + measured.offset.x,
2771                    y: base_position.y + measured.offset.y,
2772                });
2773            }
2774            measured_children.push(MeasuredChild {
2775                node: measured,
2776                offset: Point {
2777                    x: content_offset.x + base_position.x,
2778                    y: content_offset.y + base_position.y,
2779                },
2780            });
2781        }
2782        measured_children
2783    }
2784
2785    fn write_node_geometry(
2786        &self,
2787        node_id: NodeId,
2788        node: &LayoutNode,
2789        measurement: &ModifierChainMeasurement,
2790    ) {
2791        let geometry = node.coordinator_geometry();
2792        let moved = if measurement.uses_chain {
2793            self.coordinator_chain
2794                .write_geometry(geometry, measurement.offset)
2795        } else {
2796            geometry.replace([GeometryRect {
2797                x: 0.0,
2798                y: 0.0,
2799                width: measurement.size.width,
2800                height: measurement.size.height,
2801            }])
2802        };
2803        if moved {
2804            crate::render_state::record_geometry_scene_node(node_id);
2805        }
2806    }
2807
2808    #[cfg(test)]
2809    pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
2810        LayoutRuntimeDebugStats {
2811            child_ids: self.child_ids.clone(),
2812            child_state_ptrs: self
2813                .child_states
2814                .iter()
2815                .map(|state| Rc::as_ptr(state) as *const () as usize)
2816                .collect(),
2817            child_measurable_ptrs: self
2818                .child_measurables
2819                .iter()
2820                .map(|measurable| {
2821                    measurable.as_ref() as *const dyn Measurable as *const () as usize
2822                })
2823                .collect(),
2824            child_measurable_count: self.child_measurables.len(),
2825            coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
2826            coordinator_node_count: self.coordinator_chain.nodes.len(),
2827        }
2828    }
2829}
2830
2831#[cfg(test)]
2832#[derive(Debug, Clone, PartialEq, Eq)]
2833pub(crate) struct LayoutRuntimeDebugStats {
2834    pub(crate) child_ids: Vec<NodeId>,
2835    pub(crate) child_state_ptrs: Vec<usize>,
2836    pub(crate) child_measurable_ptrs: Vec<usize>,
2837    pub(crate) child_measurable_count: usize,
2838    pub(crate) coordinator_node_ptrs: Vec<usize>,
2839    pub(crate) coordinator_node_count: usize,
2840}
2841
2842#[derive(Default)]
2843struct LayoutChildFrame {
2844    builder: RefCell<Option<Rc<LayoutBuilderState>>>,
2845    error: RefCell<Option<NodeError>>,
2846}
2847
2848impl LayoutChildFrame {
2849    fn bind(&self, builder: &Rc<LayoutBuilderState>) {
2850        self.error.borrow_mut().take();
2851        *self.builder.borrow_mut() = Some(Rc::clone(builder));
2852    }
2853
2854    fn unbind(&self) {
2855        self.builder.borrow_mut().take();
2856    }
2857
2858    fn record_error(&self, err: NodeError) {
2859        if self.builder.borrow().is_none() {
2860            return;
2861        }
2862        let mut slot = self.error.borrow_mut();
2863        if slot.is_none() {
2864            *slot = Some(err);
2865        }
2866    }
2867}
2868
2869struct LayoutChildBinding<'a> {
2870    cache: &'a LayoutNodeCacheHandles,
2871    layout_state: Option<&'a Rc<RefCell<LayoutState>>>,
2872    parent_data: Option<cranpose_ui_layout::ParentData>,
2873    dirty: bool,
2874}
2875
2876fn parent_data_of(props: crate::modifier::LayoutProperties) -> cranpose_ui_layout::ParentData {
2877    let weight = props.weight().unwrap_or_default();
2878    cranpose_ui_layout::ParentData {
2879        weight: weight.weight,
2880        fill: weight.fill,
2881        box_alignment: props.box_alignment(),
2882        row_alignment: props.row_alignment(),
2883        row_baseline: props.row_baseline(),
2884        column_alignment: props.column_alignment(),
2885    }
2886}
2887
2888struct LayoutChildMeasureState {
2889    node_id: NodeId,
2890    frame: Rc<LayoutChildFrame>,
2891    cache: RefCell<LayoutNodeCacheHandles>,
2892    cache_epoch: Cell<u64>,
2893    force_remeasure: Cell<bool>,
2894    parent_data: Cell<Option<cranpose_ui_layout::ParentData>>,
2895    measured: RefCell<Option<Rc<MeasuredNode>>>,
2896    last_position: Cell<Option<Point>>,
2897    layout_state: RefCell<Option<Rc<RefCell<LayoutState>>>>,
2898}
2899
2900impl LayoutChildMeasureState {
2901    fn placement_position(&self, placement: Option<&Placement>) -> Point {
2902        placement
2903            .map(|placement| Point {
2904                x: placement.x,
2905                y: placement.y,
2906            })
2907            .or_else(|| self.last_position.get())
2908            .unwrap_or_default()
2909    }
2910
2911    fn new(node_id: NodeId, frame: Rc<LayoutChildFrame>) -> Rc<Self> {
2912        Rc::new(Self {
2913            node_id,
2914            frame,
2915            cache: RefCell::new(LayoutNodeCacheHandles::default()),
2916            cache_epoch: Cell::new(0),
2917            force_remeasure: Cell::new(true),
2918            parent_data: Cell::new(None),
2919            measured: RefCell::new(None),
2920            last_position: Cell::new(None),
2921            layout_state: RefCell::new(None),
2922        })
2923    }
2924
2925    fn bind(&self, binding: LayoutChildBinding<'_>, pass: &LayoutBuilderState) {
2926        let child_epoch = binding.cache.epoch();
2927        let stale = binding.dirty || child_epoch < pass.cache_floor;
2928        let cache_epoch = if stale { pass.cache_epoch } else { child_epoch };
2929        binding.cache.activate(cache_epoch);
2930        self.measured.borrow_mut().take();
2931        self.last_position.set(None);
2932        self.cache.borrow_mut().clone_from(binding.cache);
2933        self.cache_epoch.set(cache_epoch);
2934        self.force_remeasure.set(stale);
2935        self.parent_data.set(binding.parent_data);
2936        let mut layout_state = self.layout_state.borrow_mut();
2937        let shared = layout_state
2938            .as_ref()
2939            .zip(binding.layout_state)
2940            .is_some_and(|(current, bound)| Rc::ptr_eq(current, bound));
2941        if !shared {
2942            *layout_state = binding.layout_state.cloned();
2943        }
2944    }
2945
2946    fn place_retained(&self, position: Point) {
2947        self.last_position.set(Some(position));
2948        let builder = self.frame.builder.borrow();
2949        let Some(builder) = builder.as_ref() else {
2950            return;
2951        };
2952        if let Some(layout_state) = self.layout_state.borrow().as_ref() {
2953            layout_state.borrow_mut().place(position);
2954            return;
2955        }
2956        let Ok(mut applier) = builder.applier.try_borrow_typed() else {
2957            return;
2958        };
2959        let _ = applier.with_node::<SubcomposeLayoutNode, _>(self.node_id, |node| {
2960            node.set_position(position);
2961        });
2962    }
2963
2964    fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
2965        let builder = self.frame.builder.borrow();
2966        let builder = builder.as_ref().ok_or(NodeError::MissingContext {
2967            id: self.node_id,
2968            reason: "layout child applier not configured",
2969        })?;
2970        builder.measure_node(self.node_id, constraints)
2971    }
2972
2973    fn measure_cached(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
2974        let cache = self.cache.borrow();
2975        cache.activate(self.cache_epoch.get());
2976        if !self.force_remeasure.get()
2977            && let Some(cached) = cache.get_measurement(constraints)
2978        {
2979            return Some(cached);
2980        }
2981
2982        match self.perform_measure(constraints) {
2983            Ok(measured) => {
2984                self.force_remeasure.set(false);
2985                cache.store_measurement(constraints, Rc::clone(&measured));
2986                Some(measured)
2987            }
2988            Err(err) => {
2989                self.frame.record_error(err);
2990                None
2991            }
2992        }
2993    }
2994}
2995
2996struct LayoutChildMeasurable {
2997    state: Rc<LayoutChildMeasureState>,
2998}
2999
3000impl LayoutChildMeasurable {
3001    fn new(state: Rc<LayoutChildMeasureState>) -> Self {
3002        Self { state }
3003    }
3004
3005    fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
3006        if self.state.frame.builder.borrow().is_none() {
3007            return None;
3008        }
3009        self.state.parent_data.get()
3010    }
3011
3012    fn intrinsic(
3013        &self,
3014        kind: IntrinsicKind,
3015        constraints: Constraints,
3016        extent: fn(Size) -> f32,
3017    ) -> f32 {
3018        let state = &self.state;
3019        let cache = state.cache.borrow();
3020        cache.activate(state.cache_epoch.get());
3021        if !state.force_remeasure.get()
3022            && let Some(value) = cache.get_intrinsic(&kind)
3023        {
3024            return value;
3025        }
3026        let Some(node) = state.measure_cached(constraints) else {
3027            return 0.0;
3028        };
3029        let value = extent(node.size_for_parent());
3030        cache.store_intrinsic(kind, value);
3031        value
3032    }
3033}
3034
3035impl PlaceTarget for LayoutChildMeasureState {
3036    fn place(&self, x: f32, y: f32) {
3037        let internal_offset = self
3038            .measured
3039            .borrow()
3040            .as_ref()
3041            .map(|measured| measured.offset)
3042            .unwrap_or_default();
3043        self.place_retained(Point {
3044            x: x + internal_offset.x,
3045            y: y + internal_offset.y,
3046        });
3047    }
3048}
3049
3050impl Measurable for LayoutChildMeasurable {
3051    fn measure(&self, constraints: Constraints) -> Placeable {
3052        let state = &self.state;
3053        let measured = state.measure_cached(constraints);
3054        let (measured_size, size_for_parent) = measured.as_ref().map_or(
3055            (
3056                Size {
3057                    width: 0.0,
3058                    height: 0.0,
3059                },
3060                Size {
3061                    width: 0.0,
3062                    height: 0.0,
3063                },
3064            ),
3065            |measured| (measured.size, measured.size_for_parent()),
3066        );
3067        if let Some(layout_state) = state.layout_state.borrow().as_ref() {
3068            layout_state.borrow_mut().set_size(measured_size);
3069        }
3070        let alignment_lines = measured
3071            .as_ref()
3072            .map_or_else(AlignmentLines::default, |node| {
3073                node.alignment_lines_for_parent()
3074            });
3075        *state.measured.borrow_mut() = measured;
3076
3077        Placeable::with_place_target(
3078            size_for_parent.width,
3079            size_for_parent.height,
3080            state.node_id,
3081            Rc::clone(&self.state) as Rc<dyn PlaceTarget>,
3082        )
3083        .with_alignment_lines(alignment_lines)
3084    }
3085
3086    fn min_intrinsic_width(&self, height: f32) -> f32 {
3087        self.intrinsic(
3088            IntrinsicKind::MinWidth(height),
3089            Constraints {
3090                min_width: 0.0,
3091                max_width: f32::INFINITY,
3092                min_height: height,
3093                max_height: height,
3094            },
3095            |size| size.width,
3096        )
3097    }
3098
3099    fn max_intrinsic_width(&self, height: f32) -> f32 {
3100        self.intrinsic(
3101            IntrinsicKind::MaxWidth(height),
3102            Constraints {
3103                min_width: 0.0,
3104                max_width: f32::INFINITY,
3105                min_height: 0.0,
3106                max_height: height,
3107            },
3108            |size| size.width,
3109        )
3110    }
3111
3112    fn min_intrinsic_height(&self, width: f32) -> f32 {
3113        self.intrinsic(
3114            IntrinsicKind::MinHeight(width),
3115            Constraints {
3116                min_width: width,
3117                max_width: width,
3118                min_height: 0.0,
3119                max_height: f32::INFINITY,
3120            },
3121            |size| size.height,
3122        )
3123    }
3124
3125    fn max_intrinsic_height(&self, width: f32) -> f32 {
3126        self.intrinsic(
3127            IntrinsicKind::MaxHeight(width),
3128            Constraints {
3129                min_width: 0.0,
3130                max_width: width,
3131                min_height: 0.0,
3132                max_height: f32::INFINITY,
3133            },
3134            |size| size.height,
3135        )
3136    }
3137
3138    fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3139        let parent_data = self.resolved_parent_data()?;
3140        if !parent_data.has_weight() {
3141            return None;
3142        }
3143        Some(cranpose_ui_layout::FlexParentData::new(
3144            parent_data.weight,
3145            parent_data.fill,
3146        ))
3147    }
3148
3149    fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3150        self.resolved_parent_data().unwrap_or_default()
3151    }
3152}
3153
3154#[derive(Clone)]
3155struct RuntimeNodeMetadata {
3156    modifier: Modifier,
3157    resolved_modifiers: ResolvedModifiers,
3158    modifier_slices: Rc<ModifierNodeSlices>,
3159    semantics: Option<Rc<SemanticsConfiguration>>,
3160    role: SemanticsRole,
3161    button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3162}
3163
3164impl Default for RuntimeNodeMetadata {
3165    fn default() -> Self {
3166        Self {
3167            modifier: Modifier::empty(),
3168            resolved_modifiers: ResolvedModifiers::default(),
3169            modifier_slices: Rc::default(),
3170            semantics: None,
3171            role: SemanticsRole::Unknown,
3172            button_handler: None,
3173        }
3174    }
3175}
3176
3177fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3178    modifier_slices
3179        .annotated_text()
3180        .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3181            value: SemanticsText(Rc::clone(text)),
3182        })
3183}
3184
3185fn runtime_metadata_for(
3186    applier: &mut MemoryApplier,
3187    node_id: NodeId,
3188) -> Result<RuntimeNodeMetadata, NodeError> {
3189    if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3190        let modifier = layout.modifier.clone();
3191        let resolved_modifiers = layout.resolved_modifiers();
3192        let modifier_slices = layout.modifier_slices_snapshot();
3193        let role = role_from_modifier_slices(&modifier_slices);
3194
3195        RuntimeNodeMetadata {
3196            modifier,
3197            resolved_modifiers,
3198            modifier_slices,
3199            semantics: layout.semantics_configuration().map(Rc::new),
3200            role,
3201            button_handler: None,
3202        }
3203    }) {
3204        return Ok(meta);
3205    }
3206
3207    if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
3208        applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3209            (
3210                node.modifier(),
3211                node.resolved_modifiers(),
3212                node.modifier_slices_snapshot(),
3213                node.semantics_configuration().map(Rc::new),
3214            )
3215        })
3216    {
3217        return Ok(RuntimeNodeMetadata {
3218            modifier,
3219            resolved_modifiers,
3220            modifier_slices,
3221            semantics,
3222            role: SemanticsRole::Subcompose,
3223            button_handler: None,
3224        });
3225    }
3226    Ok(RuntimeNodeMetadata::default())
3227}
3228
3229fn clear_semantics_dirty_flags(
3230    applier: &mut MemoryApplier,
3231    node: &MeasuredNode,
3232) -> Result<(), NodeError> {
3233    match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3234        layout.clear_needs_semantics();
3235    }) {
3236        Ok(()) => {}
3237        Err(NodeError::Missing { .. }) => {}
3238        Err(NodeError::TypeMismatch { .. }) => {
3239            match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3240                subcompose.clear_needs_semantics();
3241            }) {
3242                Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
3243                Err(err) => return Err(err),
3244            }
3245        }
3246        Err(err) => return Err(err),
3247    }
3248
3249    for child in &node.children {
3250        clear_semantics_dirty_flags(applier, &child.node)?;
3251    }
3252
3253    Ok(())
3254}
3255
3256fn build_semantics_tree_from_live_nodes(
3257    applier: &mut MemoryApplier,
3258    node: &MeasuredNode,
3259) -> Result<SemanticsTree, NodeError> {
3260    Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3261        applier, node, None,
3262    )?))
3263}
3264
3265fn semantics_node_from_parts(
3266    node_id: NodeId,
3267    node_generation: u32,
3268    mut role: SemanticsRole,
3269    config: Option<SemanticsConfiguration>,
3270    children: Vec<SemanticsNode>,
3271    held_details: Option<Box<SemanticsDetails>>,
3272    bounds: GeometryRect,
3273) -> SemanticsNode {
3274    let focusable = crate::focus_dispatch::has_focus_target(node_id);
3275    let mut node = SemanticsNode {
3276        node_id,
3277        bounds,
3278        node_generation,
3279        children,
3280        focusable,
3281        focused: focusable && crate::focus_dispatch::active_focus_target() == Some(node_id),
3282        details: held_details,
3283        ..SemanticsNode::default()
3284    };
3285    let details = match config {
3286        Some(mut config) => {
3287            if config.role == Some(SemanticsWidgetRole::Button) {
3288                role = SemanticsRole::Button;
3289            }
3290            if config.is_activatable() {
3291                node.actions.push(SemanticsAction::Click {
3292                    handler: SemanticsCallback::new(node_id),
3293                });
3294            }
3295            let on_click_label = config.on_click_label.take().or_else(|| {
3296                config
3297                    .on_click
3298                    .as_ref()
3299                    .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
3300            });
3301            node.widget_role = config.role;
3302            node.description = config.content_description.take();
3303            node.on_click = config.on_click.take();
3304            node.selected = config.selected;
3305            node.toggled = config.toggled;
3306            node.enabled = config.enabled;
3307            node.hidden = config.hidden;
3308            node.merge_descendants = config.merge_descendants;
3309            node.traversal_index = config.traversal_index;
3310            node.text = config.text.take();
3311            let is_modal = config.is_modal
3312                && modal_takes_space(Size {
3313                    width: bounds.width,
3314                    height: bounds.height,
3315                });
3316            SemanticsDetails::from_configuration(config, on_click_label, is_modal)
3317        }
3318        None => SemanticsDetails::NONE,
3319    };
3320    node.set_details(details);
3321    node.role = role;
3322    node
3323}
3324
3325fn build_semantics_node_from_live_nodes(
3326    applier: &mut MemoryApplier,
3327    node: &MeasuredNode,
3328    origin: Option<Point>,
3329) -> Result<SemanticsNode, NodeError> {
3330    let (role, config, (bounds, content), placement) =
3331        match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3332            let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
3333            let config = layout.semantics_configuration();
3334            layout.clear_needs_semantics();
3335            let state = layout.layout_state();
3336            (
3337                role,
3338                config,
3339                semantics_placement(&state, origin),
3340                SemanticsPlacement::of(&state),
3341            )
3342        }) {
3343            Ok(data) => data,
3344            Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3345                match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3346                    subcompose.clear_needs_semantics();
3347                    let state = subcompose.layout_state();
3348                    (
3349                        SemanticsRole::Subcompose,
3350                        subcompose.semantics_configuration(),
3351                        semantics_placement(&state, origin),
3352                        SemanticsPlacement::of(&state),
3353                    )
3354                }) {
3355                    Ok(data) => data,
3356                    Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3357                        let top_left = origin.unwrap_or_default();
3358                        (
3359                            SemanticsRole::Unknown,
3360                            None,
3361                            (
3362                                GeometryRect::from_origin_size(top_left, node.size),
3363                                top_left,
3364                            ),
3365                            SemanticsPlacement::default(),
3366                        )
3367                    }
3368                    Err(err) => return Err(err),
3369                }
3370            }
3371            Err(err) => return Err(err),
3372        };
3373
3374    let mut children = Vec::with_capacity(node.children.len());
3375    for child in &node.children {
3376        children.push(build_semantics_node_from_live_nodes(
3377            applier,
3378            &child.node,
3379            Some(content),
3380        )?);
3381    }
3382
3383    Ok(SemanticsNode {
3384        placement,
3385        ..semantics_node_from_parts(
3386            node.node_id,
3387            applier.node_generation(node.node_id),
3388            role,
3389            config,
3390            children,
3391            None,
3392            bounds,
3393        )
3394    })
3395}
3396
3397fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
3398    stats.semantics_node_count += 1;
3399    stats.semantics_action_count += node.actions.len();
3400    stats.semantics_action_capacity += node.actions.capacity();
3401    stats.semantics_child_count += node.children.len();
3402    stats.semantics_child_capacity += node.children.capacity();
3403    stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
3404    stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
3405    if node.details.is_some() {
3406        stats.semantics_heap_bytes += size_of::<SemanticsDetails>();
3407    }
3408
3409    if let Some(description) = &node.description {
3410        stats.semantics_description_count += 1;
3411        stats.semantics_description_bytes += description.capacity();
3412        stats.semantics_heap_bytes += description.capacity();
3413    }
3414
3415    for child in &node.children {
3416        record_semantics_allocation_stats(child, stats);
3417    }
3418}
3419
3420fn record_layout_box_allocation_stats(
3421    layout_box: &LayoutBox,
3422    stats: &mut LayoutAllocationDebugStats,
3423) {
3424    stats.layout_box_count += 1;
3425    stats.layout_box_child_count += layout_box.children.len();
3426    stats.layout_box_child_capacity += layout_box.children.capacity();
3427    stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
3428    stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
3429
3430    for child in &layout_box.children {
3431        record_layout_box_allocation_stats(child, stats);
3432    }
3433}
3434
3435fn build_layout_tree(
3436    applier: &mut MemoryApplier,
3437    node: &MeasuredNode,
3438) -> Result<LayoutTree, NodeError> {
3439    fn place(
3440        applier: &mut MemoryApplier,
3441        node: &MeasuredNode,
3442        origin: Point,
3443        parent_transform: ProjectiveTransform,
3444    ) -> Result<LayoutBox, NodeError> {
3445        let top_left = Point {
3446            x: origin.x + node.offset.x,
3447            y: origin.y + node.offset.y,
3448        };
3449        let rect = GeometryRect {
3450            x: top_left.x,
3451            y: top_left.y,
3452            width: node.size.width,
3453            height: node.size.height,
3454        };
3455        let (data, window_transform) =
3456            snapshot_node_data(applier, node.node_id, top_left, node.size, parent_transform)?;
3457        let mut children = Vec::with_capacity(node.children.len());
3458        for child in &node.children {
3459            if crate::modifier::is_window_root(applier, child.node.node_id) {
3460                continue;
3461            }
3462            let child_origin = Point {
3463                x: top_left.x + child.offset.x,
3464                y: top_left.y + child.offset.y,
3465            };
3466            children.push(place(applier, &child.node, child_origin, window_transform)?);
3467        }
3468        Ok(LayoutBox {
3469            node_generation: applier.node_generation(node.node_id),
3470            ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
3471        })
3472    }
3473
3474    Ok(LayoutTree::new(place(
3475        applier,
3476        node,
3477        Point { x: 0.0, y: 0.0 },
3478        ProjectiveTransform::identity(),
3479    )?))
3480}
3481
3482fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
3483    match &layout_box.node_data.kind {
3484        LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
3485        LayoutNodeKind::Spacer => SemanticsRole::Spacer,
3486        LayoutNodeKind::Unknown => SemanticsRole::Unknown,
3487        LayoutNodeKind::Button { .. } => SemanticsRole::Button,
3488        LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
3489    }
3490}
3491
3492fn build_semantics_node_from_layout_box(layout_box: &LayoutBox, origin: Point) -> SemanticsNode {
3493    let rect = layout_box.rect;
3494    let content = Point {
3495        x: rect.x + layout_box.content_offset.x,
3496        y: rect.y + layout_box.content_offset.y,
3497    };
3498    let children = layout_box
3499        .children
3500        .iter()
3501        .map(|child| build_semantics_node_from_layout_box(child, content))
3502        .collect();
3503
3504    SemanticsNode {
3505        placement: SemanticsPlacement {
3506            position: Point {
3507                x: rect.x - origin.x,
3508                y: rect.y - origin.y,
3509            },
3510            content_offset: layout_box.content_offset,
3511        },
3512        ..semantics_node_from_parts(
3513            layout_box.node_id,
3514            layout_box.node_generation,
3515            semantics_role_from_layout_box(layout_box),
3516            layout_box.node_data.semantics().cloned(),
3517            children,
3518            None,
3519            rect,
3520        )
3521    }
3522}
3523
3524fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
3525    match &info.role {
3526        SemanticsRole::Layout => LayoutNodeKind::Layout,
3527        SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
3528        SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
3529        SemanticsRole::Spacer => LayoutNodeKind::Spacer,
3530        SemanticsRole::Button => {
3531            let handler = info
3532                .button_handler
3533                .as_ref()
3534                .cloned()
3535                .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
3536            LayoutNodeKind::Button { on_click: handler }
3537        }
3538        SemanticsRole::Unknown => LayoutNodeKind::Unknown,
3539    }
3540}
3541
3542fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
3543    let horizontal = padding.horizontal_sum();
3544    let vertical = padding.vertical_sum();
3545    let min_width = (constraints.min_width - horizontal).max(0.0);
3546    let mut max_width = constraints.max_width;
3547    if max_width.is_finite() {
3548        max_width = (max_width - horizontal).max(0.0);
3549    }
3550    let min_height = (constraints.min_height - vertical).max(0.0);
3551    let mut max_height = constraints.max_height;
3552    if max_height.is_finite() {
3553        max_height = (max_height - vertical).max(0.0);
3554    }
3555    normalize_constraints(Constraints {
3556        min_width,
3557        max_width,
3558        min_height,
3559        max_height,
3560    })
3561}
3562
3563fn resolve_dimension(
3564    base: f32,
3565    explicit: DimensionConstraint,
3566    min_override: Option<f32>,
3567    max_override: Option<f32>,
3568    min_limit: f32,
3569    max_limit: f32,
3570) -> f32 {
3571    let mut min_bound = min_limit;
3572    if let Some(min_value) = min_override {
3573        min_bound = min_bound.max(min_value);
3574    }
3575
3576    let mut max_bound = if max_limit.is_finite() {
3577        max_limit
3578    } else {
3579        max_override.unwrap_or(max_limit)
3580    };
3581    if let Some(max_value) = max_override {
3582        if max_bound.is_finite() {
3583            max_bound = max_bound.min(max_value);
3584        } else {
3585            max_bound = max_value;
3586        }
3587    }
3588    if max_bound < min_bound {
3589        max_bound = min_bound;
3590    }
3591
3592    let mut size = match explicit {
3593        DimensionConstraint::Points(points) => points,
3594        DimensionConstraint::Fraction(fraction) => {
3595            if max_limit.is_finite() {
3596                max_limit * fraction.clamp(0.0, 1.0)
3597            } else {
3598                base
3599            }
3600        }
3601        DimensionConstraint::Unspecified => base,
3602        DimensionConstraint::Intrinsic(_) => base,
3603    };
3604
3605    size = clamp_dimension(size, min_bound, max_bound);
3606    size = clamp_dimension(size, min_limit, max_limit);
3607    size.max(0.0)
3608}
3609
3610fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
3611    let mut result = value.max(min);
3612    if max.is_finite() {
3613        result = result.min(max);
3614    }
3615    result
3616}
3617
3618fn normalize_constraints(mut constraints: Constraints) -> Constraints {
3619    if constraints.max_width < constraints.min_width {
3620        constraints.max_width = constraints.min_width;
3621    }
3622    if constraints.max_height < constraints.min_height {
3623        constraints.max_height = constraints.min_height;
3624    }
3625    constraints
3626}
3627
3628#[cfg(test)]
3629#[path = "tests/layout_tests.rs"]
3630mod tests;
3631
3632#[cfg(test)]
3633#[path = "tests/semantics_update_tests.rs"]
3634mod semantics_update_tests;
3635
3636#[cfg(test)]
3637#[path = "tests/coordinator_geometry_tests.rs"]
3638mod coordinator_geometry_tests;