1pub mod core;
2pub mod policies;
3mod semantics_labels;
4
5use std::{
6 cell::{Cell, RefCell},
7 fmt,
8 mem::size_of,
9 rc::Rc,
10 sync::OnceLock,
11};
12
13use cranpose_core::{
14 Applier, ApplierHost, Composer, ConcreteApplierHost, MemoryApplier, Node, NodeError, NodeId,
15 Phase, RuntimeHandle, SlotTable, SlotsHost, SnapshotStateObserver,
16};
17use cranpose_foundation::{
18 CanvasSemanticsNode, CollectionInfo, InvalidationKind, LiveRegionMode, ModifierNodeContext,
19 NodeCapabilities, ProgressBarRangeInfo, ScrollAxisRange, SemanticsConfiguration,
20 SemanticsCustomAction, SemanticsDismiss, SemanticsExpand, SemanticsLongClick,
21 SemanticsMagicTap, SemanticsScrollBy, SemanticsScrollToIndex, SemanticsSetProgress,
22 SemanticsSetSelection, SemanticsSetText, SemanticsWidgetRole, text::TextRange,
23};
24use cranpose_ui_layout::{Constraints, MeasurePolicy, Placement};
25use web_time::Instant;
26
27#[cfg(test)]
28use self::core::{HorizontalAlignment, VerticalAlignment};
29use self::core::{Measurable, Placeable};
30use crate::{
31 modifier::{
32 DimensionConstraint, EdgeInsets, Modifier, ModifierNodeSlices,
33 ModifierNodeSlicesDebugStats, Point, Rect as GeometryRect, ResolvedModifiers, Size,
34 },
35 subcompose_layout::{CachedBatchMeasureInputs, SubcomposeLayoutNode},
36 widgets::nodes::{IntrinsicKind, LayoutNode, LayoutNodeCacheHandles, LayoutState},
37};
38
39#[derive(Default)]
40pub(crate) struct LayoutNodeContext {
41 invalidations: Vec<InvalidationKind>,
42 update_requested: bool,
43 active_capabilities: Vec<NodeCapabilities>,
44 density: f32,
45}
46
47impl LayoutNodeContext {
48 pub(crate) fn new(density: f32) -> Self {
49 Self {
50 density,
51 ..Self::default()
52 }
53 }
54
55 pub(crate) fn take_invalidations(&mut self) -> Vec<InvalidationKind> {
56 std::mem::take(&mut self.invalidations)
57 }
58}
59
60impl ModifierNodeContext for LayoutNodeContext {
61 fn invalidate(&mut self, kind: InvalidationKind) {
62 if !self.invalidations.contains(&kind) {
63 self.invalidations.push(kind);
64 }
65 }
66
67 fn request_update(&mut self) {
68 self.update_requested = true;
69 }
70
71 fn push_active_capabilities(&mut self, capabilities: NodeCapabilities) {
72 self.active_capabilities.push(capabilities);
73 }
74
75 fn pop_active_capabilities(&mut self) {
76 self.active_capabilities.pop();
77 }
78
79 fn density(&self) -> f32 {
80 self.density
81 }
82}
83
84#[doc(hidden)]
85pub fn invalidate_all_layout_caches() {
86 crate::render_state::invalidate_layout_cache_epoch();
87}
88
89fn layout_measure_telemetry_threshold_ms() -> Option<f64> {
90 static THRESHOLD_MS: OnceLock<Option<f64>> = OnceLock::new();
91 *THRESHOLD_MS.get_or_init(|| {
92 std::env::var("CRANPOSE_LAYOUT_MEASURE_TELEMETRY_MS")
93 .ok()
94 .and_then(|value| value.parse::<f64>().ok())
95 .filter(|value| value.is_finite() && *value >= 0.0)
96 .or_else(|| {
97 std::env::var_os("CRANPOSE_LAYOUT_MEASURE_TELEMETRY")
98 .is_some()
99 .then_some(4.0)
100 })
101 })
102}
103
104struct LayoutMeasureTelemetry {
105 root: NodeId,
106 start: Instant,
107 after_repasses: Instant,
108 after_guard: Instant,
109 after_builder: Instant,
110 after_measure: Instant,
111 after_root_place: Instant,
112 after_aux: Instant,
113 after_builder_drop: Instant,
114 after_guard_drop: Instant,
115}
116
117fn log_layout_measure_telemetry(times: LayoutMeasureTelemetry) {
118 let Some(threshold_ms) = layout_measure_telemetry_threshold_ms() else {
119 return;
120 };
121
122 let total_ms = times
123 .after_guard_drop
124 .duration_since(times.start)
125 .as_secs_f64()
126 * 1000.0;
127 if total_ms < threshold_ms {
128 return;
129 }
130
131 let repass_ms = times
132 .after_repasses
133 .duration_since(times.start)
134 .as_secs_f64()
135 * 1000.0;
136 let guard_ms = times
137 .after_guard
138 .duration_since(times.after_repasses)
139 .as_secs_f64()
140 * 1000.0;
141 let builder_ms = times
142 .after_builder
143 .duration_since(times.after_guard)
144 .as_secs_f64()
145 * 1000.0;
146 let measure_ms = times
147 .after_measure
148 .duration_since(times.after_builder)
149 .as_secs_f64()
150 * 1000.0;
151 let root_place_ms = times
152 .after_root_place
153 .duration_since(times.after_measure)
154 .as_secs_f64()
155 * 1000.0;
156 let aux_ms = times
157 .after_aux
158 .duration_since(times.after_root_place)
159 .as_secs_f64()
160 * 1000.0;
161 let builder_drop_ms = times
162 .after_builder_drop
163 .duration_since(times.after_aux)
164 .as_secs_f64()
165 * 1000.0;
166 let guard_drop_ms = times
167 .after_guard_drop
168 .duration_since(times.after_builder_drop)
169 .as_secs_f64()
170 * 1000.0;
171 log::warn!(
172 "[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}",
173 times.root
174 );
175}
176
177fn log_node_measure_telemetry(
178 kind: &'static str,
179 node_id: NodeId,
180 constraints: Constraints,
181 size: Size,
182 children: usize,
183 start: Instant,
184) {
185 let Some(threshold_ms) = layout_measure_telemetry_threshold_ms() else {
186 return;
187 };
188
189 let total_ms = start.elapsed().as_secs_f64() * 1000.0;
190 if total_ms < threshold_ms {
191 return;
192 }
193
194 log::warn!(
195 "[layout-node-telemetry] kind={kind} node={} total_ms={total_ms:.2} constraints=({:.1},{:.1},{:.1},{:.1}) size=({:.1},{:.1}) children={children}",
196 node_id,
197 constraints.min_width,
198 constraints.max_width,
199 constraints.min_height,
200 constraints.max_height,
201 size.width,
202 size.height,
203 );
204}
205
206struct ApplierSlotGuard<'a> {
207 target: &'a mut MemoryApplier,
208 host: Rc<ConcreteApplierHost<MemoryApplier>>,
209 slots: Rc<RefCell<SlotTable>>,
210}
211
212impl<'a> ApplierSlotGuard<'a> {
213 fn new(target: &'a mut MemoryApplier) -> Self {
214 let original_applier = std::mem::replace(target, MemoryApplier::new());
215 let host = Rc::new(ConcreteApplierHost::new(original_applier));
216
217 let slots = {
218 let mut applier_ref = host.borrow_typed();
219 std::mem::take(applier_ref.slots())
220 };
221 let slots = Rc::new(RefCell::new(slots));
222
223 Self {
224 target,
225 host,
226 slots,
227 }
228 }
229
230 fn host(&self) -> Rc<ConcreteApplierHost<MemoryApplier>> {
231 Rc::clone(&self.host)
232 }
233
234 fn slots_handle(&self) -> Rc<RefCell<SlotTable>> {
235 Rc::clone(&self.slots)
236 }
237}
238
239impl Drop for ApplierSlotGuard<'_> {
240 fn drop(&mut self) {
241 {
242 let mut applier_ref = self.host.borrow_typed();
243 *applier_ref.slots() = std::mem::take(&mut *self.slots.borrow_mut());
244 }
245
246 {
247 let mut applier_ref = self.host.borrow_typed();
248 let original_applier = std::mem::take(&mut *applier_ref);
249 let _ = std::mem::replace(self.target, original_applier);
250 }
251 }
252}
253
254struct ModifierChainMeasurement {
255 size: Size,
256 content_offset: Point,
257 offset: Point,
258 window_root: bool,
259}
260
261type LayoutModifierNodeData = (
262 usize,
263 Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
264);
265
266struct ScratchVecPool<T> {
267 available: Vec<Vec<T>>,
268}
269
270impl<T> ScratchVecPool<T> {
271 fn acquire(&mut self) -> Vec<T> {
272 self.available.pop().unwrap_or_default()
273 }
274
275 fn release(&mut self, mut values: Vec<T>) {
276 values.clear();
277 self.available.push(values);
278 }
279
280 #[cfg(test)]
281 fn available_count(&self) -> usize {
282 self.available.len()
283 }
284}
285
286impl<T> Default for ScratchVecPool<T> {
287 fn default() -> Self {
288 Self {
289 available: Vec::new(),
290 }
291 }
292}
293
294#[derive(Default)]
295pub(crate) struct FrameLayoutArena {
296 tmp_records: ScratchVecPool<(NodeId, ChildRecord)>,
297 tmp_child_ids: ScratchVecPool<NodeId>,
298 tmp_layout_node_data: ScratchVecPool<LayoutModifierNodeData>,
299 tmp_placements: ScratchVecPool<Placement>,
300}
301
302#[cfg(test)]
303impl FrameLayoutArena {
304 pub(crate) fn available_placement_scratch_count(&self) -> usize {
305 self.tmp_placements.available_count()
306 }
307
308 pub(crate) fn seed_placement_scratch_for_test(&mut self) {
309 self.tmp_placements.release(Vec::with_capacity(1));
310 }
311}
312
313#[derive(Clone, Debug, PartialEq, Eq)]
315pub struct SemanticsCallback {
316 node_id: NodeId,
317}
318
319impl SemanticsCallback {
320 pub fn new(node_id: NodeId) -> Self {
321 Self { node_id }
322 }
323
324 pub fn node_id(&self) -> NodeId {
325 self.node_id
326 }
327}
328
329#[derive(Clone, Debug, PartialEq, Eq)]
331pub enum SemanticsAction {
332 Click { handler: SemanticsCallback },
333}
334
335#[derive(Clone, Debug)]
338pub struct SemanticsText(Rc<crate::text::AnnotatedString>);
339
340impl SemanticsText {
341 pub fn as_str(&self) -> &str {
343 &self.0.text
344 }
345}
346
347impl PartialEq for SemanticsText {
348 fn eq(&self, other: &Self) -> bool {
349 self.as_str() == other.as_str()
350 }
351}
352
353impl Eq for SemanticsText {}
354
355impl From<&str> for SemanticsText {
356 fn from(text: &str) -> Self {
357 Self(Rc::new(crate::text::AnnotatedString::from(text)))
358 }
359}
360
361#[derive(Clone, Debug, PartialEq, Eq)]
364pub enum SemanticsRole {
365 Layout,
367 Subcompose,
369 Text { value: SemanticsText },
371 Spacer,
373 Button,
375 Unknown,
377}
378
379#[derive(Clone, Debug, PartialEq)]
386pub struct SemanticsNode {
387 pub node_id: NodeId,
388 pub bounds: GeometryRect,
391 pub node_generation: u32,
393 pub role: SemanticsRole,
395 pub widget_role: Option<SemanticsWidgetRole>,
399 pub actions: Vec<SemanticsAction>,
400 pub children: Vec<SemanticsNode>,
401 pub description: Option<String>,
402 pub state_description: Option<String>,
403 pub on_click_label: Option<String>,
404 pub on_click: Option<SemanticsCustomAction>,
406 pub on_long_click: Option<SemanticsLongClick>,
408 pub on_long_click_label: Option<String>,
410 pub on_magic_tap: Option<SemanticsMagicTap>,
412 pub on_magic_tap_label: Option<String>,
414 pub input_labels: Vec<String>,
416 pub language: Option<String>,
418 pub selected: Option<bool>,
419 pub toggled: Option<bool>,
420 pub enabled: bool,
421 pub custom_actions: Vec<SemanticsCustomAction>,
422 pub canvas_children: Vec<CanvasSemanticsNode>,
425 pub editable_text: bool,
426 pub multiline: bool,
428 pub hidden: bool,
430 pub is_modal: bool,
432 pub merge_descendants: bool,
435 pub selectable_group: bool,
437 pub pane_title: Option<String>,
439 pub error: Option<String>,
441 pub password: bool,
443 pub traversal_index: f32,
445 pub text: Option<String>,
447 pub text_selection: Option<TextRange>,
448 pub focusable: bool,
451 pub focused: bool,
453 pub live_region: Option<LiveRegionMode>,
456 pub progress: Option<ProgressBarRangeInfo>,
459 pub set_progress: Option<SemanticsSetProgress>,
461 pub set_text: Option<SemanticsSetText>,
463 pub set_selection: Option<SemanticsSetSelection>,
466 pub expand: Option<SemanticsExpand>,
468 pub collapse: Option<SemanticsExpand>,
470 pub dismiss: Option<SemanticsDismiss>,
472 pub vertical_scroll: Option<ScrollAxisRange>,
474 pub horizontal_scroll: Option<ScrollAxisRange>,
476 pub scroll_by: Option<SemanticsScrollBy>,
478 pub scroll_to_index: Option<SemanticsScrollToIndex>,
480 pub collection: Option<CollectionInfo>,
482}
483
484impl Default for SemanticsNode {
485 fn default() -> Self {
486 Self {
487 node_id: 0,
488 bounds: GeometryRect::EMPTY,
489 node_generation: 0,
490 role: SemanticsRole::Unknown,
491 widget_role: None,
492 actions: Vec::new(),
493 children: Vec::new(),
494 description: None,
495 state_description: None,
496 on_click_label: None,
497 on_click: None,
498 on_long_click: None,
499 on_long_click_label: None,
500 on_magic_tap: None,
501 on_magic_tap_label: None,
502 input_labels: Vec::new(),
503 language: None,
504 selected: None,
505 toggled: None,
506 enabled: true,
507 custom_actions: Vec::new(),
508 canvas_children: Vec::new(),
509 editable_text: false,
510 multiline: false,
511 hidden: false,
512 is_modal: false,
513 merge_descendants: false,
514 selectable_group: false,
515 pane_title: None,
516 error: None,
517 password: false,
518 traversal_index: 0.0,
519 text: None,
520 text_selection: None,
521 focusable: false,
522 focused: false,
523 live_region: None,
524 progress: None,
525 set_progress: None,
526 set_text: None,
527 set_selection: None,
528 expand: None,
529 collapse: None,
530 dismiss: None,
531 vertical_scroll: None,
532 horizontal_scroll: None,
533 scroll_by: None,
534 scroll_to_index: None,
535 collection: None,
536 }
537 }
538}
539
540#[derive(Clone, Debug, PartialEq)]
542pub struct SemanticsTree {
543 root: SemanticsNode,
544}
545
546impl SemanticsTree {
547 fn new(mut root: SemanticsNode) -> Self {
548 if let Some(modal) = take_top_modal(&mut root) {
549 root = modal;
550 }
551 Self { root }
552 }
553
554 pub fn root(&self) -> &SemanticsNode {
556 &self.root
557 }
558}
559
560fn modal_takes_space(size: Size) -> bool {
562 size.width > 0.0 && size.height > 0.0 && size.width.is_finite() && size.height.is_finite()
563}
564
565fn take_top_modal(node: &mut SemanticsNode) -> Option<SemanticsNode> {
566 if node.hidden {
567 return None;
568 }
569 for child in node.children.iter_mut().rev() {
570 if let Some(modal) = take_top_modal(child) {
571 return Some(modal);
572 }
573 }
574 node.is_modal.then(|| std::mem::take(node))
575}
576
577#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
578pub struct LayoutAllocationDebugStats {
579 pub layout_box_count: usize,
580 pub layout_box_child_count: usize,
581 pub layout_box_child_capacity: usize,
582 pub layout_box_heap_bytes: usize,
583 pub modifier_slice_count: usize,
584 pub modifier_slice_heap_bytes: usize,
585 pub modifier_draw_command_count: usize,
586 pub modifier_draw_command_capacity: usize,
587 pub modifier_pointer_input_count: usize,
588 pub modifier_pointer_input_capacity: usize,
589 pub modifier_text_content_count: usize,
590 pub modifier_text_style_count: usize,
591 pub modifier_text_layout_options_count: usize,
592 pub modifier_prepared_text_layout_count: usize,
593 pub modifier_graphics_layer_count: usize,
594 pub modifier_graphics_layer_resolver_count: usize,
595 pub semantics_node_count: usize,
596 pub semantics_action_count: usize,
597 pub semantics_action_capacity: usize,
598 pub semantics_child_count: usize,
599 pub semantics_child_capacity: usize,
600 pub semantics_description_count: usize,
601 pub semantics_description_bytes: usize,
602 pub semantics_heap_bytes: usize,
603}
604
605impl LayoutAllocationDebugStats {
606 fn add_modifier_slice(&mut self, stats: ModifierNodeSlicesDebugStats) {
607 self.modifier_slice_count += 1;
608 self.modifier_slice_heap_bytes += stats.heap_bytes;
609 self.modifier_draw_command_count += stats.draw_command_count;
610 self.modifier_draw_command_capacity += stats.draw_command_capacity;
611 self.modifier_pointer_input_count += stats.pointer_input_count;
612 self.modifier_pointer_input_capacity += stats.pointer_input_capacity;
613 self.modifier_text_content_count += usize::from(stats.has_text_content);
614 self.modifier_text_style_count += usize::from(stats.has_text_style);
615 self.modifier_text_layout_options_count += usize::from(stats.has_text_layout_options);
616 self.modifier_prepared_text_layout_count += usize::from(stats.has_prepared_text_layout);
617 self.modifier_graphics_layer_count += usize::from(stats.has_graphics_layer);
618 self.modifier_graphics_layer_resolver_count +=
619 usize::from(stats.has_graphics_layer_resolver);
620 }
621}
622
623#[derive(Debug, Clone)]
625pub struct LayoutTree {
626 root: LayoutBox,
627}
628
629impl LayoutTree {
630 pub fn new(root: LayoutBox) -> Self {
631 Self { root }
632 }
633
634 pub fn root(&self) -> &LayoutBox {
635 &self.root
636 }
637
638 pub fn root_mut(&mut self) -> &mut LayoutBox {
639 &mut self.root
640 }
641
642 pub fn into_root(self) -> LayoutBox {
643 self.root
644 }
645
646 pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
647 let mut stats = LayoutAllocationDebugStats::default();
648 record_layout_box_allocation_stats(&self.root, &mut stats);
649 stats
650 }
651}
652
653#[derive(Debug, Clone)]
655pub struct LayoutBox {
656 pub node_id: NodeId,
657 pub node_generation: u32,
659 pub rect: GeometryRect,
660 pub content_offset: Point,
662 pub node_data: LayoutNodeData,
663 pub children: Vec<LayoutBox>,
664}
665
666impl LayoutBox {
667 pub fn new(
668 node_id: NodeId,
669 rect: GeometryRect,
670 content_offset: Point,
671 node_data: LayoutNodeData,
672 children: Vec<LayoutBox>,
673 ) -> Self {
674 Self {
675 node_id,
676 node_generation: 0,
677 rect,
678 content_offset,
679 node_data,
680 children,
681 }
682 }
683}
684
685#[derive(Debug, Clone)]
687pub struct LayoutNodeData {
688 #[cfg(feature = "inspection")]
690 pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
691 pub modifier: Modifier,
692 pub resolved_modifiers: ResolvedModifiers,
693 pub modifier_slices: Rc<ModifierNodeSlices>,
694 pub semantics: Option<Rc<SemanticsConfiguration>>,
695 pub kind: LayoutNodeKind,
696}
697
698impl LayoutNodeData {
699 pub fn new(
700 modifier: Modifier,
701 resolved_modifiers: ResolvedModifiers,
702 modifier_slices: Rc<ModifierNodeSlices>,
703 semantics: Option<Rc<SemanticsConfiguration>>,
704 kind: LayoutNodeKind,
705 ) -> Self {
706 Self {
707 #[cfg(feature = "inspection")]
708 source_trace: Rc::default(),
709 modifier,
710 resolved_modifiers,
711 modifier_slices,
712 semantics,
713 kind,
714 }
715 }
716
717 pub fn semantics(&self) -> Option<&SemanticsConfiguration> {
720 self.semantics.as_deref()
721 }
722
723 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
724 self.resolved_modifiers
725 }
726
727 pub fn modifier_slices(&self) -> &ModifierNodeSlices {
728 &self.modifier_slices
729 }
730}
731
732#[derive(Clone)]
739pub enum LayoutNodeKind {
740 Layout,
741 Subcompose,
742 Spacer,
743 Button { on_click: Rc<RefCell<dyn FnMut()>> },
744 Unknown,
745}
746
747impl fmt::Debug for LayoutNodeKind {
748 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
749 match self {
750 LayoutNodeKind::Layout => f.write_str("Layout"),
751 LayoutNodeKind::Subcompose => f.write_str("Subcompose"),
752 LayoutNodeKind::Spacer => f.write_str("Spacer"),
753 LayoutNodeKind::Button { .. } => f.write_str("Button"),
754 LayoutNodeKind::Unknown => f.write_str("Unknown"),
755 }
756 }
757}
758
759pub trait LayoutEngine {
761 fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError>;
762}
763
764impl LayoutEngine for MemoryApplier {
765 fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError> {
766 let measurements = measure_layout(self, root, max_size)?;
767 measurements
768 .into_layout_tree()
769 .ok_or(NodeError::MissingContext {
770 id: root,
771 reason: "layout tree was not requested",
772 })
773 }
774}
775
776#[derive(Debug, Clone)]
778pub struct LayoutMeasurements {
779 root: Rc<MeasuredNode>,
780 semantics: Option<SemanticsTree>,
781 layout_tree: Option<LayoutTree>,
782}
783
784impl LayoutMeasurements {
785 fn new(
786 root: Rc<MeasuredNode>,
787 semantics: Option<SemanticsTree>,
788 layout_tree: Option<LayoutTree>,
789 ) -> Self {
790 Self {
791 root,
792 semantics,
793 layout_tree,
794 }
795 }
796
797 pub fn root_size(&self) -> Size {
799 self.root.size
800 }
801
802 pub fn semantics_tree(&self) -> Option<&SemanticsTree> {
803 self.semantics.as_ref()
804 }
805
806 pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
807 let mut stats = self
808 .layout_tree
809 .as_ref()
810 .map(LayoutTree::debug_allocation_stats)
811 .unwrap_or_default();
812 if let Some(semantics) = &self.semantics {
813 record_semantics_allocation_stats(semantics.root(), &mut stats);
814 }
815 stats
816 }
817
818 pub fn into_layout_tree(self) -> Option<LayoutTree> {
820 self.layout_tree
821 }
822
823 pub fn layout_tree(&self) -> Option<LayoutTree> {
825 self.layout_tree.clone()
826 }
827}
828
829pub fn build_semantics_tree_from_layout_tree(layout_tree: &LayoutTree) -> SemanticsTree {
834 SemanticsTree::new(build_semantics_node_from_layout_box(layout_tree.root()))
835}
836
837pub fn build_layout_tree_from_applier(
843 applier: &mut MemoryApplier,
844 root: NodeId,
845) -> Result<Option<LayoutTree>, NodeError> {
846 let origin = layout_tree_origin(read_layout_node(applier, root, |state, _| state)?);
847 let mut child_stack = Vec::new();
848 place_layout_box(applier, root, origin, Point::default(), &mut child_stack)
849 .map(|root| root.map(LayoutTree::new))
850}
851
852fn layout_tree_origin(root: Option<LayoutState>) -> Point {
853 let Some(state) = root else {
854 return Point::default();
855 };
856 let position = state.position();
857 Point {
858 x: -position.x,
859 y: -position.y,
860 }
861}
862
863fn read_layout_node<R>(
866 applier: &mut MemoryApplier,
867 node_id: NodeId,
868 mut read: impl FnMut(LayoutState, &[NodeId]) -> R,
869) -> Result<Option<R>, NodeError> {
870 match applier
871 .with_node::<LayoutNode, _>(node_id, |node| read(node.layout_state(), &node.children))
872 {
873 Ok(value) => return Ok(Some(value)),
874 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {}
875 Err(err) => return Err(err),
876 }
877
878 match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
879 let state = node.layout_state();
880 node.with_active_children(|children| read(state, children))
881 }) {
882 Ok(value) => Ok(Some(value)),
883 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
884 Err(err) => Err(err),
885 }
886}
887
888fn snapshot_node_data(
889 applier: &mut MemoryApplier,
890 node_id: NodeId,
891 top_left: Point,
892 size: Size,
893 parent_layer_translation: Point,
894) -> Result<(LayoutNodeData, Point), NodeError> {
895 let info = runtime_metadata_for(applier, node_id)?;
896 let kind = layout_kind_from_metadata(node_id, &info);
897 let RuntimeNodeMetadata {
898 modifier,
899 resolved_modifiers,
900 modifier_slices,
901 semantics,
902 ..
903 } = info;
904
905 let layer_translation = match modifier_slices.graphics_layer() {
906 Some(layer) => Point {
907 x: parent_layer_translation.x + layer.translation_x,
908 y: parent_layer_translation.y + layer.translation_y,
909 },
910 None => parent_layer_translation,
911 };
912
913 publish_window_geometry(&modifier_slices, top_left, layer_translation, size);
914
915 let data = LayoutNodeData::new(
916 modifier,
917 resolved_modifiers,
918 modifier_slices,
919 semantics,
920 kind,
921 );
922 #[cfg(feature = "inspection")]
923 let data = {
924 let mut data = data;
925 data.source_trace = applier
926 .with_node::<LayoutNode, _>(node_id, |node| node.source_trace.clone())
927 .unwrap_or_default();
928 data
929 };
930 Ok((data, layer_translation))
931}
932
933fn place_layout_box(
938 applier: &mut MemoryApplier,
939 node_id: NodeId,
940 parent_content_origin: Point,
941 parent_layer_translation: Point,
942 child_stack: &mut Vec<NodeId>,
943) -> Result<Option<LayoutBox>, NodeError> {
944 let first_child = child_stack.len();
945 let Some(state) = read_layout_node(applier, node_id, |state, children| {
946 if state.is_placed() {
947 child_stack.extend_from_slice(children);
948 }
949 state
950 })?
951 else {
952 return Ok(None);
953 };
954 if !state.is_placed() {
955 return Ok(None);
956 }
957
958 let top_left = Point {
959 x: parent_content_origin.x + state.position().x,
960 y: parent_content_origin.y + state.position().y,
961 };
962 let rect = GeometryRect {
963 x: top_left.x,
964 y: top_left.y,
965 width: state.size().width,
966 height: state.size().height,
967 };
968 let (data, layer_translation) = snapshot_node_data(
969 applier,
970 node_id,
971 top_left,
972 state.size(),
973 parent_layer_translation,
974 )?;
975 let child_origin = Point {
976 x: top_left.x + state.content_offset.x,
977 y: top_left.y + state.content_offset.y,
978 };
979 let end = child_stack.len();
980 let mut children = Vec::with_capacity(end - first_child);
981 for index in first_child..end {
982 let child_id = child_stack[index];
983 if crate::modifier::is_window_root(applier, child_id) {
984 continue;
985 }
986 if let Some(child) = place_layout_box(
987 applier,
988 child_id,
989 child_origin,
990 layer_translation,
991 child_stack,
992 )? {
993 children.push(child);
994 }
995 }
996 child_stack.truncate(first_child);
997
998 Ok(Some(LayoutBox {
999 node_generation: applier.node_generation(node_id),
1000 ..LayoutBox::new(node_id, rect, state.content_offset, data, children)
1001 }))
1002}
1003
1004fn semantics_placement(
1009 state: &crate::widgets::nodes::layout_node::LayoutState,
1010 origin: Option<Point>,
1011) -> (GeometryRect, Point) {
1012 let top_left = origin.map_or_else(Point::default, |origin| Point {
1013 x: origin.x + state.position().x,
1014 y: origin.y + state.position().y,
1015 });
1016 let content = Point {
1017 x: top_left.x + state.content_offset.x,
1018 y: top_left.y + state.content_offset.y,
1019 };
1020 (
1021 GeometryRect::from_origin_size(top_left, state.size()),
1022 content,
1023 )
1024}
1025
1026pub fn build_semantics_tree_from_applier(
1032 applier: &mut MemoryApplier,
1033 root: NodeId,
1034) -> Result<Option<SemanticsTree>, NodeError> {
1035 let mut child_stack = Vec::new();
1036 semantics_node_from_applier(applier, root, None, &mut child_stack)
1037 .map(|root| root.map(SemanticsTree::new))
1038}
1039
1040fn semantics_children_from_applier(
1044 applier: &mut MemoryApplier,
1045 child_stack: &mut Vec<NodeId>,
1046 first_child: usize,
1047 content: Point,
1048) -> Result<Vec<SemanticsNode>, NodeError> {
1049 let end = child_stack.len();
1050 let mut children = Vec::with_capacity(end - first_child);
1051 for index in first_child..end {
1052 let child_id = child_stack[index];
1053 if crate::modifier::is_window_root(applier, child_id) {
1054 continue;
1055 }
1056 if let Some(child) =
1057 semantics_node_from_applier(applier, child_id, Some(content), child_stack)?
1058 {
1059 children.push(child);
1060 }
1061 }
1062 child_stack.truncate(first_child);
1063 Ok(children)
1064}
1065
1066fn semantics_node_from_applier(
1067 applier: &mut MemoryApplier,
1068 node_id: NodeId,
1069 origin: Option<Point>,
1070 child_stack: &mut Vec<NodeId>,
1071) -> Result<Option<SemanticsNode>, NodeError> {
1072 let first_child = child_stack.len();
1073 match applier.with_node::<LayoutNode, _>(node_id, |layout| {
1074 let state = layout.layout_state();
1075 if !state.is_placed() {
1076 return None;
1077 }
1078 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
1079 let config = layout.semantics_configuration();
1080 child_stack.extend_from_slice(&layout.children);
1081 layout.clear_needs_semantics();
1082 Some((role, config, semantics_placement(&state, origin)))
1083 }) {
1084 Ok(Some((role, config, (bounds, content)))) => {
1085 let children =
1086 semantics_children_from_applier(applier, child_stack, first_child, content)?;
1087 return Ok(Some(semantics_node_from_parts(
1088 node_id,
1089 applier.node_generation(node_id),
1090 role,
1091 config,
1092 children,
1093 bounds,
1094 )));
1095 }
1096 Ok(None) => return Ok(None),
1097 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {}
1098 Err(err) => return Err(err),
1099 }
1100
1101 match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |subcompose| {
1102 let state = subcompose.layout_state();
1103 if !state.is_placed() {
1104 return None;
1105 }
1106 let config = subcompose.semantics_configuration();
1107 subcompose.with_active_children(|children| child_stack.extend_from_slice(children));
1108 subcompose.clear_needs_semantics();
1109 Some((config, semantics_placement(&state, origin)))
1110 }) {
1111 Ok(Some((config, (bounds, content)))) => {
1112 let children =
1113 semantics_children_from_applier(applier, child_stack, first_child, content)?;
1114 Ok(Some(semantics_node_from_parts(
1115 node_id,
1116 applier.node_generation(node_id),
1117 SemanticsRole::Subcompose,
1118 config,
1119 children,
1120 bounds,
1121 )))
1122 }
1123 Ok(None) | Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1124 Err(err) => Err(err),
1125 }
1126}
1127
1128pub fn top_modal_from_applier(
1133 applier: &mut MemoryApplier,
1134 root: NodeId,
1135) -> Result<Option<NodeId>, NodeError> {
1136 enum Step {
1141 Enter { node: NodeId, child: bool },
1142 Modal(NodeId),
1143 }
1144
1145 fn push_placed(
1146 steps: &mut Vec<Step>,
1147 node: NodeId,
1148 reach: cranpose_foundation::SemanticsReach,
1149 size: Size,
1150 children: impl IntoIterator<Item = NodeId>,
1151 ) {
1152 if reach.hidden {
1153 return;
1154 }
1155 if reach.is_modal && modal_takes_space(size) {
1156 steps.push(Step::Modal(node));
1157 }
1158 steps.extend(
1159 children
1160 .into_iter()
1161 .map(|node| Step::Enter { node, child: true }),
1162 );
1163 }
1164
1165 let mut steps = vec![Step::Enter {
1166 node: root,
1167 child: false,
1168 }];
1169 while let Some(step) = steps.pop() {
1170 let (node_id, child) = match step {
1171 Step::Modal(node) => return Ok(Some(node)),
1172 Step::Enter { node, child } => (node, child),
1173 };
1174 let node = match applier.get_mut(node_id) {
1175 Ok(node) => node.as_any_mut(),
1176 Err(NodeError::Missing { .. }) => continue,
1177 Err(error) => return Err(error),
1178 };
1179 if let Some(layout) = node.downcast_mut::<LayoutNode>() {
1180 let state = layout.layout_state();
1181 if state.is_placed() && !(child && layout.is_window_root()) {
1182 let children = layout.children.iter().copied();
1183 push_placed(
1184 &mut steps,
1185 node_id,
1186 layout.semantics_reach(),
1187 state.size(),
1188 children,
1189 );
1190 }
1191 } else if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
1192 let state = subcompose.layout_state();
1193 if state.is_placed() {
1194 let reach = subcompose.semantics_reach();
1195 subcompose.with_active_children(|children| {
1196 push_placed(
1197 &mut steps,
1198 node_id,
1199 reach,
1200 state.size(),
1201 children.iter().copied(),
1202 );
1203 });
1204 }
1205 }
1206 }
1207 Ok(None)
1208}
1209
1210#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1211pub struct MeasureLayoutOptions {
1212 pub collect_semantics: bool,
1213 pub build_layout_tree: bool,
1214}
1215
1216impl Default for MeasureLayoutOptions {
1217 fn default() -> Self {
1218 Self {
1219 collect_semantics: true,
1220 build_layout_tree: true,
1221 }
1222 }
1223}
1224
1225pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1233 Ok(applier.get_mut(root)?.needs_layout())
1234}
1235
1236fn publish_window_geometry(
1237 modifier_slices: &crate::modifier::ModifierNodeSlices,
1238 top_left: Point,
1239 layer_translation: Point,
1240 size: Size,
1241) {
1242 let origin = Point {
1243 x: top_left.x + layer_translation.x,
1244 y: top_left.y + layer_translation.y,
1245 };
1246 if let Some(sink) = modifier_slices.text_window_origin() {
1247 sink.set(origin);
1248 }
1249 if let Some(sink) = modifier_slices.viewport_window_rect() {
1250 sink.set(GeometryRect {
1251 x: origin.x,
1252 y: origin.y,
1253 width: size.width,
1254 height: size.height,
1255 });
1256 }
1257 modifier_slices.publish_pointer_input_size(size);
1258}
1259
1260pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1266 Ok(applier.get_mut(root)?.needs_semantics())
1267}
1268
1269#[cfg(test)]
1270pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
1271 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1272}
1273
1274pub fn measure_layout(
1276 applier: &mut MemoryApplier,
1277 root: NodeId,
1278 max_size: Size,
1279) -> Result<LayoutMeasurements, NodeError> {
1280 measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
1281}
1282
1283pub fn measure_layout_with_options(
1284 applier: &mut MemoryApplier,
1285 root: NodeId,
1286 max_size: Size,
1287 options: MeasureLayoutOptions,
1288) -> Result<LayoutMeasurements, NodeError> {
1289 let telemetry_start = Instant::now();
1290 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 mut 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.measure_node(root, normalize_constraints(constraints))?;
1341 let after_measure = Instant::now();
1342
1343 if let Ok(mut applier) = applier_host.try_borrow_typed()
1344 && applier
1345 .with_node::<LayoutNode, _>(root, |node| {
1346 node.set_position(Point::default());
1347 })
1348 .is_err()
1349 {
1350 let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
1351 node.set_position(Point::default());
1352 });
1353 }
1354 let after_root_place = Instant::now();
1355
1356 let (layout_tree, semantics) = {
1357 let mut applier_ref = applier_host.borrow_typed();
1358 let layout_tree = if options.build_layout_tree {
1359 Some(build_layout_tree(&mut applier_ref, &measured)?)
1360 } else {
1361 None
1362 };
1363 let semantics = if options.collect_semantics {
1364 let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
1365 clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
1366 build_semantics_tree_from_layout_tree(layout_tree)
1367 } else {
1368 build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
1369 };
1370 Some(semantics_tree)
1371 } else {
1372 None
1373 };
1374 (layout_tree, semantics)
1375 };
1376 let after_aux = Instant::now();
1377
1378 drop(builder);
1379 let after_builder_drop = Instant::now();
1380
1381 drop(guard);
1382 let after_guard_drop = Instant::now();
1383
1384 log_layout_measure_telemetry(LayoutMeasureTelemetry {
1385 root,
1386 start: telemetry_start,
1387 after_repasses,
1388 after_guard,
1389 after_builder,
1390 after_measure,
1391 after_root_place,
1392 after_aux,
1393 after_builder_drop,
1394 after_guard_drop,
1395 });
1396
1397 Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
1398}
1399
1400fn process_pending_layout_repasses(
1401 applier: &mut MemoryApplier,
1402 root: NodeId,
1403) -> Result<(), NodeError> {
1404 for node_id in crate::render_state::take_modifier_slice_repass_nodes() {
1405 if let Ok(node) = applier.get_mut(node_id) {
1406 let any = node.as_any_mut();
1407 if let Some(layout) = any.downcast_mut::<crate::widgets::nodes::LayoutNode>() {
1408 layout.mark_modifier_slices_dirty();
1409 } else if let Some(subcompose) =
1410 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1411 {
1412 subcompose.mark_modifier_slices_dirty();
1413 }
1414 }
1415 }
1416 let measure_repass_nodes = crate::take_measure_repass_nodes();
1417 let repass_nodes = crate::take_layout_repass_nodes();
1418 if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
1419 return Ok(());
1420 }
1421 for node_id in measure_repass_nodes {
1422 cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
1423 }
1424 for node_id in repass_nodes {
1425 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1426 }
1427 applier.get_mut(root)?.mark_needs_layout();
1428 Ok(())
1429}
1430
1431struct LayoutBuilder {
1432 state: Rc<RefCell<LayoutBuilderState>>,
1433}
1434
1435impl LayoutBuilder {
1436 fn new_with_epoch(
1437 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1438 epoch: u64,
1439 slots: Rc<RefCell<SlotTable>>,
1440 frame_arena: FrameLayoutArena,
1441 ) -> Self {
1442 Self {
1443 state: Rc::new(RefCell::new(LayoutBuilderState::new_with_epoch(
1444 applier,
1445 epoch,
1446 slots,
1447 frame_arena,
1448 ))),
1449 }
1450 }
1451
1452 fn measure_node(
1453 &mut self,
1454 node_id: NodeId,
1455 constraints: Constraints,
1456 ) -> Result<Rc<MeasuredNode>, NodeError> {
1457 LayoutBuilderState::measure_node(Rc::clone(&self.state), node_id, constraints)
1458 }
1459
1460 fn set_runtime_handle(&mut self, handle: Option<RuntimeHandle>) {
1461 self.state.borrow_mut().runtime_handle = handle;
1462 }
1463}
1464
1465impl Drop for LayoutBuilder {
1466 fn drop(&mut self) {
1467 if Rc::strong_count(&self.state) != 1 {
1468 return;
1469 }
1470 let Ok(mut state) = self.state.try_borrow_mut() else {
1471 return;
1472 };
1473 crate::render_state::replace_layout_frame_arena(std::mem::take(&mut state.frame_arena));
1474 }
1475}
1476
1477struct LayoutBuilderState {
1478 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1479 runtime_handle: Option<RuntimeHandle>,
1480 slots: Rc<RefCell<SlotTable>>,
1481 cache_epoch: u64,
1482 cache_floor: u64,
1483 frame_arena: FrameLayoutArena,
1484}
1485
1486struct LayoutRuntimeFrameBindingCleanup {
1487 state: Rc<RefCell<LayoutRuntimeState>>,
1488}
1489
1490impl LayoutRuntimeFrameBindingCleanup {
1491 fn new(state: Rc<RefCell<LayoutRuntimeState>>) -> Self {
1492 Self { state }
1493 }
1494}
1495
1496impl Drop for LayoutRuntimeFrameBindingCleanup {
1497 fn drop(&mut self) {
1498 self.state.borrow().clear_frame_bindings();
1499 }
1500}
1501
1502impl LayoutBuilderState {
1503 fn new_with_epoch(
1504 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1505 epoch: u64,
1506 slots: Rc<RefCell<SlotTable>>,
1507 frame_arena: FrameLayoutArena,
1508 ) -> Self {
1509 let runtime_handle = applier.borrow_typed().runtime_handle();
1510
1511 Self {
1512 applier,
1513 runtime_handle,
1514 slots,
1515 cache_epoch: epoch,
1516 cache_floor: crate::render_state::layout_cache_floor(),
1517 frame_arena,
1518 }
1519 }
1520
1521 fn try_with_applier_result<R>(
1522 state_rc: &Rc<RefCell<Self>>,
1523 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1524 ) -> Option<Result<R, NodeError>> {
1525 let host = {
1526 let state = state_rc.borrow();
1527 Rc::clone(&state.applier)
1528 };
1529
1530 let Ok(mut applier) = host.try_borrow_typed() else {
1531 return None;
1532 };
1533
1534 Some(f(&mut applier))
1535 }
1536
1537 fn with_applier_result<R>(
1538 state_rc: &Rc<RefCell<Self>>,
1539 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1540 ) -> Result<R, NodeError> {
1541 Self::try_with_applier_result(state_rc, f).unwrap_or_else(|| {
1542 Err(NodeError::MissingContext {
1543 id: NodeId::default(),
1544 reason: "applier already borrowed",
1545 })
1546 })
1547 }
1548
1549 fn clear_node_placed(state_rc: &Rc<RefCell<Self>>, node_id: NodeId) {
1550 let host = {
1551 let state = state_rc.borrow();
1552 Rc::clone(&state.applier)
1553 };
1554 let Ok(mut applier) = host.try_borrow_typed() else {
1555 return;
1556 };
1557 if applier
1558 .with_node::<LayoutNode, _>(node_id, |node| {
1559 node.clear_placed();
1560 })
1561 .is_err()
1562 {
1563 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1564 node.clear_placed();
1565 });
1566 }
1567 }
1568
1569 fn measure_node(
1570 state_rc: Rc<RefCell<Self>>,
1571 node_id: NodeId,
1572 constraints: Constraints,
1573 ) -> Result<Rc<MeasuredNode>, NodeError> {
1574 let telemetry_start = Instant::now();
1575 Self::clear_node_placed(&state_rc, node_id);
1576
1577 if let Some(subcompose) =
1578 Self::try_measure_subcompose(Rc::clone(&state_rc), node_id, constraints)?
1579 {
1580 log_node_measure_telemetry(
1581 "subcompose",
1582 node_id,
1583 constraints,
1584 subcompose.size,
1585 subcompose.children.len(),
1586 telemetry_start,
1587 );
1588 return Ok(subcompose);
1589 }
1590
1591 if let Some(result) = Self::try_with_applier_result(&state_rc, |applier| {
1592 match applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1593 LayoutNodeSnapshot::from_layout_node(layout_node)
1594 }) {
1595 Ok(snapshot) => Ok(Some(snapshot)),
1596 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1597 Err(err) => Err(err),
1598 }
1599 }) && let Some(snapshot) = result?
1600 {
1601 let measured =
1602 Self::measure_layout_node(Rc::clone(&state_rc), node_id, snapshot, constraints)?;
1603 log_node_measure_telemetry(
1604 "layout",
1605 node_id,
1606 constraints,
1607 measured.size,
1608 measured.children.len(),
1609 telemetry_start,
1610 );
1611 return Ok(measured);
1612 }
1613
1614 let measured = Rc::new(MeasuredNode::new(
1615 node_id,
1616 Size::default(),
1617 Point { x: 0.0, y: 0.0 },
1618 Point::default(),
1619 Vec::new(),
1620 ));
1621 log_node_measure_telemetry(
1622 "fallback",
1623 node_id,
1624 constraints,
1625 measured.size,
1626 measured.children.len(),
1627 telemetry_start,
1628 );
1629 Ok(measured)
1630 }
1631
1632 fn cached_measure_node_with_applier(
1633 applier: &mut MemoryApplier,
1634 node_id: NodeId,
1635 constraints: Constraints,
1636 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1637 let Some(data) = Self::layout_child_measure_data(applier, node_id)? else {
1638 return Ok(None);
1639 };
1640 if data.needs_measure
1641 || data.needs_layout
1642 || data.cache.epoch() == 0
1643 || data.cache.epoch() != crate::render_state::current_layout_cache_epoch()
1644 {
1645 return Ok(None);
1646 }
1647
1648 let Some(measured) = data.cache.get_measurement(constraints) else {
1649 return Ok(None);
1650 };
1651
1652 if let Some(layout_state) = data.layout_state {
1653 let mut layout_state = layout_state.borrow_mut();
1654 layout_state.set_size(measured.size);
1655 } else {
1656 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1657 node.set_measured_size(measured.size);
1658 });
1659 }
1660
1661 Ok(Some(measured))
1662 }
1663
1664 fn try_measure_subcompose(
1665 state_rc: Rc<RefCell<Self>>,
1666 node_id: NodeId,
1667 constraints: Constraints,
1668 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1669 let applier_host = {
1670 let state = state_rc.borrow();
1671 Rc::clone(&state.applier)
1672 };
1673
1674 let (node_handle, resolved_modifiers) = {
1675 let Ok(mut applier) = applier_host.try_borrow_typed() else {
1676 return Ok(None);
1677 };
1678 let node = match applier.get_mut(node_id) {
1679 Ok(node) => node,
1680 Err(NodeError::Missing { .. }) => return Ok(None),
1681 Err(err) => return Err(err),
1682 };
1683 let any = node.as_any_mut();
1684 if let Some(subcompose) =
1685 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1686 {
1687 let handle = subcompose.handle();
1688 let resolved_modifiers = handle
1689 .resolved_modifiers()
1690 .on_device_grid(subcompose.density().density());
1691 (handle, resolved_modifiers)
1692 } else {
1693 return Ok(None);
1694 }
1695 };
1696
1697 let runtime_handle = {
1698 let mut state = state_rc.borrow_mut();
1699 if state.runtime_handle.is_none()
1700 && let Ok(applier) = applier_host.try_borrow_typed()
1701 {
1702 state.runtime_handle = applier.runtime_handle();
1703 }
1704 state
1705 .runtime_handle
1706 .clone()
1707 .ok_or(NodeError::MissingContext {
1708 id: node_id,
1709 reason: "runtime handle required for subcomposition",
1710 })?
1711 };
1712
1713 let props = resolved_modifiers.layout_properties();
1714 let padding = resolved_modifiers.padding();
1715 let offset = resolved_modifiers.offset();
1716 let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1717
1718 if let DimensionConstraint::Points(width) = props.width() {
1719 let constrained_width = width - padding.horizontal_sum();
1720 inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1721 inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1722 }
1723 if let DimensionConstraint::Points(height) = props.height() {
1724 let constrained_height = height - padding.vertical_sum();
1725 inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1726 inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1727 }
1728
1729 let mut slots_guard = SlotsGuard::take(Rc::clone(&state_rc));
1730 let slots_host = slots_guard.host();
1731 let applier_host_dyn: Rc<dyn ApplierHost> = applier_host.clone();
1732 let observer = SnapshotStateObserver::new(|callback| callback());
1733 let composer = Composer::new(
1734 Rc::clone(&slots_host),
1735 applier_host_dyn,
1736 runtime_handle,
1737 observer,
1738 Some(node_id),
1739 );
1740 composer.enter_phase(Phase::Measure);
1741
1742 let state_rc_clone = Rc::clone(&state_rc);
1743 let measure_error = RefCell::new(None);
1744 let state_rc_for_subcompose = Rc::clone(&state_rc_clone);
1745 let error_for_subcompose = &measure_error;
1746 let measured_children = node_handle.measured_children_scratch();
1747 let measured_children_for_subcompose = Rc::clone(&measured_children);
1748 let state_rc_for_cached = Rc::clone(&state_rc_clone);
1749 let error_for_cached = &measure_error;
1750 let measured_children_for_cached = Rc::clone(&measured_children);
1751 let measured_children_for_lookup = Rc::clone(&measured_children);
1752 let measured_children_for_retained = Rc::clone(&measured_children);
1753
1754 let measure_result = node_handle.measure_with_cached_batch(
1755 &composer,
1756 node_id,
1757 inner_constraints,
1758 CachedBatchMeasureInputs {
1759 measurer: Box::new(
1760 move |child_id: NodeId, child_constraints: Constraints| -> Size {
1761 match Self::measure_node(
1762 Rc::clone(&state_rc_for_subcompose),
1763 child_id,
1764 child_constraints,
1765 ) {
1766 Ok(measured) => {
1767 measured_children_for_subcompose
1768 .borrow_mut()
1769 .insert(child_id, Rc::clone(&measured));
1770 measured.size
1771 }
1772 Err(err) => {
1773 let mut slot = error_for_subcompose.borrow_mut();
1774 if slot.is_none() {
1775 *slot = Some(err);
1776 }
1777 Size::default()
1778 }
1779 }
1780 },
1781 ),
1782 cached_measure_batch_registrar: Box::new(
1783 move |child_ids: &[NodeId],
1784 child_constraints: Constraints,
1785 out: &mut Vec<Option<Size>>| {
1786 out.clear();
1787 out.resize(child_ids.len(), None);
1788
1789 let applier_host = {
1790 let state = state_rc_for_cached.borrow();
1791 Rc::clone(&state.applier)
1792 };
1793 let Ok(mut applier) = applier_host.try_borrow_typed() else {
1794 return;
1795 };
1796
1797 let mut measured_children = measured_children_for_cached.borrow_mut();
1798 for (index, &child_id) in child_ids.iter().enumerate() {
1799 match Self::cached_measure_node_with_applier(
1800 &mut applier,
1801 child_id,
1802 child_constraints,
1803 ) {
1804 Ok(Some(measured)) => {
1805 out[index] = Some(measured.size);
1806 measured_children.insert(child_id, Rc::clone(&measured));
1807 }
1808 Ok(None) => {}
1809 Err(err) => {
1810 let mut slot = error_for_cached.borrow_mut();
1811 if slot.is_none() {
1812 *slot = Some(err);
1813 }
1814 break;
1815 }
1816 }
1817 }
1818 },
1819 ),
1820 retained_measure_lookup: Box::new(move |child_id| {
1821 measured_children_for_lookup
1822 .borrow()
1823 .get(&child_id)
1824 .cloned()
1825 }),
1826 retained_measure_registrar: Box::new(move |measurements| {
1827 let mut measured_children = measured_children_for_retained.borrow_mut();
1828 for measured in measurements {
1829 measured_children.insert(measured.node_id(), Rc::clone(measured));
1830 }
1831 }),
1832 error: &measure_error,
1833 },
1834 )?;
1835 drop(composer);
1836 slots_guard.restore(slots_host.into_table()?);
1837
1838 if let Some(err) = measure_error.borrow_mut().take() {
1839 return Err(err);
1840 }
1841
1842 let cranpose_ui_layout::MeasureResult {
1843 size: measured_size,
1844 placements,
1845 } = measure_result;
1846
1847 let mut width = measured_size.width + padding.horizontal_sum();
1848 let mut height = measured_size.height + padding.vertical_sum();
1849
1850 width = resolve_dimension(
1851 width,
1852 props.width(),
1853 props.min_width(),
1854 props.max_width(),
1855 constraints.min_width,
1856 constraints.max_width,
1857 );
1858 height = resolve_dimension(
1859 height,
1860 props.height(),
1861 props.min_height(),
1862 props.max_height(),
1863 constraints.min_height,
1864 constraints.max_height,
1865 );
1866
1867 let mut children = Vec::with_capacity(placements.len());
1868 let mut measured_children_by_id = measured_children.borrow_mut();
1869
1870 if let Ok(mut applier) = applier_host.try_borrow_typed() {
1871 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1872 parent_node.set_measured_size(Size { width, height });
1873 parent_node.clear_needs_measure();
1874 parent_node.clear_needs_layout();
1875 });
1876 }
1877
1878 for placement in &placements {
1879 let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1880 measured
1881 } else {
1882 Self::measure_node(Rc::clone(&state_rc), placement.node_id, inner_constraints)?
1883 };
1884 let policy_position = Point {
1885 x: padding.left + placement.x,
1886 y: padding.top + placement.y,
1887 };
1888 let retained_position = Point {
1889 x: policy_position.x + child.offset.x,
1890 y: policy_position.y + child.offset.y,
1891 };
1892
1893 if let Ok(mut applier) = applier_host.try_borrow_typed()
1894 && applier
1895 .with_node::<LayoutNode, _>(placement.node_id, |node| {
1896 node.set_position(retained_position);
1897 })
1898 .is_err()
1899 {
1900 let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1901 node.set_position(retained_position);
1902 });
1903 }
1904
1905 children.push(MeasuredChild {
1906 node: child,
1907 offset: policy_position,
1908 });
1909 }
1910
1911 node_handle.set_active_children(children.iter().map(|c| c.node.node_id));
1912 node_handle.recycle_placement_scratch(placements);
1913
1914 Ok(Some(Rc::new(MeasuredNode::new(
1915 node_id,
1916 Size { width, height },
1917 offset,
1918 Point::default(),
1919 children,
1920 ))))
1921 }
1922 fn measure_through_modifier_chain(
1923 state_rc: &Rc<RefCell<Self>>,
1924 node_id: NodeId,
1925 runtime_state: &mut LayoutRuntimeState,
1926 measure_policy: &Rc<dyn MeasurePolicy>,
1927 constraints: Constraints,
1928 layout_node_data: &mut Vec<LayoutModifierNodeData>,
1929 placements: &mut Vec<Placement>,
1930 ) -> ModifierChainMeasurement {
1931 use cranpose_foundation::NodeCapabilities;
1932
1933 layout_node_data.clear();
1934 let mut offset = Point::default();
1935 let mut density = crate::density::Density::default();
1936 let mut window_root = false;
1937 let mut geometry = None;
1938
1939 {
1940 let state = state_rc.borrow();
1941 let mut applier = state.applier.borrow_typed();
1942
1943 let _ = applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1944 density = layout_node.density();
1945 window_root = layout_node.is_window_root();
1946 geometry = Some(layout_node.coordinator_geometry());
1947 let chain_handle = layout_node.modifier_chain();
1948
1949 if !chain_handle.has_layout_nodes() {
1950 return;
1951 }
1952
1953 chain_handle.chain().for_each_forward_matching(
1954 NodeCapabilities::LAYOUT,
1955 |node_ref| {
1956 if let Some(index) = node_ref.entry_index() {
1957 if let Some(node_rc) = chain_handle.chain().get_node_rc(index) {
1958 layout_node_data.push((index, Rc::clone(&node_rc)));
1959 }
1960
1961 node_ref.with_node(|node| {
1962 if let Some(offset_node) =
1963 node.as_any()
1964 .downcast_ref::<crate::modifier_nodes::OffsetNode>()
1965 {
1966 let delta = offset_node.device_offset(density.density());
1967 offset.x += delta.x;
1968 offset.y += delta.y;
1969 }
1970 });
1971 }
1972 },
1973 );
1974 });
1975 }
1976
1977 let scope = crate::density::DensityMeasureScope::new(density);
1978
1979 if layout_node_data.is_empty() {
1980 let final_size = measure_policy.measure_into(
1981 &scope,
1982 runtime_state.child_measurables(),
1983 constraints,
1984 placements,
1985 );
1986 if let Some(geometry) = geometry {
1987 geometry.replace([GeometryRect {
1988 x: 0.0,
1989 y: 0.0,
1990 width: final_size.width,
1991 height: final_size.height,
1992 }]);
1993 }
1994
1995 return ModifierChainMeasurement {
1996 size: final_size,
1997 content_offset: Point::default(),
1998 offset,
1999 window_root,
2000 };
2001 }
2002
2003 runtime_state.reconcile_coordinator_chain(layout_node_data.as_slice());
2004 let frame = CoordinatorFrame::new(
2005 measure_policy,
2006 &scope,
2007 runtime_state.child_measurables(),
2008 placements,
2009 );
2010
2011 let placeable = runtime_state
2012 .coordinator_chain()
2013 .measure_from(0, &frame, constraints);
2014 let final_size = Size {
2015 width: placeable.width(),
2016 height: placeable.height(),
2017 };
2018
2019 if let Some(geometry) = geometry {
2020 runtime_state
2021 .coordinator_chain()
2022 .write_geometry(&geometry, offset);
2023 }
2024
2025 let content_offset = placeable.content_offset();
2026 let all_placement_offset = Point {
2027 x: content_offset.0,
2028 y: content_offset.1,
2029 };
2030
2031 let content_offset = Point {
2032 x: all_placement_offset.x - offset.x,
2033 y: all_placement_offset.y - offset.y,
2034 };
2035
2036 let invalidations = frame.take_invalidations();
2037 if !invalidations.is_empty() {
2038 Self::with_applier_result(state_rc, |applier| {
2039 applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2040 for kind in invalidations {
2041 match kind {
2042 InvalidationKind::Layout => layout_node.mark_needs_measure(),
2043 InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2044 InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2045 InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2046 InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2047 }
2048 }
2049 })
2050 })
2051 .ok();
2052 }
2053
2054 ModifierChainMeasurement {
2055 size: final_size,
2056 content_offset,
2057 offset,
2058 window_root,
2059 }
2060 }
2061
2062 fn layout_child_measure_data(
2063 applier: &mut MemoryApplier,
2064 child_id: NodeId,
2065 ) -> Result<Option<LayoutChildMeasureData>, NodeError> {
2066 match applier.with_node::<LayoutNode, _>(child_id, |n| LayoutChildMeasureData {
2067 cache: n.cache_handles(),
2068 layout_state: Some(n.layout_state_handle()),
2069 needs_layout: n.needs_layout(),
2070 needs_measure: n.needs_measure(),
2071 }) {
2072 Ok(data) => Ok(Some(data)),
2073 Err(NodeError::TypeMismatch { .. }) => {
2074 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |n| {
2075 LayoutChildMeasureData {
2076 cache: n.cache_handles(),
2077 layout_state: None,
2078 needs_layout: n.needs_layout(),
2079 needs_measure: n.needs_measure(),
2080 }
2081 }) {
2082 Ok(data) => Ok(Some(data)),
2083 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
2084 Err(err) => Err(err),
2085 }
2086 }
2087 Err(NodeError::Missing { .. }) => Ok(None),
2088 Err(err) => Err(err),
2089 }
2090 }
2091
2092 fn measure_layout_node(
2093 state_rc: Rc<RefCell<Self>>,
2094 node_id: NodeId,
2095 snapshot: LayoutNodeSnapshot,
2096 constraints: Constraints,
2097 ) -> Result<Rc<MeasuredNode>, NodeError> {
2098 let (cache_epoch, cache_floor) = {
2099 let state = state_rc.borrow();
2100 (state.cache_epoch, state.cache_floor)
2101 };
2102 let LayoutNodeSnapshot {
2103 measure_policy,
2104 cache,
2105 layout_runtime_state,
2106 needs_layout,
2107 needs_measure,
2108 } = snapshot;
2109 cache.activate(cache_epoch);
2110
2111 if !needs_measure
2112 && !needs_layout
2113 && let Some(cached) = cache.get_measurement(constraints)
2114 {
2115 Self::with_applier_result(&state_rc, |applier| {
2116 applier.with_node::<LayoutNode, _>(node_id, |node| {
2117 node.clear_needs_measure();
2118 node.clear_needs_layout();
2119 })
2120 })
2121 .ok();
2122 return Ok(cached);
2123 }
2124
2125 let (runtime_handle, applier_host) = {
2126 let state = state_rc.borrow();
2127 (state.runtime_handle.clone(), Rc::clone(&state.applier))
2128 };
2129
2130 let measure_handle = LayoutMeasureHandle::new(Rc::clone(&state_rc));
2131 let error = Rc::new(RefCell::new(None));
2132 let mut pools = VecPools::acquire(Rc::clone(&state_rc));
2133 let (records, child_ids, layout_node_data, placements) = pools.parts();
2134
2135 applier_host
2136 .borrow_typed()
2137 .with_node::<LayoutNode, _>(node_id, |node| {
2138 child_ids.extend_from_slice(&node.children);
2139 })?;
2140
2141 let mut valid_child_count = 0;
2142 for index in 0..child_ids.len() {
2143 let child_id = child_ids[index];
2144 let child_exists = {
2145 let mut applier = applier_host.borrow_typed();
2146 Self::layout_child_measure_data(&mut applier, child_id)?.is_some()
2147 };
2148 if child_exists {
2149 child_ids[valid_child_count] = child_id;
2150 valid_child_count += 1;
2151 }
2152 }
2153 child_ids.truncate(valid_child_count);
2154
2155 let _frame_binding_cleanup =
2156 LayoutRuntimeFrameBindingCleanup::new(Rc::clone(&layout_runtime_state));
2157
2158 {
2159 let mut runtime_state = layout_runtime_state.borrow_mut();
2160 runtime_state.reconcile_child_measurables(child_ids.as_slice());
2161
2162 for (index, &child_id) in child_ids.iter().enumerate() {
2163 let data = {
2164 let mut applier = applier_host.borrow_typed();
2165 Self::layout_child_measure_data(&mut applier, child_id)?
2166 };
2167 let Some(data) = data else {
2168 continue;
2169 };
2170
2171 let child_is_stale = data.is_stale(cache_floor);
2172 let child_cache_epoch = if child_is_stale {
2173 cache_epoch
2174 } else {
2175 data.cache.epoch()
2176 };
2177 let child_state = runtime_state.child_state(index);
2178 child_state.configure(LayoutChildMeasureConfig {
2179 applier: Rc::clone(&applier_host),
2180 node_id: child_id,
2181 error: Rc::clone(&error),
2182 runtime_handle: runtime_handle.clone(),
2183 cache: data.cache,
2184 cache_epoch: child_cache_epoch,
2185 force_remeasure: child_is_stale,
2186 measure_handle: Some(measure_handle.clone()),
2187 layout_state: data.layout_state,
2188 });
2189 records.push((child_id, ChildRecord { state: child_state }));
2190 }
2191 }
2192
2193 let chain_constraints = constraints;
2194
2195 let modifier_chain_result = {
2196 let mut runtime_state = layout_runtime_state.borrow_mut();
2197 Self::measure_through_modifier_chain(
2198 &state_rc,
2199 node_id,
2200 &mut runtime_state,
2201 &measure_policy,
2202 chain_constraints,
2203 layout_node_data,
2204 placements,
2205 )
2206 };
2207
2208 let (width, height, content_offset, offset, window_root) = {
2209 let result = modifier_chain_result;
2210 if let Some(err) = error.borrow_mut().take() {
2211 return Err(err);
2212 }
2213
2214 (
2215 result.size.width,
2216 result.size.height,
2217 result.content_offset,
2218 result.offset,
2219 result.window_root,
2220 )
2221 };
2222
2223 let mut measured_children = Vec::with_capacity(records.len());
2224 for (child_id, record) in records.iter() {
2225 if let Some(measured) = record.state.take_measured() {
2226 let placed = placements
2227 .iter()
2228 .find(|placement| placement.node_id == *child_id)
2229 .map(|placement| Point {
2230 x: placement.x,
2231 y: placement.y,
2232 });
2233 if let Some(raw) = placed {
2234 record.state.place_retained(Point {
2235 x: raw.x + measured.offset.x,
2236 y: raw.y + measured.offset.y,
2237 });
2238 }
2239 let base_position = placed
2240 .or_else(|| record.state.last_position())
2241 .unwrap_or(Point { x: 0.0, y: 0.0 });
2242 let position = Point {
2243 x: content_offset.x + base_position.x,
2244 y: content_offset.y + base_position.y,
2245 };
2246 measured_children.push(MeasuredChild {
2247 node: measured,
2248 offset: position,
2249 });
2250 }
2251 }
2252
2253 let measured = Rc::new(
2254 MeasuredNode::new(
2255 node_id,
2256 Size { width, height },
2257 offset,
2258 content_offset,
2259 measured_children,
2260 )
2261 .with_window_root(window_root),
2262 );
2263
2264 cache.store_measurement(constraints, Rc::clone(&measured));
2265
2266 Self::with_applier_result(&state_rc, |applier| {
2267 applier.with_node::<LayoutNode, _>(node_id, |node| {
2268 node.clear_needs_measure();
2269 node.clear_needs_layout();
2270 node.set_measured_size(Size { width, height });
2271 node.set_content_offset(content_offset);
2272 })
2273 })
2274 .ok();
2275
2276 Ok(measured)
2277 }
2278}
2279
2280struct LayoutChildMeasureData {
2281 cache: LayoutNodeCacheHandles,
2282 layout_state: Option<Rc<RefCell<LayoutState>>>,
2283 needs_layout: bool,
2284 needs_measure: bool,
2285}
2286
2287impl LayoutChildMeasureData {
2288 fn is_stale(&self, cache_floor: u64) -> bool {
2289 self.needs_layout || self.needs_measure || self.cache.epoch() < cache_floor
2290 }
2291}
2292
2293struct LayoutNodeSnapshot {
2294 measure_policy: Rc<dyn MeasurePolicy>,
2295 cache: LayoutNodeCacheHandles,
2296 layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
2297 needs_layout: bool,
2298 needs_measure: bool,
2299}
2300
2301impl LayoutNodeSnapshot {
2302 fn from_layout_node(node: &LayoutNode) -> Self {
2303 Self {
2304 measure_policy: Rc::clone(&node.measure_policy),
2305 cache: node.cache_handles(),
2306 layout_runtime_state: node.layout_runtime_state_handle(),
2307 needs_layout: node.needs_layout(),
2308 needs_measure: node.needs_measure(),
2309 }
2310 }
2311}
2312
2313struct VecPools {
2314 state: Rc<RefCell<LayoutBuilderState>>,
2315 records: Vec<(NodeId, ChildRecord)>,
2316 child_ids: Vec<NodeId>,
2317 layout_node_data: Vec<LayoutModifierNodeData>,
2318 placements: Vec<Placement>,
2319}
2320
2321impl VecPools {
2322 fn acquire(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2323 let (records, child_ids, layout_node_data, placements) = {
2324 let mut state_mut = state.borrow_mut();
2325 (
2326 state_mut.frame_arena.tmp_records.acquire(),
2327 state_mut.frame_arena.tmp_child_ids.acquire(),
2328 state_mut.frame_arena.tmp_layout_node_data.acquire(),
2329 state_mut.frame_arena.tmp_placements.acquire(),
2330 )
2331 };
2332 Self {
2333 state,
2334 records,
2335 child_ids,
2336 layout_node_data,
2337 placements,
2338 }
2339 }
2340
2341 #[expect(clippy::type_complexity)]
2342 fn parts(
2343 &mut self,
2344 ) -> (
2345 &mut Vec<(NodeId, ChildRecord)>,
2346 &mut Vec<NodeId>,
2347 &mut Vec<LayoutModifierNodeData>,
2348 &mut Vec<Placement>,
2349 ) {
2350 (
2351 &mut self.records,
2352 &mut self.child_ids,
2353 &mut self.layout_node_data,
2354 &mut self.placements,
2355 )
2356 }
2357}
2358
2359impl Drop for VecPools {
2360 fn drop(&mut self) {
2361 let mut state = self.state.borrow_mut();
2362 state
2363 .frame_arena
2364 .tmp_records
2365 .release(std::mem::take(&mut self.records));
2366 state
2367 .frame_arena
2368 .tmp_child_ids
2369 .release(std::mem::take(&mut self.child_ids));
2370 state
2371 .frame_arena
2372 .tmp_layout_node_data
2373 .release(std::mem::take(&mut self.layout_node_data));
2374 state
2375 .frame_arena
2376 .tmp_placements
2377 .release(std::mem::take(&mut self.placements));
2378 }
2379}
2380
2381struct SlotsGuard {
2382 state: Rc<RefCell<LayoutBuilderState>>,
2383 slots: Option<SlotTable>,
2384}
2385
2386impl SlotsGuard {
2387 fn take(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2388 let slots = {
2389 let state_ref = state.borrow();
2390 let mut slots_ref = state_ref.slots.borrow_mut();
2391 std::mem::take(&mut *slots_ref)
2392 };
2393 Self {
2394 state,
2395 slots: Some(slots),
2396 }
2397 }
2398
2399 fn host(&mut self) -> Rc<SlotsHost> {
2400 let slots = self.slots.take().unwrap_or_default();
2401 Rc::new(SlotsHost::new(slots))
2402 }
2403
2404 fn restore(&mut self, slots: SlotTable) {
2405 debug_assert!(self.slots.is_none());
2406 self.slots = Some(slots);
2407 }
2408}
2409
2410impl Drop for SlotsGuard {
2411 fn drop(&mut self) {
2412 if let Some(slots) = self.slots.take() {
2413 let state_ref = self.state.borrow();
2414 *state_ref.slots.borrow_mut() = slots;
2415 }
2416 }
2417}
2418
2419#[derive(Clone)]
2420struct LayoutMeasureHandle {
2421 state: Rc<RefCell<LayoutBuilderState>>,
2422}
2423
2424impl LayoutMeasureHandle {
2425 fn new(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2426 Self { state }
2427 }
2428
2429 fn measure(
2430 &self,
2431 node_id: NodeId,
2432 constraints: Constraints,
2433 ) -> Result<Rc<MeasuredNode>, NodeError> {
2434 LayoutBuilderState::measure_node(Rc::clone(&self.state), node_id, constraints)
2435 }
2436}
2437
2438#[derive(Debug, Clone)]
2439pub(crate) struct MeasuredNode {
2440 node_id: NodeId,
2441 size: Size,
2442 offset: Point,
2443 content_offset: Point,
2444 children: Vec<MeasuredChild>,
2445 window_root: bool,
2446}
2447
2448impl MeasuredNode {
2449 fn new(
2450 node_id: NodeId,
2451 size: Size,
2452 offset: Point,
2453 content_offset: Point,
2454 children: Vec<MeasuredChild>,
2455 ) -> Self {
2456 Self {
2457 node_id,
2458 size,
2459 offset,
2460 content_offset,
2461 children,
2462 window_root: false,
2463 }
2464 }
2465
2466 fn with_window_root(mut self, window_root: bool) -> Self {
2467 self.window_root = window_root;
2468 self
2469 }
2470
2471 pub(crate) fn size_for_parent(&self) -> Size {
2472 if self.window_root {
2473 Size::new(0.0, 0.0)
2474 } else {
2475 self.size
2476 }
2477 }
2478
2479 #[cfg(test)]
2480 pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2481 Self::new(
2482 node_id,
2483 size,
2484 Point::default(),
2485 Point::default(),
2486 Vec::new(),
2487 )
2488 }
2489
2490 pub(crate) fn node_id(&self) -> NodeId {
2491 self.node_id
2492 }
2493
2494 pub(crate) fn size(&self) -> Size {
2495 self.size
2496 }
2497}
2498
2499#[derive(Debug, Clone)]
2500struct MeasuredChild {
2501 node: Rc<MeasuredNode>,
2502 offset: Point,
2503}
2504
2505struct ChildRecord {
2506 state: Rc<LayoutChildMeasureState>,
2507}
2508
2509struct CoordinatorFrame<'a> {
2510 measure_policy: &'a Rc<dyn MeasurePolicy>,
2511 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2512 measurables: &'a [Box<dyn Measurable>],
2513 placements: RefCell<&'a mut Vec<Placement>>,
2514 context: RefCell<LayoutNodeContext>,
2515}
2516
2517impl<'a> CoordinatorFrame<'a> {
2518 fn new(
2519 measure_policy: &'a Rc<dyn MeasurePolicy>,
2520 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2521 measurables: &'a [Box<dyn Measurable>],
2522 placements: &'a mut Vec<Placement>,
2523 ) -> Self {
2524 Self {
2525 measure_policy,
2526 scope,
2527 measurables,
2528 placements: RefCell::new(placements),
2529 context: RefCell::new(LayoutNodeContext::new(scope.density())),
2530 }
2531 }
2532
2533 fn take_invalidations(&self) -> Vec<InvalidationKind> {
2534 self.context.borrow_mut().take_invalidations()
2535 }
2536}
2537
2538struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2539 chain: &'chain CoordinatorChain,
2540 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2541 index: usize,
2542}
2543
2544impl Measurable for CoordinatorLink<'_, '_, '_> {
2545 fn measure(&self, constraints: Constraints) -> Placeable {
2546 self.chain.measure_from(self.index, self.frame, constraints)
2547 }
2548
2549 fn min_intrinsic_width(&self, height: f32) -> f32 {
2550 self.chain
2551 .min_intrinsic_width_from(self.index, self.frame, height)
2552 }
2553
2554 fn max_intrinsic_width(&self, height: f32) -> f32 {
2555 self.chain
2556 .max_intrinsic_width_from(self.index, self.frame, height)
2557 }
2558
2559 fn min_intrinsic_height(&self, width: f32) -> f32 {
2560 self.chain
2561 .min_intrinsic_height_from(self.index, self.frame, width)
2562 }
2563
2564 fn max_intrinsic_height(&self, width: f32) -> f32 {
2565 self.chain
2566 .max_intrinsic_height_from(self.index, self.frame, width)
2567 }
2568}
2569
2570struct CoordinatorNode {
2571 modifier_index: usize,
2572 node: Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2573 measured_size: Cell<Size>,
2574 accumulated_offset: Cell<Point>,
2575}
2576
2577impl CoordinatorNode {
2578 fn new(
2579 modifier_index: usize,
2580 node: Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2581 ) -> Self {
2582 Self {
2583 modifier_index,
2584 node,
2585 measured_size: Cell::new(Size::default()),
2586 accumulated_offset: Cell::new(Point::default()),
2587 }
2588 }
2589
2590 fn matches(
2591 &self,
2592 modifier_index: usize,
2593 node: &Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2594 ) -> bool {
2595 self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2596 }
2597
2598 #[cfg(test)]
2599 fn ptr(&self) -> usize {
2600 Rc::as_ptr(&self.node) as *const () as usize
2601 }
2602}
2603
2604#[derive(Default)]
2605struct CoordinatorChain {
2606 nodes: Vec<CoordinatorNode>,
2607 inner_size: Cell<Size>,
2609}
2610
2611impl CoordinatorChain {
2612 fn reconcile(&mut self, layout_node_data: &[LayoutModifierNodeData]) {
2613 if self.matches(layout_node_data) {
2614 return;
2615 }
2616
2617 let mut previous_nodes = std::mem::take(&mut self.nodes);
2618 self.nodes.reserve(layout_node_data.len());
2619
2620 for (modifier_index, node) in layout_node_data {
2621 if let Some(position) = previous_nodes
2622 .iter()
2623 .position(|candidate| candidate.matches(*modifier_index, node))
2624 {
2625 self.nodes.push(previous_nodes.swap_remove(position));
2626 } else {
2627 self.nodes
2628 .push(CoordinatorNode::new(*modifier_index, Rc::clone(node)));
2629 }
2630 }
2631 }
2632
2633 fn matches(&self, layout_node_data: &[LayoutModifierNodeData]) -> bool {
2634 self.nodes.len() == layout_node_data.len()
2635 && self
2636 .nodes
2637 .iter()
2638 .zip(layout_node_data.iter())
2639 .all(|(node, (modifier_index, node_rc))| node.matches(*modifier_index, node_rc))
2640 }
2641
2642 fn measure_from(
2643 &self,
2644 index: usize,
2645 frame: &CoordinatorFrame<'_>,
2646 constraints: Constraints,
2647 ) -> Placeable {
2648 let Some(node) = self.nodes.get(index) else {
2649 let mut placements = frame.placements.borrow_mut();
2650 let size = frame.measure_policy.measure_into(
2651 frame.scope,
2652 frame.measurables,
2653 constraints,
2654 &mut placements,
2655 );
2656 self.inner_size.set(size);
2657 return Placeable::value(size.width, size.height, NodeId::default());
2658 };
2659
2660 let wrapped = CoordinatorLink {
2661 chain: self,
2662 frame,
2663 index: index + 1,
2664 };
2665 let node_borrow = node.node.borrow();
2666
2667 let Some(layout_node) = node_borrow.as_layout_node() else {
2668 let placeable = wrapped.measure(constraints);
2669 node.measured_size.set(Size {
2670 width: placeable.width(),
2671 height: placeable.height(),
2672 });
2673 let child_accumulated = self.total_content_offset_from(index + 1);
2674 node.accumulated_offset.set(child_accumulated);
2675 return Placeable::value_with_offset(
2676 placeable.width(),
2677 placeable.height(),
2678 NodeId::default(),
2679 (child_accumulated.x, child_accumulated.y),
2680 );
2681 };
2682
2683 let result = match frame.context.try_borrow_mut() {
2684 Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
2685 Err(_) => {
2686 let mut temp = LayoutNodeContext::new(frame.scope.density());
2687 let result = layout_node.measure(&mut temp, &wrapped, constraints);
2688 if let Ok(mut context) = frame.context.try_borrow_mut() {
2689 for kind in temp.take_invalidations() {
2690 context.invalidate(kind);
2691 }
2692 }
2693 result
2694 }
2695 };
2696
2697 node.measured_size.set(result.size);
2698 let local_offset = Point {
2699 x: result.placement_offset_x,
2700 y: result.placement_offset_y,
2701 };
2702 let child_accumulated = self.total_content_offset_from(index + 1);
2703 let accumulated = Point {
2704 x: local_offset.x + child_accumulated.x,
2705 y: local_offset.y + child_accumulated.y,
2706 };
2707 node.accumulated_offset.set(accumulated);
2708
2709 Placeable::value_with_offset(
2710 result.size.width,
2711 result.size.height,
2712 NodeId::default(),
2713 (accumulated.x, accumulated.y),
2714 )
2715 }
2716
2717 fn min_intrinsic_width_from(
2718 &self,
2719 index: usize,
2720 frame: &CoordinatorFrame<'_>,
2721 height: f32,
2722 ) -> f32 {
2723 let Some(node) = self.nodes.get(index) else {
2724 return frame
2725 .measure_policy
2726 .min_intrinsic_width(frame.measurables, height);
2727 };
2728 let wrapped = CoordinatorLink {
2729 chain: self,
2730 frame,
2731 index: index + 1,
2732 };
2733 let node_borrow = node.node.borrow();
2734 node_borrow.as_layout_node().map_or_else(
2735 || wrapped.min_intrinsic_width(height),
2736 |layout_node| layout_node.min_intrinsic_width(&wrapped, height, frame.scope.density()),
2737 )
2738 }
2739
2740 fn max_intrinsic_width_from(
2741 &self,
2742 index: usize,
2743 frame: &CoordinatorFrame<'_>,
2744 height: f32,
2745 ) -> f32 {
2746 let Some(node) = self.nodes.get(index) else {
2747 return frame
2748 .measure_policy
2749 .max_intrinsic_width(frame.measurables, height);
2750 };
2751 let wrapped = CoordinatorLink {
2752 chain: self,
2753 frame,
2754 index: index + 1,
2755 };
2756 let node_borrow = node.node.borrow();
2757 node_borrow.as_layout_node().map_or_else(
2758 || wrapped.max_intrinsic_width(height),
2759 |layout_node| layout_node.max_intrinsic_width(&wrapped, height, frame.scope.density()),
2760 )
2761 }
2762
2763 fn min_intrinsic_height_from(
2764 &self,
2765 index: usize,
2766 frame: &CoordinatorFrame<'_>,
2767 width: f32,
2768 ) -> f32 {
2769 let Some(node) = self.nodes.get(index) else {
2770 return frame
2771 .measure_policy
2772 .min_intrinsic_height(frame.measurables, width);
2773 };
2774 let wrapped = CoordinatorLink {
2775 chain: self,
2776 frame,
2777 index: index + 1,
2778 };
2779 let node_borrow = node.node.borrow();
2780 node_borrow.as_layout_node().map_or_else(
2781 || wrapped.min_intrinsic_height(width),
2782 |layout_node| layout_node.min_intrinsic_height(&wrapped, width, frame.scope.density()),
2783 )
2784 }
2785
2786 fn max_intrinsic_height_from(
2787 &self,
2788 index: usize,
2789 frame: &CoordinatorFrame<'_>,
2790 width: f32,
2791 ) -> f32 {
2792 let Some(node) = self.nodes.get(index) else {
2793 return frame
2794 .measure_policy
2795 .max_intrinsic_height(frame.measurables, width);
2796 };
2797 let wrapped = CoordinatorLink {
2798 chain: self,
2799 frame,
2800 index: index + 1,
2801 };
2802 let node_borrow = node.node.borrow();
2803 node_borrow.as_layout_node().map_or_else(
2804 || wrapped.max_intrinsic_height(width),
2805 |layout_node| layout_node.max_intrinsic_height(&wrapped, width, frame.scope.density()),
2806 )
2807 }
2808
2809 fn write_geometry(&self, geometry: &crate::modifier::CoordinatorGeometry, node_offset: Point) {
2812 let content = self.total_content_offset_from(0);
2813 let placed = |inner_offset: Point, size: Size| GeometryRect {
2814 x: content.x - inner_offset.x - node_offset.x,
2815 y: content.y - inner_offset.y - node_offset.y,
2816 width: size.width,
2817 height: size.height,
2818 };
2819 geometry.replace(
2820 self.nodes
2821 .iter()
2822 .map(|node| placed(node.accumulated_offset.get(), node.measured_size.get()))
2823 .chain(std::iter::once(placed(
2824 Point::default(),
2825 self.inner_size.get(),
2826 ))),
2827 );
2828 }
2829
2830 fn total_content_offset_from(&self, index: usize) -> Point {
2831 self.nodes
2832 .get(index)
2833 .map(|node| node.accumulated_offset.get())
2834 .unwrap_or_default()
2835 }
2836
2837 #[cfg(test)]
2838 fn debug_ptrs(&self) -> Vec<usize> {
2839 self.nodes.iter().map(CoordinatorNode::ptr).collect()
2840 }
2841}
2842
2843#[derive(Default)]
2844pub(crate) struct LayoutRuntimeState {
2845 child_ids: Vec<NodeId>,
2846 child_states: Vec<Rc<LayoutChildMeasureState>>,
2847 child_measurables: Vec<Box<dyn Measurable>>,
2848 coordinator_chain: CoordinatorChain,
2849}
2850
2851impl LayoutRuntimeState {
2852 fn reconcile_child_measurables(&mut self, child_ids: &[NodeId]) {
2853 if self.child_ids == child_ids {
2854 return;
2855 }
2856
2857 let mut previous_ids = std::mem::take(&mut self.child_ids);
2858 let mut previous_states = std::mem::take(&mut self.child_states);
2859 let mut previous_measurables = std::mem::take(&mut self.child_measurables);
2860
2861 self.child_ids.reserve(child_ids.len());
2862 self.child_states.reserve(child_ids.len());
2863 self.child_measurables.reserve(child_ids.len());
2864
2865 for &child_id in child_ids {
2866 if let Some(position) = previous_ids.iter().position(|&id| id == child_id) {
2867 self.child_ids.push(previous_ids.swap_remove(position));
2868 self.child_states
2869 .push(previous_states.swap_remove(position));
2870 self.child_measurables
2871 .push(previous_measurables.swap_remove(position));
2872 } else {
2873 let state = LayoutChildMeasureState::new(child_id);
2874 self.child_ids.push(child_id);
2875 self.child_states.push(Rc::clone(&state));
2876 self.child_measurables
2877 .push(Box::new(LayoutChildMeasurable::new(state)));
2878 }
2879 }
2880 }
2881
2882 fn child_state(&self, index: usize) -> Rc<LayoutChildMeasureState> {
2883 Rc::clone(&self.child_states[index])
2884 }
2885
2886 fn child_measurables(&self) -> &[Box<dyn Measurable>] {
2887 self.child_measurables.as_slice()
2888 }
2889
2890 fn reconcile_coordinator_chain(&mut self, layout_node_data: &[LayoutModifierNodeData]) {
2891 self.coordinator_chain.reconcile(layout_node_data);
2892 }
2893
2894 fn coordinator_chain(&self) -> &CoordinatorChain {
2895 &self.coordinator_chain
2896 }
2897
2898 fn clear_frame_bindings(&self) {
2899 for child_state in &self.child_states {
2900 child_state.clear_frame_bindings();
2901 }
2902 }
2903
2904 #[cfg(test)]
2905 pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
2906 LayoutRuntimeDebugStats {
2907 child_ids: self.child_ids.clone(),
2908 child_state_ptrs: self
2909 .child_states
2910 .iter()
2911 .map(|state| Rc::as_ptr(state) as *const () as usize)
2912 .collect(),
2913 child_measurable_ptrs: self
2914 .child_measurables
2915 .iter()
2916 .map(|measurable| {
2917 measurable.as_ref() as *const dyn Measurable as *const () as usize
2918 })
2919 .collect(),
2920 child_measurable_count: self.child_measurables.len(),
2921 coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
2922 coordinator_node_count: self.coordinator_chain.nodes.len(),
2923 }
2924 }
2925}
2926
2927#[cfg(test)]
2928#[derive(Debug, Clone, PartialEq, Eq)]
2929pub(crate) struct LayoutRuntimeDebugStats {
2930 pub(crate) child_ids: Vec<NodeId>,
2931 pub(crate) child_state_ptrs: Vec<usize>,
2932 pub(crate) child_measurable_ptrs: Vec<usize>,
2933 pub(crate) child_measurable_count: usize,
2934 pub(crate) coordinator_node_ptrs: Vec<usize>,
2935 pub(crate) coordinator_node_count: usize,
2936}
2937
2938struct LayoutChildMeasureConfig {
2939 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
2940 node_id: NodeId,
2941 error: Rc<RefCell<Option<NodeError>>>,
2942 runtime_handle: Option<RuntimeHandle>,
2943 cache: LayoutNodeCacheHandles,
2944 cache_epoch: u64,
2945 force_remeasure: bool,
2946 measure_handle: Option<LayoutMeasureHandle>,
2947 layout_state: Option<Rc<RefCell<LayoutState>>>,
2948}
2949
2950struct LayoutChildMeasureState {
2951 applier: RefCell<Option<Rc<ConcreteApplierHost<MemoryApplier>>>>,
2952 node_id: Cell<NodeId>,
2953 measured: RefCell<Option<Rc<MeasuredNode>>>,
2954 last_position: Cell<Option<Point>>,
2955 error: RefCell<Option<Rc<RefCell<Option<NodeError>>>>>,
2956 runtime_handle: RefCell<Option<RuntimeHandle>>,
2957 cache: RefCell<LayoutNodeCacheHandles>,
2958 cache_epoch: Cell<u64>,
2959 force_remeasure: Cell<bool>,
2960 measure_handle: RefCell<Option<LayoutMeasureHandle>>,
2961 layout_state: RefCell<Option<Rc<RefCell<LayoutState>>>>,
2962}
2963
2964impl LayoutChildMeasureState {
2965 fn new(node_id: NodeId) -> Rc<Self> {
2966 Rc::new(Self {
2967 applier: RefCell::new(None),
2968 node_id: Cell::new(node_id),
2969 measured: RefCell::new(None),
2970 last_position: Cell::new(None),
2971 error: RefCell::new(None),
2972 runtime_handle: RefCell::new(None),
2973 cache: RefCell::new(LayoutNodeCacheHandles::default()),
2974 cache_epoch: Cell::new(0),
2975 force_remeasure: Cell::new(true),
2976 measure_handle: RefCell::new(None),
2977 layout_state: RefCell::new(None),
2978 })
2979 }
2980
2981 fn configure(&self, config: LayoutChildMeasureConfig) {
2982 config.cache.activate(config.cache_epoch);
2983 *self.applier.borrow_mut() = Some(config.applier);
2984 self.node_id.set(config.node_id);
2985 self.measured.borrow_mut().take();
2986 self.last_position.set(None);
2987 *self.error.borrow_mut() = Some(config.error);
2988 *self.runtime_handle.borrow_mut() = config.runtime_handle;
2989 *self.cache.borrow_mut() = config.cache;
2990 self.cache_epoch.set(config.cache_epoch);
2991 self.force_remeasure.set(config.force_remeasure);
2992 *self.measure_handle.borrow_mut() = config.measure_handle;
2993 *self.layout_state.borrow_mut() = config.layout_state;
2994 }
2995
2996 fn clear_frame_bindings(&self) {
2997 self.measured.borrow_mut().take();
2998 *self.applier.borrow_mut() = None;
2999 *self.error.borrow_mut() = None;
3000 *self.runtime_handle.borrow_mut() = None;
3001 *self.measure_handle.borrow_mut() = None;
3002 *self.layout_state.borrow_mut() = None;
3003 }
3004
3005 fn node_id(&self) -> NodeId {
3006 self.node_id.get()
3007 }
3008
3009 fn cache(&self) -> LayoutNodeCacheHandles {
3010 self.cache.borrow().clone()
3011 }
3012
3013 fn applier(&self) -> Option<Rc<ConcreteApplierHost<MemoryApplier>>> {
3014 self.applier.borrow().clone()
3015 }
3016
3017 fn layout_state(&self) -> Option<Rc<RefCell<LayoutState>>> {
3018 self.layout_state.borrow().clone()
3019 }
3020
3021 fn take_measured(&self) -> Option<Rc<MeasuredNode>> {
3022 self.measured.borrow_mut().take()
3023 }
3024
3025 fn last_position(&self) -> Option<Point> {
3026 self.last_position.get()
3027 }
3028
3029 fn set_last_position(&self, position: Point) {
3030 self.last_position.set(Some(position));
3031 }
3032
3033 fn place_retained(&self, position: Point) {
3034 self.set_last_position(position);
3035 if let Some(layout_state) = self.layout_state() {
3036 layout_state.borrow_mut().place(position);
3037 return;
3038 }
3039 let Some(applier) = self.applier() else {
3040 return;
3041 };
3042 let Ok(mut applier) = applier.try_borrow_typed() else {
3043 return;
3044 };
3045 let node_id = self.node_id();
3046 if applier
3047 .with_node::<LayoutNode, _>(node_id, |node| {
3048 node.set_position(position);
3049 })
3050 .is_err()
3051 {
3052 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3053 node.set_position(position);
3054 });
3055 }
3056 }
3057
3058 fn set_measured(&self, measured: Option<Rc<MeasuredNode>>) {
3059 *self.measured.borrow_mut() = measured;
3060 }
3061
3062 fn record_error(&self, err: NodeError) {
3063 let Some(error) = self.error.borrow().clone() else {
3064 return;
3065 };
3066 let mut slot = error.borrow_mut();
3067 if slot.is_none() {
3068 *slot = Some(err);
3069 }
3070 }
3071
3072 fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
3073 let node_id = self.node_id();
3074 if let Some(handle) = self.measure_handle.borrow().clone() {
3075 return handle.measure(node_id, constraints);
3076 }
3077 let applier = self.applier().ok_or(NodeError::MissingContext {
3078 id: node_id,
3079 reason: "layout child applier not configured",
3080 })?;
3081 measure_node_with_host(
3082 applier,
3083 self.runtime_handle.borrow().clone(),
3084 node_id,
3085 constraints,
3086 self.cache_epoch.get(),
3087 )
3088 }
3089
3090 fn intrinsic_measure(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
3091 let cache = self.cache();
3092 cache.activate(self.cache_epoch.get());
3093 if !self.force_remeasure.get()
3094 && let Some(cached) = cache.get_measurement(constraints)
3095 {
3096 return Some(cached);
3097 }
3098
3099 match self.perform_measure(constraints) {
3100 Ok(measured) => {
3101 self.force_remeasure.set(false);
3102 cache.store_measurement(constraints, Rc::clone(&measured));
3103 Some(measured)
3104 }
3105 Err(err) => {
3106 self.record_error(err);
3107 None
3108 }
3109 }
3110 }
3111}
3112
3113struct LayoutChildMeasurable {
3114 state: Rc<LayoutChildMeasureState>,
3115}
3116
3117impl LayoutChildMeasurable {
3118 fn new(state: Rc<LayoutChildMeasureState>) -> Self {
3119 Self { state }
3120 }
3121
3122 fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
3123 let applier = self.state.applier()?;
3124 let node_id = self.state.node_id();
3125 let Ok(mut applier) = applier.try_borrow_typed() else {
3126 return None;
3127 };
3128
3129 applier
3130 .with_node::<LayoutNode, _>(node_id, |layout_node| {
3131 let props = layout_node.resolved_modifiers().layout_properties();
3132 let weight = props.weight().unwrap_or_default();
3133 cranpose_ui_layout::ParentData {
3134 weight: weight.weight,
3135 fill: weight.fill,
3136 box_alignment: props.box_alignment(),
3137 row_alignment: props.row_alignment(),
3138 column_alignment: props.column_alignment(),
3139 }
3140 })
3141 .ok()
3142 }
3143}
3144
3145impl Measurable for LayoutChildMeasurable {
3146 fn measure(&self, constraints: Constraints) -> Placeable {
3147 let state = &self.state;
3148 let cache = state.cache();
3149 cache.activate(state.cache_epoch.get());
3150 let measured_size;
3151 if !state.force_remeasure.get() {
3152 if let Some(cached) = cache.get_measurement(constraints) {
3153 measured_size = cached.size;
3154 state.set_measured(Some(Rc::clone(&cached)));
3155 } else {
3156 match state.perform_measure(constraints) {
3157 Ok(measured) => {
3158 state.force_remeasure.set(false);
3159 measured_size = measured.size;
3160 cache.store_measurement(constraints, Rc::clone(&measured));
3161 state.set_measured(Some(measured));
3162 }
3163 Err(err) => {
3164 state.record_error(err);
3165 state.set_measured(None);
3166 measured_size = Size {
3167 width: 0.0,
3168 height: 0.0,
3169 };
3170 }
3171 }
3172 }
3173 } else {
3174 match state.perform_measure(constraints) {
3175 Ok(measured) => {
3176 state.force_remeasure.set(false);
3177 measured_size = measured.size;
3178 cache.store_measurement(constraints, Rc::clone(&measured));
3179 state.set_measured(Some(measured));
3180 }
3181 Err(err) => {
3182 state.record_error(err);
3183 state.set_measured(None);
3184 measured_size = Size {
3185 width: 0.0,
3186 height: 0.0,
3187 };
3188 }
3189 }
3190 }
3191
3192 if let Some(layout_state) = state.layout_state() {
3193 let mut layout_state = layout_state.borrow_mut();
3194 layout_state.set_size(measured_size);
3195 } else if let Some(applier) = state.applier() {
3196 let Ok(mut applier) = applier.try_borrow_typed() else {
3197 return Placeable::value(
3198 measured_size.width,
3199 measured_size.height,
3200 state.node_id(),
3201 );
3202 };
3203 let _ = applier.with_node::<LayoutNode, _>(state.node_id(), |node| {
3204 node.set_measured_size(measured_size);
3205 });
3206 }
3207
3208 let state = Rc::clone(&self.state);
3209 let node_id = state.node_id();
3210 let size_for_parent = state
3211 .measured
3212 .borrow()
3213 .as_ref()
3214 .map_or(measured_size, |measured| measured.size_for_parent());
3215
3216 let place_fn = Rc::new(move |x: f32, y: f32| {
3217 let internal_offset = state
3218 .measured
3219 .borrow()
3220 .as_ref()
3221 .map(|m| m.offset)
3222 .unwrap_or_default();
3223
3224 state.place_retained(Point {
3225 x: x + internal_offset.x,
3226 y: y + internal_offset.y,
3227 });
3228 });
3229
3230 Placeable::with_place_fn(
3231 size_for_parent.width,
3232 size_for_parent.height,
3233 node_id,
3234 place_fn,
3235 )
3236 }
3237
3238 fn min_intrinsic_width(&self, height: f32) -> f32 {
3239 let kind = IntrinsicKind::MinWidth(height);
3240 let cache = self.state.cache();
3241 cache.activate(self.state.cache_epoch.get());
3242 if !self.state.force_remeasure.get()
3243 && let Some(value) = cache.get_intrinsic(&kind)
3244 {
3245 return value;
3246 }
3247 let constraints = Constraints {
3248 min_width: 0.0,
3249 max_width: f32::INFINITY,
3250 min_height: height,
3251 max_height: height,
3252 };
3253 if let Some(node) = self.state.intrinsic_measure(constraints) {
3254 let value = node.size_for_parent().width;
3255 cache.store_intrinsic(kind, value);
3256 value
3257 } else {
3258 0.0
3259 }
3260 }
3261
3262 fn max_intrinsic_width(&self, height: f32) -> f32 {
3263 let kind = IntrinsicKind::MaxWidth(height);
3264 let cache = self.state.cache();
3265 cache.activate(self.state.cache_epoch.get());
3266 if !self.state.force_remeasure.get()
3267 && let Some(value) = cache.get_intrinsic(&kind)
3268 {
3269 return value;
3270 }
3271 let constraints = Constraints {
3272 min_width: 0.0,
3273 max_width: f32::INFINITY,
3274 min_height: 0.0,
3275 max_height: height,
3276 };
3277 if let Some(node) = self.state.intrinsic_measure(constraints) {
3278 let value = node.size_for_parent().width;
3279 cache.store_intrinsic(kind, value);
3280 value
3281 } else {
3282 0.0
3283 }
3284 }
3285
3286 fn min_intrinsic_height(&self, width: f32) -> f32 {
3287 let kind = IntrinsicKind::MinHeight(width);
3288 let cache = self.state.cache();
3289 cache.activate(self.state.cache_epoch.get());
3290 if !self.state.force_remeasure.get()
3291 && let Some(value) = cache.get_intrinsic(&kind)
3292 {
3293 return value;
3294 }
3295 let constraints = Constraints {
3296 min_width: width,
3297 max_width: width,
3298 min_height: 0.0,
3299 max_height: f32::INFINITY,
3300 };
3301 if let Some(node) = self.state.intrinsic_measure(constraints) {
3302 let value = node.size_for_parent().height;
3303 cache.store_intrinsic(kind, value);
3304 value
3305 } else {
3306 0.0
3307 }
3308 }
3309
3310 fn max_intrinsic_height(&self, width: f32) -> f32 {
3311 let kind = IntrinsicKind::MaxHeight(width);
3312 let cache = self.state.cache();
3313 cache.activate(self.state.cache_epoch.get());
3314 if !self.state.force_remeasure.get()
3315 && let Some(value) = cache.get_intrinsic(&kind)
3316 {
3317 return value;
3318 }
3319 let constraints = Constraints {
3320 min_width: 0.0,
3321 max_width: width,
3322 min_height: 0.0,
3323 max_height: f32::INFINITY,
3324 };
3325 if let Some(node) = self.state.intrinsic_measure(constraints) {
3326 let value = node.size_for_parent().height;
3327 cache.store_intrinsic(kind, value);
3328 value
3329 } else {
3330 0.0
3331 }
3332 }
3333
3334 fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3335 let parent_data = self.resolved_parent_data()?;
3336 if !parent_data.has_weight() {
3337 return None;
3338 }
3339 Some(cranpose_ui_layout::FlexParentData::new(
3340 parent_data.weight,
3341 parent_data.fill,
3342 ))
3343 }
3344
3345 fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3346 self.resolved_parent_data().unwrap_or_default()
3347 }
3348}
3349
3350fn measure_node_with_host(
3351 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
3352 runtime_handle: Option<RuntimeHandle>,
3353 node_id: NodeId,
3354 constraints: Constraints,
3355 epoch: u64,
3356) -> Result<Rc<MeasuredNode>, NodeError> {
3357 let runtime_handle = match runtime_handle {
3358 Some(handle) => Some(handle),
3359 None => applier.borrow_typed().runtime_handle(),
3360 };
3361 let mut builder = LayoutBuilder::new_with_epoch(
3362 applier,
3363 epoch,
3364 Rc::new(RefCell::new(SlotTable::default())),
3365 FrameLayoutArena::default(),
3366 );
3367 builder.set_runtime_handle(runtime_handle);
3368 builder.measure_node(node_id, constraints)
3369}
3370
3371#[derive(Clone)]
3372struct RuntimeNodeMetadata {
3373 modifier: Modifier,
3374 resolved_modifiers: ResolvedModifiers,
3375 modifier_slices: Rc<ModifierNodeSlices>,
3376 semantics: Option<Rc<SemanticsConfiguration>>,
3377 role: SemanticsRole,
3378 button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3379}
3380
3381impl Default for RuntimeNodeMetadata {
3382 fn default() -> Self {
3383 Self {
3384 modifier: Modifier::empty(),
3385 resolved_modifiers: ResolvedModifiers::default(),
3386 modifier_slices: Rc::default(),
3387 semantics: None,
3388 role: SemanticsRole::Unknown,
3389 button_handler: None,
3390 }
3391 }
3392}
3393
3394fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3395 modifier_slices
3396 .annotated_text()
3397 .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3398 value: SemanticsText(Rc::clone(text)),
3399 })
3400}
3401
3402fn runtime_metadata_for(
3403 applier: &mut MemoryApplier,
3404 node_id: NodeId,
3405) -> Result<RuntimeNodeMetadata, NodeError> {
3406 if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3407 let modifier = layout.modifier.clone();
3408 let resolved_modifiers = layout.resolved_modifiers();
3409 let modifier_slices = layout.modifier_slices_snapshot();
3410 let role = role_from_modifier_slices(&modifier_slices);
3411
3412 RuntimeNodeMetadata {
3413 modifier,
3414 resolved_modifiers,
3415 modifier_slices,
3416 semantics: layout.semantics_configuration().map(Rc::new),
3417 role,
3418 button_handler: None,
3419 }
3420 }) {
3421 return Ok(meta);
3422 }
3423
3424 if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
3425 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3426 (
3427 node.modifier(),
3428 node.resolved_modifiers(),
3429 node.modifier_slices_snapshot(),
3430 node.semantics_configuration().map(Rc::new),
3431 )
3432 })
3433 {
3434 return Ok(RuntimeNodeMetadata {
3435 modifier,
3436 resolved_modifiers,
3437 modifier_slices,
3438 semantics,
3439 role: SemanticsRole::Subcompose,
3440 button_handler: None,
3441 });
3442 }
3443 Ok(RuntimeNodeMetadata::default())
3444}
3445
3446fn clear_semantics_dirty_flags(
3447 applier: &mut MemoryApplier,
3448 node: &MeasuredNode,
3449) -> Result<(), NodeError> {
3450 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3451 layout.clear_needs_semantics();
3452 }) {
3453 Ok(()) => {}
3454 Err(NodeError::Missing { .. }) => {}
3455 Err(NodeError::TypeMismatch { .. }) => {
3456 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3457 subcompose.clear_needs_semantics();
3458 }) {
3459 Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
3460 Err(err) => return Err(err),
3461 }
3462 }
3463 Err(err) => return Err(err),
3464 }
3465
3466 for child in &node.children {
3467 clear_semantics_dirty_flags(applier, &child.node)?;
3468 }
3469
3470 Ok(())
3471}
3472
3473fn build_semantics_tree_from_live_nodes(
3474 applier: &mut MemoryApplier,
3475 node: &MeasuredNode,
3476) -> Result<SemanticsTree, NodeError> {
3477 Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3478 applier, node, None,
3479 )?))
3480}
3481
3482fn semantics_node_from_parts(
3483 node_id: NodeId,
3484 node_generation: u32,
3485 mut role: SemanticsRole,
3486 config: Option<SemanticsConfiguration>,
3487 children: Vec<SemanticsNode>,
3488 bounds: GeometryRect,
3489) -> SemanticsNode {
3490 let mut node = SemanticsNode {
3491 node_id,
3492 bounds,
3493 node_generation,
3494 children,
3495 ..SemanticsNode::default()
3496 };
3497
3498 if let Some(config) = config {
3499 if config.role == Some(SemanticsWidgetRole::Button) {
3500 role = SemanticsRole::Button;
3501 }
3502 if config.is_activatable() {
3503 node.actions.push(SemanticsAction::Click {
3504 handler: SemanticsCallback::new(node_id),
3505 });
3506 }
3507 node.widget_role = config.role;
3508 node.description = config.content_description;
3509 node.state_description = config.state_description;
3510 node.on_click_label = config.on_click_label.or_else(|| {
3511 config
3512 .on_click
3513 .as_ref()
3514 .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
3515 });
3516 node.on_click = config.on_click;
3517 node.on_long_click = config.on_long_click;
3518 node.on_long_click_label = config.on_long_click_label;
3519 node.on_magic_tap = config.on_magic_tap;
3520 node.on_magic_tap_label = config.on_magic_tap_label;
3521 node.input_labels = config.input_labels;
3522 node.language = config.language;
3523 node.selected = config.selected;
3524 node.toggled = config.toggled;
3525 node.enabled = config.enabled;
3526 node.custom_actions = config.custom_actions;
3527 node.canvas_children = config.canvas_children;
3528 node.editable_text = config.is_editable_text;
3529 node.multiline = config.multiline;
3530 node.hidden = config.hidden;
3531 node.is_modal = config.is_modal
3532 && modal_takes_space(Size {
3533 width: bounds.width,
3534 height: bounds.height,
3535 });
3536 node.merge_descendants = config.merge_descendants;
3537 node.selectable_group = config.selectable_group;
3538 node.pane_title = config.pane_title;
3539 node.error = config.error;
3540 node.password = config.password;
3541 node.traversal_index = config.traversal_index;
3542 node.text = config.text;
3543 node.text_selection = config.text_selection;
3544 node.live_region = config.live_region;
3545 node.progress = config.progress;
3546 node.set_progress = config.set_progress;
3547 node.set_text = config.set_text;
3548 node.set_selection = config.set_selection;
3549 node.expand = config.expand;
3550 node.collapse = config.collapse;
3551 node.dismiss = config.dismiss;
3552 node.vertical_scroll = config.vertical_scroll;
3553 node.horizontal_scroll = config.horizontal_scroll;
3554 node.scroll_by = config.scroll_by;
3555 node.scroll_to_index = config.scroll_to_index;
3556 node.collection = config.collection;
3557 }
3558
3559 node.focusable = crate::focus_dispatch::has_focus_target(node_id);
3560 node.focused = node.focusable && crate::focus_dispatch::active_focus_target() == Some(node_id);
3561
3562 node.role = role;
3563 node
3564}
3565
3566fn build_semantics_node_from_live_nodes(
3567 applier: &mut MemoryApplier,
3568 node: &MeasuredNode,
3569 origin: Option<Point>,
3570) -> Result<SemanticsNode, NodeError> {
3571 let (role, config, (bounds, content)) =
3572 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3573 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
3574 let config = layout.semantics_configuration();
3575 layout.clear_needs_semantics();
3576 (
3577 role,
3578 config,
3579 semantics_placement(&layout.layout_state(), origin),
3580 )
3581 }) {
3582 Ok(data) => data,
3583 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3584 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3585 subcompose.clear_needs_semantics();
3586 (
3587 SemanticsRole::Subcompose,
3588 subcompose.semantics_configuration(),
3589 semantics_placement(&subcompose.layout_state(), origin),
3590 )
3591 }) {
3592 Ok(data) => data,
3593 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3594 let top_left = origin.unwrap_or_default();
3595 (
3596 SemanticsRole::Unknown,
3597 None,
3598 (
3599 GeometryRect::from_origin_size(top_left, node.size),
3600 top_left,
3601 ),
3602 )
3603 }
3604 Err(err) => return Err(err),
3605 }
3606 }
3607 Err(err) => return Err(err),
3608 };
3609
3610 let mut children = Vec::with_capacity(node.children.len());
3611 for child in &node.children {
3612 children.push(build_semantics_node_from_live_nodes(
3613 applier,
3614 &child.node,
3615 Some(content),
3616 )?);
3617 }
3618
3619 Ok(semantics_node_from_parts(
3620 node.node_id,
3621 applier.node_generation(node.node_id),
3622 role,
3623 config,
3624 children,
3625 bounds,
3626 ))
3627}
3628
3629fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
3630 stats.semantics_node_count += 1;
3631 stats.semantics_action_count += node.actions.len();
3632 stats.semantics_action_capacity += node.actions.capacity();
3633 stats.semantics_child_count += node.children.len();
3634 stats.semantics_child_capacity += node.children.capacity();
3635 stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
3636 stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
3637
3638 if let Some(description) = &node.description {
3639 stats.semantics_description_count += 1;
3640 stats.semantics_description_bytes += description.capacity();
3641 stats.semantics_heap_bytes += description.capacity();
3642 }
3643
3644 for child in &node.children {
3645 record_semantics_allocation_stats(child, stats);
3646 }
3647}
3648
3649fn record_layout_box_allocation_stats(
3650 layout_box: &LayoutBox,
3651 stats: &mut LayoutAllocationDebugStats,
3652) {
3653 stats.layout_box_count += 1;
3654 stats.layout_box_child_count += layout_box.children.len();
3655 stats.layout_box_child_capacity += layout_box.children.capacity();
3656 stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
3657 stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
3658
3659 for child in &layout_box.children {
3660 record_layout_box_allocation_stats(child, stats);
3661 }
3662}
3663
3664fn build_layout_tree(
3665 applier: &mut MemoryApplier,
3666 node: &MeasuredNode,
3667) -> Result<LayoutTree, NodeError> {
3668 fn place(
3669 applier: &mut MemoryApplier,
3670 node: &MeasuredNode,
3671 origin: Point,
3672 parent_layer_translation: Point,
3673 ) -> Result<LayoutBox, NodeError> {
3674 let top_left = Point {
3675 x: origin.x + node.offset.x,
3676 y: origin.y + node.offset.y,
3677 };
3678 let rect = GeometryRect {
3679 x: top_left.x,
3680 y: top_left.y,
3681 width: node.size.width,
3682 height: node.size.height,
3683 };
3684 let (data, layer_translation) = snapshot_node_data(
3685 applier,
3686 node.node_id,
3687 top_left,
3688 node.size,
3689 parent_layer_translation,
3690 )?;
3691 let mut children = Vec::with_capacity(node.children.len());
3692 for child in &node.children {
3693 if crate::modifier::is_window_root(applier, child.node.node_id) {
3694 continue;
3695 }
3696 let child_origin = Point {
3697 x: top_left.x + child.offset.x,
3698 y: top_left.y + child.offset.y,
3699 };
3700 children.push(place(
3701 applier,
3702 &child.node,
3703 child_origin,
3704 layer_translation,
3705 )?);
3706 }
3707 Ok(LayoutBox {
3708 node_generation: applier.node_generation(node.node_id),
3709 ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
3710 })
3711 }
3712
3713 Ok(LayoutTree::new(place(
3714 applier,
3715 node,
3716 Point { x: 0.0, y: 0.0 },
3717 Point { x: 0.0, y: 0.0 },
3718 )?))
3719}
3720
3721fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
3722 match &layout_box.node_data.kind {
3723 LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
3724 LayoutNodeKind::Spacer => SemanticsRole::Spacer,
3725 LayoutNodeKind::Unknown => SemanticsRole::Unknown,
3726 LayoutNodeKind::Button { .. } => SemanticsRole::Button,
3727 LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
3728 }
3729}
3730
3731fn build_semantics_node_from_layout_box(layout_box: &LayoutBox) -> SemanticsNode {
3732 let children = layout_box
3733 .children
3734 .iter()
3735 .map(build_semantics_node_from_layout_box)
3736 .collect();
3737
3738 semantics_node_from_parts(
3739 layout_box.node_id,
3740 layout_box.node_generation,
3741 semantics_role_from_layout_box(layout_box),
3742 layout_box.node_data.semantics().cloned(),
3743 children,
3744 layout_box.rect,
3745 )
3746}
3747
3748fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
3749 match &info.role {
3750 SemanticsRole::Layout => LayoutNodeKind::Layout,
3751 SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
3752 SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
3753 SemanticsRole::Spacer => LayoutNodeKind::Spacer,
3754 SemanticsRole::Button => {
3755 let handler = info
3756 .button_handler
3757 .as_ref()
3758 .cloned()
3759 .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
3760 LayoutNodeKind::Button { on_click: handler }
3761 }
3762 SemanticsRole::Unknown => LayoutNodeKind::Unknown,
3763 }
3764}
3765
3766fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
3767 let horizontal = padding.horizontal_sum();
3768 let vertical = padding.vertical_sum();
3769 let min_width = (constraints.min_width - horizontal).max(0.0);
3770 let mut max_width = constraints.max_width;
3771 if max_width.is_finite() {
3772 max_width = (max_width - horizontal).max(0.0);
3773 }
3774 let min_height = (constraints.min_height - vertical).max(0.0);
3775 let mut max_height = constraints.max_height;
3776 if max_height.is_finite() {
3777 max_height = (max_height - vertical).max(0.0);
3778 }
3779 normalize_constraints(Constraints {
3780 min_width,
3781 max_width,
3782 min_height,
3783 max_height,
3784 })
3785}
3786
3787fn resolve_dimension(
3788 base: f32,
3789 explicit: DimensionConstraint,
3790 min_override: Option<f32>,
3791 max_override: Option<f32>,
3792 min_limit: f32,
3793 max_limit: f32,
3794) -> f32 {
3795 let mut min_bound = min_limit;
3796 if let Some(min_value) = min_override {
3797 min_bound = min_bound.max(min_value);
3798 }
3799
3800 let mut max_bound = if max_limit.is_finite() {
3801 max_limit
3802 } else {
3803 max_override.unwrap_or(max_limit)
3804 };
3805 if let Some(max_value) = max_override {
3806 if max_bound.is_finite() {
3807 max_bound = max_bound.min(max_value);
3808 } else {
3809 max_bound = max_value;
3810 }
3811 }
3812 if max_bound < min_bound {
3813 max_bound = min_bound;
3814 }
3815
3816 let mut size = match explicit {
3817 DimensionConstraint::Points(points) => points,
3818 DimensionConstraint::Fraction(fraction) => {
3819 if max_limit.is_finite() {
3820 max_limit * fraction.clamp(0.0, 1.0)
3821 } else {
3822 base
3823 }
3824 }
3825 DimensionConstraint::Unspecified => base,
3826 DimensionConstraint::Intrinsic(_) => base,
3827 };
3828
3829 size = clamp_dimension(size, min_bound, max_bound);
3830 size = clamp_dimension(size, min_limit, max_limit);
3831 size.max(0.0)
3832}
3833
3834fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
3835 let mut result = value.max(min);
3836 if max.is_finite() {
3837 result = result.min(max);
3838 }
3839 result
3840}
3841
3842fn normalize_constraints(mut constraints: Constraints) -> Constraints {
3843 if constraints.max_width < constraints.min_width {
3844 constraints.max_width = constraints.min_width;
3845 }
3846 if constraints.max_height < constraints.min_height {
3847 constraints.max_height = constraints.min_height;
3848 }
3849 constraints
3850}
3851
3852#[cfg(test)]
3853#[path = "tests/layout_tests.rs"]
3854mod tests;
3855
3856#[cfg(test)]
3857#[path = "tests/coordinator_geometry_tests.rs"]
3858mod coordinator_geometry_tests;