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, PlaceTarget, 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
1128fn write_node_geometry(
1132 geometry: Option<&crate::modifier::CoordinatorGeometry>,
1133 node_id: NodeId,
1134 write: impl FnOnce(&crate::modifier::CoordinatorGeometry) -> bool,
1135) {
1136 if geometry.is_some_and(write) {
1137 crate::render_state::record_geometry_scene_node(node_id);
1138 }
1139}
1140
1141pub fn top_modal_from_applier(
1146 applier: &mut MemoryApplier,
1147 root: NodeId,
1148) -> Result<Option<NodeId>, NodeError> {
1149 enum Step {
1154 Enter { node: NodeId, child: bool },
1155 Modal(NodeId),
1156 }
1157
1158 fn push_placed(
1159 steps: &mut Vec<Step>,
1160 node: NodeId,
1161 reach: cranpose_foundation::SemanticsReach,
1162 size: Size,
1163 children: impl IntoIterator<Item = NodeId>,
1164 ) {
1165 if reach.hidden {
1166 return;
1167 }
1168 if reach.is_modal && modal_takes_space(size) {
1169 steps.push(Step::Modal(node));
1170 }
1171 steps.extend(
1172 children
1173 .into_iter()
1174 .map(|node| Step::Enter { node, child: true }),
1175 );
1176 }
1177
1178 let mut steps = vec![Step::Enter {
1179 node: root,
1180 child: false,
1181 }];
1182 while let Some(step) = steps.pop() {
1183 let (node_id, child) = match step {
1184 Step::Modal(node) => return Ok(Some(node)),
1185 Step::Enter { node, child } => (node, child),
1186 };
1187 let node = match applier.get_mut(node_id) {
1188 Ok(node) => node.as_any_mut(),
1189 Err(NodeError::Missing { .. }) => continue,
1190 Err(error) => return Err(error),
1191 };
1192 if let Some(layout) = node.downcast_mut::<LayoutNode>() {
1193 let state = layout.layout_state();
1194 if state.is_placed() && !(child && layout.is_window_root()) {
1195 let children = layout.children.iter().copied();
1196 push_placed(
1197 &mut steps,
1198 node_id,
1199 layout.semantics_reach(),
1200 state.size(),
1201 children,
1202 );
1203 }
1204 } else if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
1205 let state = subcompose.layout_state();
1206 if state.is_placed() {
1207 let reach = subcompose.semantics_reach();
1208 subcompose.with_active_children(|children| {
1209 push_placed(
1210 &mut steps,
1211 node_id,
1212 reach,
1213 state.size(),
1214 children.iter().copied(),
1215 );
1216 });
1217 }
1218 }
1219 }
1220 Ok(None)
1221}
1222
1223#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1224pub struct MeasureLayoutOptions {
1225 pub collect_semantics: bool,
1226 pub build_layout_tree: bool,
1227}
1228
1229impl Default for MeasureLayoutOptions {
1230 fn default() -> Self {
1231 Self {
1232 collect_semantics: true,
1233 build_layout_tree: true,
1234 }
1235 }
1236}
1237
1238pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1246 Ok(applier.get_mut(root)?.needs_layout())
1247}
1248
1249fn publish_window_geometry(
1250 modifier_slices: &crate::modifier::ModifierNodeSlices,
1251 top_left: Point,
1252 layer_translation: Point,
1253 size: Size,
1254) {
1255 let origin = Point {
1256 x: top_left.x + layer_translation.x,
1257 y: top_left.y + layer_translation.y,
1258 };
1259 if let Some(sink) = modifier_slices.text_window_origin() {
1260 sink.set(origin);
1261 }
1262 if let Some(sink) = modifier_slices.viewport_window_rect() {
1263 sink.set(GeometryRect {
1264 x: origin.x,
1265 y: origin.y,
1266 width: size.width,
1267 height: size.height,
1268 });
1269 }
1270 modifier_slices.publish_pointer_input_size(size);
1271}
1272
1273pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1279 Ok(applier.get_mut(root)?.needs_semantics())
1280}
1281
1282#[cfg(test)]
1283pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
1284 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1285}
1286
1287pub fn measure_layout(
1289 applier: &mut MemoryApplier,
1290 root: NodeId,
1291 max_size: Size,
1292) -> Result<LayoutMeasurements, NodeError> {
1293 measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
1294}
1295
1296pub fn measure_layout_with_options(
1297 applier: &mut MemoryApplier,
1298 root: NodeId,
1299 max_size: Size,
1300 options: MeasureLayoutOptions,
1301) -> Result<LayoutMeasurements, NodeError> {
1302 let telemetry_start = Instant::now();
1303 process_pending_layout_repasses(applier, root)?;
1304 let after_repasses = Instant::now();
1305
1306 let constraints = Constraints {
1307 min_width: 0.0,
1308 max_width: max_size.width,
1309 min_height: 0.0,
1310 max_height: max_size.height,
1311 };
1312
1313 let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
1314 .with_node::<LayoutNode, _>(root, |node| {
1315 (
1316 node.needs_measure(),
1317 node.needs_semantics(),
1318 node.cache_handles().epoch(),
1319 )
1320 }) {
1321 Ok(tuple) => tuple,
1322 Err(NodeError::TypeMismatch { .. }) => {
1323 let node = applier.get_mut(root)?;
1324 let measure_dirty = node.needs_measure();
1325 let semantics_dirty = node.needs_semantics();
1326 (measure_dirty, semantics_dirty, 0)
1327 }
1328 Err(err) => return Err(err),
1329 };
1330
1331 let epoch = if needs_remeasure {
1332 crate::render_state::next_layout_cache_epoch()
1333 } else if cached_epoch != 0 {
1334 cached_epoch
1335 } else {
1336 crate::render_state::current_layout_cache_epoch()
1337 };
1338
1339 let guard = ApplierSlotGuard::new(applier);
1340 let applier_host = guard.host();
1341 let slots_handle = guard.slots_handle();
1342 let after_guard = Instant::now();
1343
1344 let frame_arena = crate::render_state::take_layout_frame_arena();
1345 let mut builder = LayoutBuilder::new_with_epoch(
1346 Rc::clone(&applier_host),
1347 epoch,
1348 Rc::clone(&slots_handle),
1349 frame_arena,
1350 );
1351 let after_builder = Instant::now();
1352
1353 let measured = builder.measure_node(root, normalize_constraints(constraints))?;
1354 let after_measure = Instant::now();
1355
1356 if let Ok(mut applier) = applier_host.try_borrow_typed()
1357 && applier
1358 .with_node::<LayoutNode, _>(root, |node| {
1359 node.set_position(Point::default());
1360 })
1361 .is_err()
1362 {
1363 let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
1364 node.set_position(Point::default());
1365 });
1366 }
1367 let after_root_place = Instant::now();
1368
1369 let (layout_tree, semantics) = {
1370 let mut applier_ref = applier_host.borrow_typed();
1371 let layout_tree = if options.build_layout_tree {
1372 Some(build_layout_tree(&mut applier_ref, &measured)?)
1373 } else {
1374 None
1375 };
1376 let semantics = if options.collect_semantics {
1377 let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
1378 clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
1379 build_semantics_tree_from_layout_tree(layout_tree)
1380 } else {
1381 build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
1382 };
1383 Some(semantics_tree)
1384 } else {
1385 None
1386 };
1387 (layout_tree, semantics)
1388 };
1389 let after_aux = Instant::now();
1390
1391 drop(builder);
1392 let after_builder_drop = Instant::now();
1393
1394 drop(guard);
1395 let after_guard_drop = Instant::now();
1396
1397 log_layout_measure_telemetry(LayoutMeasureTelemetry {
1398 root,
1399 start: telemetry_start,
1400 after_repasses,
1401 after_guard,
1402 after_builder,
1403 after_measure,
1404 after_root_place,
1405 after_aux,
1406 after_builder_drop,
1407 after_guard_drop,
1408 });
1409
1410 Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
1411}
1412
1413fn process_pending_layout_repasses(
1414 applier: &mut MemoryApplier,
1415 root: NodeId,
1416) -> Result<(), NodeError> {
1417 for node_id in crate::render_state::take_modifier_slice_repass_nodes() {
1418 if let Ok(node) = applier.get_mut(node_id) {
1419 let any = node.as_any_mut();
1420 if let Some(layout) = any.downcast_mut::<crate::widgets::nodes::LayoutNode>() {
1421 layout.mark_modifier_slices_dirty();
1422 } else if let Some(subcompose) =
1423 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1424 {
1425 subcompose.mark_modifier_slices_dirty();
1426 }
1427 }
1428 }
1429 let measure_repass_nodes = crate::take_measure_repass_nodes();
1430 let repass_nodes = crate::take_layout_repass_nodes();
1431 if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
1432 return Ok(());
1433 }
1434 for node_id in measure_repass_nodes {
1435 cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
1436 }
1437 for node_id in repass_nodes {
1438 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1439 }
1440 applier.get_mut(root)?.mark_needs_layout();
1441 Ok(())
1442}
1443
1444struct LayoutBuilder {
1445 state: Rc<RefCell<LayoutBuilderState>>,
1446}
1447
1448impl LayoutBuilder {
1449 fn new_with_epoch(
1450 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1451 epoch: u64,
1452 slots: Rc<RefCell<SlotTable>>,
1453 frame_arena: FrameLayoutArena,
1454 ) -> Self {
1455 Self {
1456 state: Rc::new(RefCell::new(LayoutBuilderState::new_with_epoch(
1457 applier,
1458 epoch,
1459 slots,
1460 frame_arena,
1461 ))),
1462 }
1463 }
1464
1465 fn measure_node(
1466 &mut self,
1467 node_id: NodeId,
1468 constraints: Constraints,
1469 ) -> Result<Rc<MeasuredNode>, NodeError> {
1470 LayoutBuilderState::measure_node(Rc::clone(&self.state), node_id, constraints)
1471 }
1472
1473 fn set_runtime_handle(&mut self, handle: Option<RuntimeHandle>) {
1474 self.state.borrow_mut().runtime_handle = handle;
1475 }
1476}
1477
1478impl Drop for LayoutBuilder {
1479 fn drop(&mut self) {
1480 if Rc::strong_count(&self.state) != 1 {
1481 return;
1482 }
1483 let Ok(mut state) = self.state.try_borrow_mut() else {
1484 return;
1485 };
1486 crate::render_state::replace_layout_frame_arena(std::mem::take(&mut state.frame_arena));
1487 }
1488}
1489
1490struct LayoutBuilderState {
1491 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1492 runtime_handle: Option<RuntimeHandle>,
1493 slots: Rc<RefCell<SlotTable>>,
1494 cache_epoch: u64,
1495 cache_floor: u64,
1496 frame_arena: FrameLayoutArena,
1497}
1498
1499struct LayoutRuntimeFrameBindingCleanup {
1500 state: Rc<RefCell<LayoutRuntimeState>>,
1501}
1502
1503impl LayoutRuntimeFrameBindingCleanup {
1504 fn new(state: Rc<RefCell<LayoutRuntimeState>>) -> Self {
1505 Self { state }
1506 }
1507}
1508
1509impl Drop for LayoutRuntimeFrameBindingCleanup {
1510 fn drop(&mut self) {
1511 self.state.borrow().clear_frame_bindings();
1512 }
1513}
1514
1515impl LayoutBuilderState {
1516 fn new_with_epoch(
1517 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1518 epoch: u64,
1519 slots: Rc<RefCell<SlotTable>>,
1520 frame_arena: FrameLayoutArena,
1521 ) -> Self {
1522 let runtime_handle = applier.borrow_typed().runtime_handle();
1523
1524 Self {
1525 applier,
1526 runtime_handle,
1527 slots,
1528 cache_epoch: epoch,
1529 cache_floor: crate::render_state::layout_cache_floor(),
1530 frame_arena,
1531 }
1532 }
1533
1534 fn try_with_applier_result<R>(
1535 state_rc: &Rc<RefCell<Self>>,
1536 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1537 ) -> Option<Result<R, NodeError>> {
1538 let host = {
1539 let state = state_rc.borrow();
1540 Rc::clone(&state.applier)
1541 };
1542
1543 let Ok(mut applier) = host.try_borrow_typed() else {
1544 return None;
1545 };
1546
1547 Some(f(&mut applier))
1548 }
1549
1550 fn with_applier_result<R>(
1551 state_rc: &Rc<RefCell<Self>>,
1552 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1553 ) -> Result<R, NodeError> {
1554 Self::try_with_applier_result(state_rc, f).unwrap_or_else(|| {
1555 Err(NodeError::MissingContext {
1556 id: NodeId::default(),
1557 reason: "applier already borrowed",
1558 })
1559 })
1560 }
1561
1562 fn clear_node_placed(state_rc: &Rc<RefCell<Self>>, node_id: NodeId) {
1563 let host = {
1564 let state = state_rc.borrow();
1565 Rc::clone(&state.applier)
1566 };
1567 let Ok(mut applier) = host.try_borrow_typed() else {
1568 return;
1569 };
1570 if applier
1571 .with_node::<LayoutNode, _>(node_id, |node| {
1572 node.clear_placed();
1573 })
1574 .is_err()
1575 {
1576 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1577 node.clear_placed();
1578 });
1579 }
1580 }
1581
1582 fn measure_node(
1583 state_rc: Rc<RefCell<Self>>,
1584 node_id: NodeId,
1585 constraints: Constraints,
1586 ) -> Result<Rc<MeasuredNode>, NodeError> {
1587 let telemetry_start = Instant::now();
1588 Self::clear_node_placed(&state_rc, node_id);
1589
1590 if let Some(subcompose) =
1591 Self::try_measure_subcompose(Rc::clone(&state_rc), node_id, constraints)?
1592 {
1593 log_node_measure_telemetry(
1594 "subcompose",
1595 node_id,
1596 constraints,
1597 subcompose.size,
1598 subcompose.children.len(),
1599 telemetry_start,
1600 );
1601 return Ok(subcompose);
1602 }
1603
1604 if let Some(result) = Self::try_with_applier_result(&state_rc, |applier| {
1605 match applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1606 LayoutNodeSnapshot::from_layout_node(layout_node)
1607 }) {
1608 Ok(snapshot) => Ok(Some(snapshot)),
1609 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1610 Err(err) => Err(err),
1611 }
1612 }) && let Some(snapshot) = result?
1613 {
1614 let measured =
1615 Self::measure_layout_node(Rc::clone(&state_rc), node_id, snapshot, constraints)?;
1616 log_node_measure_telemetry(
1617 "layout",
1618 node_id,
1619 constraints,
1620 measured.size,
1621 measured.children.len(),
1622 telemetry_start,
1623 );
1624 return Ok(measured);
1625 }
1626
1627 let measured = Rc::new(MeasuredNode::new(
1628 node_id,
1629 Size::default(),
1630 Point { x: 0.0, y: 0.0 },
1631 Point::default(),
1632 Vec::new(),
1633 ));
1634 log_node_measure_telemetry(
1635 "fallback",
1636 node_id,
1637 constraints,
1638 measured.size,
1639 measured.children.len(),
1640 telemetry_start,
1641 );
1642 Ok(measured)
1643 }
1644
1645 fn cached_measure_node_with_applier(
1646 applier: &mut MemoryApplier,
1647 node_id: NodeId,
1648 constraints: Constraints,
1649 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1650 let Some(data) = Self::layout_child_measure_data(applier, node_id)? else {
1651 return Ok(None);
1652 };
1653 if data.needs_measure
1654 || data.needs_layout
1655 || data.cache.epoch() == 0
1656 || data.cache.epoch() != crate::render_state::current_layout_cache_epoch()
1657 {
1658 return Ok(None);
1659 }
1660
1661 let Some(measured) = data.cache.get_measurement(constraints) else {
1662 return Ok(None);
1663 };
1664
1665 if let Some(layout_state) = data.layout_state {
1666 let mut layout_state = layout_state.borrow_mut();
1667 layout_state.set_size(measured.size);
1668 } else {
1669 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1670 node.set_measured_size(measured.size);
1671 });
1672 }
1673
1674 Ok(Some(measured))
1675 }
1676
1677 fn try_measure_subcompose(
1678 state_rc: Rc<RefCell<Self>>,
1679 node_id: NodeId,
1680 constraints: Constraints,
1681 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1682 let applier_host = {
1683 let state = state_rc.borrow();
1684 Rc::clone(&state.applier)
1685 };
1686
1687 let (node_handle, resolved_modifiers) = {
1688 let Ok(mut applier) = applier_host.try_borrow_typed() else {
1689 return Ok(None);
1690 };
1691 let node = match applier.get_mut(node_id) {
1692 Ok(node) => node,
1693 Err(NodeError::Missing { .. }) => return Ok(None),
1694 Err(err) => return Err(err),
1695 };
1696 let any = node.as_any_mut();
1697 if let Some(subcompose) =
1698 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1699 {
1700 let handle = subcompose.handle();
1701 let resolved_modifiers = handle
1702 .resolved_modifiers()
1703 .on_device_grid(subcompose.density().density());
1704 (handle, resolved_modifiers)
1705 } else {
1706 return Ok(None);
1707 }
1708 };
1709
1710 let runtime_handle = {
1711 let mut state = state_rc.borrow_mut();
1712 if state.runtime_handle.is_none()
1713 && let Ok(applier) = applier_host.try_borrow_typed()
1714 {
1715 state.runtime_handle = applier.runtime_handle();
1716 }
1717 state
1718 .runtime_handle
1719 .clone()
1720 .ok_or(NodeError::MissingContext {
1721 id: node_id,
1722 reason: "runtime handle required for subcomposition",
1723 })?
1724 };
1725
1726 let props = resolved_modifiers.layout_properties();
1727 let padding = resolved_modifiers.padding();
1728 let offset = resolved_modifiers.offset();
1729 let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1730
1731 if let DimensionConstraint::Points(width) = props.width() {
1732 let constrained_width = width - padding.horizontal_sum();
1733 inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1734 inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1735 }
1736 if let DimensionConstraint::Points(height) = props.height() {
1737 let constrained_height = height - padding.vertical_sum();
1738 inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1739 inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1740 }
1741
1742 let mut slots_guard = SlotsGuard::take(Rc::clone(&state_rc));
1743 let slots_host = slots_guard.host();
1744 let applier_host_dyn: Rc<dyn ApplierHost> = applier_host.clone();
1745 let observer = SnapshotStateObserver::new(|callback| callback());
1746 let composer = Composer::new(
1747 Rc::clone(&slots_host),
1748 applier_host_dyn,
1749 runtime_handle,
1750 observer,
1751 Some(node_id),
1752 );
1753 composer.enter_phase(Phase::Measure);
1754
1755 let state_rc_clone = Rc::clone(&state_rc);
1756 let measure_error = RefCell::new(None);
1757 let state_rc_for_subcompose = Rc::clone(&state_rc_clone);
1758 let error_for_subcompose = &measure_error;
1759 let measured_children = node_handle.measured_children_scratch();
1760 let measured_children_for_subcompose = Rc::clone(&measured_children);
1761 let state_rc_for_cached = Rc::clone(&state_rc_clone);
1762 let error_for_cached = &measure_error;
1763 let measured_children_for_cached = Rc::clone(&measured_children);
1764 let measured_children_for_lookup = Rc::clone(&measured_children);
1765 let measured_children_for_retained = Rc::clone(&measured_children);
1766
1767 let measure_result = node_handle.measure_with_cached_batch(
1768 &composer,
1769 node_id,
1770 inner_constraints,
1771 CachedBatchMeasureInputs {
1772 measurer: Box::new(
1773 move |child_id: NodeId, child_constraints: Constraints| -> Size {
1774 match Self::measure_node(
1775 Rc::clone(&state_rc_for_subcompose),
1776 child_id,
1777 child_constraints,
1778 ) {
1779 Ok(measured) => {
1780 measured_children_for_subcompose
1781 .borrow_mut()
1782 .insert(child_id, Rc::clone(&measured));
1783 measured.size
1784 }
1785 Err(err) => {
1786 let mut slot = error_for_subcompose.borrow_mut();
1787 if slot.is_none() {
1788 *slot = Some(err);
1789 }
1790 Size::default()
1791 }
1792 }
1793 },
1794 ),
1795 cached_measure_batch_registrar: Box::new(
1796 move |child_ids: &[NodeId],
1797 child_constraints: Constraints,
1798 out: &mut Vec<Option<Size>>| {
1799 out.clear();
1800 out.resize(child_ids.len(), None);
1801
1802 let applier_host = {
1803 let state = state_rc_for_cached.borrow();
1804 Rc::clone(&state.applier)
1805 };
1806 let Ok(mut applier) = applier_host.try_borrow_typed() else {
1807 return;
1808 };
1809
1810 let mut measured_children = measured_children_for_cached.borrow_mut();
1811 for (index, &child_id) in child_ids.iter().enumerate() {
1812 match Self::cached_measure_node_with_applier(
1813 &mut applier,
1814 child_id,
1815 child_constraints,
1816 ) {
1817 Ok(Some(measured)) => {
1818 out[index] = Some(measured.size);
1819 measured_children.insert(child_id, Rc::clone(&measured));
1820 }
1821 Ok(None) => {}
1822 Err(err) => {
1823 let mut slot = error_for_cached.borrow_mut();
1824 if slot.is_none() {
1825 *slot = Some(err);
1826 }
1827 break;
1828 }
1829 }
1830 }
1831 },
1832 ),
1833 retained_measure_lookup: Box::new(move |child_id| {
1834 measured_children_for_lookup
1835 .borrow()
1836 .get(&child_id)
1837 .cloned()
1838 }),
1839 retained_measure_registrar: Box::new(move |measurements| {
1840 let mut measured_children = measured_children_for_retained.borrow_mut();
1841 for measured in measurements {
1842 measured_children.insert(measured.node_id(), Rc::clone(measured));
1843 }
1844 }),
1845 error: &measure_error,
1846 },
1847 )?;
1848 drop(composer);
1849 slots_guard.restore(slots_host.into_table()?);
1850
1851 if let Some(err) = measure_error.borrow_mut().take() {
1852 return Err(err);
1853 }
1854
1855 let cranpose_ui_layout::MeasureResult {
1856 size: measured_size,
1857 placements,
1858 } = measure_result;
1859
1860 let mut width = measured_size.width + padding.horizontal_sum();
1861 let mut height = measured_size.height + padding.vertical_sum();
1862
1863 width = resolve_dimension(
1864 width,
1865 props.width(),
1866 props.min_width(),
1867 props.max_width(),
1868 constraints.min_width,
1869 constraints.max_width,
1870 );
1871 height = resolve_dimension(
1872 height,
1873 props.height(),
1874 props.min_height(),
1875 props.max_height(),
1876 constraints.min_height,
1877 constraints.max_height,
1878 );
1879
1880 let mut children = Vec::with_capacity(placements.len());
1881 let mut measured_children_by_id = measured_children.borrow_mut();
1882
1883 if let Ok(mut applier) = applier_host.try_borrow_typed() {
1884 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1885 parent_node.set_measured_size(Size { width, height });
1886 parent_node.clear_needs_measure();
1887 parent_node.clear_needs_layout();
1888 });
1889 }
1890
1891 for placement in &placements {
1892 let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1893 measured
1894 } else {
1895 Self::measure_node(Rc::clone(&state_rc), placement.node_id, inner_constraints)?
1896 };
1897 let policy_position = Point {
1898 x: padding.left + placement.x,
1899 y: padding.top + placement.y,
1900 };
1901 let retained_position = Point {
1902 x: policy_position.x + child.offset.x,
1903 y: policy_position.y + child.offset.y,
1904 };
1905
1906 if let Ok(mut applier) = applier_host.try_borrow_typed()
1907 && applier
1908 .with_node::<LayoutNode, _>(placement.node_id, |node| {
1909 node.set_position(retained_position);
1910 })
1911 .is_err()
1912 {
1913 let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1914 node.set_position(retained_position);
1915 });
1916 }
1917
1918 children.push(MeasuredChild {
1919 node: child,
1920 offset: policy_position,
1921 });
1922 }
1923
1924 node_handle.set_active_children(children.iter().map(|c| c.node.node_id));
1925 node_handle.recycle_placement_scratch(placements);
1926
1927 Ok(Some(Rc::new(MeasuredNode::new(
1928 node_id,
1929 Size { width, height },
1930 offset,
1931 Point::default(),
1932 children,
1933 ))))
1934 }
1935 fn measure_through_modifier_chain(
1936 state_rc: &Rc<RefCell<Self>>,
1937 node_id: NodeId,
1938 runtime_state: &mut LayoutRuntimeState,
1939 measure_policy: &Rc<dyn MeasurePolicy>,
1940 constraints: Constraints,
1941 layout_node_data: &mut Vec<LayoutModifierNodeData>,
1942 placements: &mut Vec<Placement>,
1943 ) -> ModifierChainMeasurement {
1944 use cranpose_foundation::NodeCapabilities;
1945
1946 layout_node_data.clear();
1947 let mut offset = Point::default();
1948 let mut density = crate::density::Density::default();
1949 let mut window_root = false;
1950 let mut geometry = None;
1951
1952 {
1953 let state = state_rc.borrow();
1954 let mut applier = state.applier.borrow_typed();
1955
1956 let _ = applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1957 density = layout_node.density();
1958 window_root = layout_node.is_window_root();
1959 geometry = Some(layout_node.coordinator_geometry());
1960 let chain_handle = layout_node.modifier_chain();
1961
1962 if !chain_handle.has_layout_nodes() {
1963 return;
1964 }
1965
1966 chain_handle.chain().for_each_forward_matching(
1967 NodeCapabilities::LAYOUT,
1968 |node_ref| {
1969 if let Some(index) = node_ref.entry_index() {
1970 if let Some(node_rc) = chain_handle.chain().get_node_rc(index) {
1971 layout_node_data.push((index, Rc::clone(&node_rc)));
1972 }
1973
1974 node_ref.with_node(|node| {
1975 if let Some(offset_node) =
1976 node.as_any()
1977 .downcast_ref::<crate::modifier_nodes::OffsetNode>()
1978 {
1979 let delta = offset_node.device_offset(density.density());
1980 offset.x += delta.x;
1981 offset.y += delta.y;
1982 }
1983 });
1984 }
1985 },
1986 );
1987 });
1988 }
1989
1990 let scope = crate::density::DensityMeasureScope::new(density);
1991
1992 if layout_node_data.is_empty() {
1993 let final_size = measure_policy.measure_into(
1994 &scope,
1995 runtime_state.child_measurables(),
1996 constraints,
1997 placements,
1998 );
1999 write_node_geometry(geometry.as_deref(), node_id, |geometry| {
2000 geometry.replace([GeometryRect {
2001 x: 0.0,
2002 y: 0.0,
2003 width: final_size.width,
2004 height: final_size.height,
2005 }])
2006 });
2007
2008 return ModifierChainMeasurement {
2009 size: final_size,
2010 content_offset: Point::default(),
2011 offset,
2012 window_root,
2013 };
2014 }
2015
2016 runtime_state.reconcile_coordinator_chain(layout_node_data.as_slice());
2017 let frame = CoordinatorFrame::new(
2018 measure_policy,
2019 &scope,
2020 runtime_state.child_measurables(),
2021 placements,
2022 );
2023
2024 let placeable = runtime_state
2025 .coordinator_chain()
2026 .measure_from(0, &frame, constraints);
2027 let final_size = Size {
2028 width: placeable.width(),
2029 height: placeable.height(),
2030 };
2031
2032 write_node_geometry(geometry.as_deref(), node_id, |geometry| {
2033 runtime_state
2034 .coordinator_chain()
2035 .write_geometry(geometry, offset)
2036 });
2037
2038 let content_offset = placeable.content_offset();
2039 let all_placement_offset = Point {
2040 x: content_offset.0,
2041 y: content_offset.1,
2042 };
2043
2044 let content_offset = Point {
2045 x: all_placement_offset.x - offset.x,
2046 y: all_placement_offset.y - offset.y,
2047 };
2048
2049 let invalidations = frame.take_invalidations();
2050 if !invalidations.is_empty() {
2051 Self::with_applier_result(state_rc, |applier| {
2052 applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2053 for kind in invalidations {
2054 match kind {
2055 InvalidationKind::Layout => layout_node.mark_needs_measure(),
2056 InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2057 InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2058 InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2059 InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2060 }
2061 }
2062 })
2063 })
2064 .ok();
2065 }
2066
2067 ModifierChainMeasurement {
2068 size: final_size,
2069 content_offset,
2070 offset,
2071 window_root,
2072 }
2073 }
2074
2075 fn layout_child_measure_data(
2076 applier: &mut MemoryApplier,
2077 child_id: NodeId,
2078 ) -> Result<Option<LayoutChildMeasureData>, NodeError> {
2079 match applier.with_node::<LayoutNode, _>(child_id, |n| LayoutChildMeasureData {
2080 cache: n.cache_handles(),
2081 layout_state: Some(n.layout_state_handle()),
2082 needs_layout: n.needs_layout(),
2083 needs_measure: n.needs_measure(),
2084 }) {
2085 Ok(data) => Ok(Some(data)),
2086 Err(NodeError::TypeMismatch { .. }) => {
2087 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |n| {
2088 LayoutChildMeasureData {
2089 cache: n.cache_handles(),
2090 layout_state: None,
2091 needs_layout: n.needs_layout(),
2092 needs_measure: n.needs_measure(),
2093 }
2094 }) {
2095 Ok(data) => Ok(Some(data)),
2096 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
2097 Err(err) => Err(err),
2098 }
2099 }
2100 Err(NodeError::Missing { .. }) => Ok(None),
2101 Err(err) => Err(err),
2102 }
2103 }
2104
2105 fn measure_layout_node(
2106 state_rc: Rc<RefCell<Self>>,
2107 node_id: NodeId,
2108 snapshot: LayoutNodeSnapshot,
2109 constraints: Constraints,
2110 ) -> Result<Rc<MeasuredNode>, NodeError> {
2111 let (cache_epoch, cache_floor) = {
2112 let state = state_rc.borrow();
2113 (state.cache_epoch, state.cache_floor)
2114 };
2115 let LayoutNodeSnapshot {
2116 measure_policy,
2117 cache,
2118 layout_runtime_state,
2119 needs_layout,
2120 needs_measure,
2121 } = snapshot;
2122 cache.activate(cache_epoch);
2123
2124 if !needs_measure
2125 && !needs_layout
2126 && let Some(cached) = cache.get_measurement(constraints)
2127 {
2128 Self::with_applier_result(&state_rc, |applier| {
2129 applier.with_node::<LayoutNode, _>(node_id, |node| {
2130 node.clear_needs_measure();
2131 node.clear_needs_layout();
2132 })
2133 })
2134 .ok();
2135 return Ok(cached);
2136 }
2137
2138 let (runtime_handle, applier_host) = {
2139 let state = state_rc.borrow();
2140 (state.runtime_handle.clone(), Rc::clone(&state.applier))
2141 };
2142
2143 let measure_handle = LayoutMeasureHandle::new(Rc::clone(&state_rc));
2144 let error = Rc::clone(&layout_runtime_state.borrow().error);
2145 error.borrow_mut().take();
2146 let mut pools = VecPools::acquire(Rc::clone(&state_rc));
2147 let (records, child_ids, layout_node_data, placements) = pools.parts();
2148
2149 applier_host
2150 .borrow_typed()
2151 .with_node::<LayoutNode, _>(node_id, |node| {
2152 child_ids.extend_from_slice(&node.children);
2153 })?;
2154
2155 let mut valid_child_count = 0;
2156 for index in 0..child_ids.len() {
2157 let child_id = child_ids[index];
2158 let child_exists = {
2159 let mut applier = applier_host.borrow_typed();
2160 Self::layout_child_measure_data(&mut applier, child_id)?.is_some()
2161 };
2162 if child_exists {
2163 child_ids[valid_child_count] = child_id;
2164 valid_child_count += 1;
2165 }
2166 }
2167 child_ids.truncate(valid_child_count);
2168
2169 let _frame_binding_cleanup =
2170 LayoutRuntimeFrameBindingCleanup::new(Rc::clone(&layout_runtime_state));
2171
2172 {
2173 let mut runtime_state = layout_runtime_state.borrow_mut();
2174 runtime_state.reconcile_child_measurables(child_ids.as_slice());
2175
2176 for (index, &child_id) in child_ids.iter().enumerate() {
2177 let data = {
2178 let mut applier = applier_host.borrow_typed();
2179 Self::layout_child_measure_data(&mut applier, child_id)?
2180 };
2181 let Some(data) = data else {
2182 continue;
2183 };
2184
2185 let child_is_stale = data.is_stale(cache_floor);
2186 let child_cache_epoch = if child_is_stale {
2187 cache_epoch
2188 } else {
2189 data.cache.epoch()
2190 };
2191 let child_state = runtime_state.child_state(index);
2192 child_state.configure(LayoutChildMeasureConfig {
2193 applier: Rc::clone(&applier_host),
2194 node_id: child_id,
2195 error: Rc::clone(&error),
2196 runtime_handle: runtime_handle.clone(),
2197 cache: data.cache,
2198 cache_epoch: child_cache_epoch,
2199 force_remeasure: child_is_stale,
2200 measure_handle: Some(measure_handle.clone()),
2201 layout_state: data.layout_state,
2202 });
2203 records.push((child_id, ChildRecord { state: child_state }));
2204 }
2205 }
2206
2207 let chain_constraints = constraints;
2208
2209 let modifier_chain_result = {
2210 let mut runtime_state = layout_runtime_state.borrow_mut();
2211 Self::measure_through_modifier_chain(
2212 &state_rc,
2213 node_id,
2214 &mut runtime_state,
2215 &measure_policy,
2216 chain_constraints,
2217 layout_node_data,
2218 placements,
2219 )
2220 };
2221
2222 let (width, height, content_offset, offset, window_root) = {
2223 let result = modifier_chain_result;
2224 if let Some(err) = error.borrow_mut().take() {
2225 return Err(err);
2226 }
2227
2228 (
2229 result.size.width,
2230 result.size.height,
2231 result.content_offset,
2232 result.offset,
2233 result.window_root,
2234 )
2235 };
2236
2237 let mut measured_children = Vec::with_capacity(records.len());
2238 for (child_id, record) in records.iter() {
2239 if let Some(measured) = record.state.take_measured() {
2240 let placed = placements
2241 .iter()
2242 .find(|placement| placement.node_id == *child_id)
2243 .map(|placement| Point {
2244 x: placement.x,
2245 y: placement.y,
2246 });
2247 if let Some(raw) = placed {
2248 record.state.place_retained(Point {
2249 x: raw.x + measured.offset.x,
2250 y: raw.y + measured.offset.y,
2251 });
2252 }
2253 let base_position = placed
2254 .or_else(|| record.state.last_position())
2255 .unwrap_or(Point { x: 0.0, y: 0.0 });
2256 let position = Point {
2257 x: content_offset.x + base_position.x,
2258 y: content_offset.y + base_position.y,
2259 };
2260 measured_children.push(MeasuredChild {
2261 node: measured,
2262 offset: position,
2263 });
2264 }
2265 }
2266
2267 let measured = Rc::new(
2268 MeasuredNode::new(
2269 node_id,
2270 Size { width, height },
2271 offset,
2272 content_offset,
2273 measured_children,
2274 )
2275 .with_window_root(window_root),
2276 );
2277
2278 cache.store_measurement(constraints, Rc::clone(&measured));
2279
2280 Self::with_applier_result(&state_rc, |applier| {
2281 applier.with_node::<LayoutNode, _>(node_id, |node| {
2282 node.clear_needs_measure();
2283 node.clear_needs_layout();
2284 node.set_measured_size(Size { width, height });
2285 node.set_content_offset(content_offset);
2286 })
2287 })
2288 .ok();
2289
2290 Ok(measured)
2291 }
2292}
2293
2294struct LayoutChildMeasureData {
2295 cache: LayoutNodeCacheHandles,
2296 layout_state: Option<Rc<RefCell<LayoutState>>>,
2297 needs_layout: bool,
2298 needs_measure: bool,
2299}
2300
2301impl LayoutChildMeasureData {
2302 fn is_stale(&self, cache_floor: u64) -> bool {
2303 self.needs_layout || self.needs_measure || self.cache.epoch() < cache_floor
2304 }
2305}
2306
2307struct LayoutNodeSnapshot {
2308 measure_policy: Rc<dyn MeasurePolicy>,
2309 cache: LayoutNodeCacheHandles,
2310 layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
2311 needs_layout: bool,
2312 needs_measure: bool,
2313}
2314
2315impl LayoutNodeSnapshot {
2316 fn from_layout_node(node: &LayoutNode) -> Self {
2317 Self {
2318 measure_policy: Rc::clone(&node.measure_policy),
2319 cache: node.cache_handles(),
2320 layout_runtime_state: node.layout_runtime_state_handle(),
2321 needs_layout: node.needs_layout(),
2322 needs_measure: node.needs_measure(),
2323 }
2324 }
2325}
2326
2327struct VecPools {
2328 state: Rc<RefCell<LayoutBuilderState>>,
2329 records: Vec<(NodeId, ChildRecord)>,
2330 child_ids: Vec<NodeId>,
2331 layout_node_data: Vec<LayoutModifierNodeData>,
2332 placements: Vec<Placement>,
2333}
2334
2335impl VecPools {
2336 fn acquire(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2337 let (records, child_ids, layout_node_data, placements) = {
2338 let mut state_mut = state.borrow_mut();
2339 (
2340 state_mut.frame_arena.tmp_records.acquire(),
2341 state_mut.frame_arena.tmp_child_ids.acquire(),
2342 state_mut.frame_arena.tmp_layout_node_data.acquire(),
2343 state_mut.frame_arena.tmp_placements.acquire(),
2344 )
2345 };
2346 Self {
2347 state,
2348 records,
2349 child_ids,
2350 layout_node_data,
2351 placements,
2352 }
2353 }
2354
2355 #[expect(clippy::type_complexity)]
2356 fn parts(
2357 &mut self,
2358 ) -> (
2359 &mut Vec<(NodeId, ChildRecord)>,
2360 &mut Vec<NodeId>,
2361 &mut Vec<LayoutModifierNodeData>,
2362 &mut Vec<Placement>,
2363 ) {
2364 (
2365 &mut self.records,
2366 &mut self.child_ids,
2367 &mut self.layout_node_data,
2368 &mut self.placements,
2369 )
2370 }
2371}
2372
2373impl Drop for VecPools {
2374 fn drop(&mut self) {
2375 let mut state = self.state.borrow_mut();
2376 state
2377 .frame_arena
2378 .tmp_records
2379 .release(std::mem::take(&mut self.records));
2380 state
2381 .frame_arena
2382 .tmp_child_ids
2383 .release(std::mem::take(&mut self.child_ids));
2384 state
2385 .frame_arena
2386 .tmp_layout_node_data
2387 .release(std::mem::take(&mut self.layout_node_data));
2388 state
2389 .frame_arena
2390 .tmp_placements
2391 .release(std::mem::take(&mut self.placements));
2392 }
2393}
2394
2395struct SlotsGuard {
2396 state: Rc<RefCell<LayoutBuilderState>>,
2397 slots: Option<SlotTable>,
2398}
2399
2400impl SlotsGuard {
2401 fn take(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2402 let slots = {
2403 let state_ref = state.borrow();
2404 let mut slots_ref = state_ref.slots.borrow_mut();
2405 std::mem::take(&mut *slots_ref)
2406 };
2407 Self {
2408 state,
2409 slots: Some(slots),
2410 }
2411 }
2412
2413 fn host(&mut self) -> Rc<SlotsHost> {
2414 let slots = self.slots.take().unwrap_or_default();
2415 Rc::new(SlotsHost::new(slots))
2416 }
2417
2418 fn restore(&mut self, slots: SlotTable) {
2419 debug_assert!(self.slots.is_none());
2420 self.slots = Some(slots);
2421 }
2422}
2423
2424impl Drop for SlotsGuard {
2425 fn drop(&mut self) {
2426 if let Some(slots) = self.slots.take() {
2427 let state_ref = self.state.borrow();
2428 *state_ref.slots.borrow_mut() = slots;
2429 }
2430 }
2431}
2432
2433#[derive(Clone)]
2434struct LayoutMeasureHandle {
2435 state: Rc<RefCell<LayoutBuilderState>>,
2436}
2437
2438impl LayoutMeasureHandle {
2439 fn new(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2440 Self { state }
2441 }
2442
2443 fn measure(
2444 &self,
2445 node_id: NodeId,
2446 constraints: Constraints,
2447 ) -> Result<Rc<MeasuredNode>, NodeError> {
2448 LayoutBuilderState::measure_node(Rc::clone(&self.state), node_id, constraints)
2449 }
2450}
2451
2452#[derive(Debug, Clone)]
2453pub(crate) struct MeasuredNode {
2454 node_id: NodeId,
2455 size: Size,
2456 offset: Point,
2457 content_offset: Point,
2458 children: Vec<MeasuredChild>,
2459 window_root: bool,
2460}
2461
2462impl MeasuredNode {
2463 fn new(
2464 node_id: NodeId,
2465 size: Size,
2466 offset: Point,
2467 content_offset: Point,
2468 children: Vec<MeasuredChild>,
2469 ) -> Self {
2470 Self {
2471 node_id,
2472 size,
2473 offset,
2474 content_offset,
2475 children,
2476 window_root: false,
2477 }
2478 }
2479
2480 fn with_window_root(mut self, window_root: bool) -> Self {
2481 self.window_root = window_root;
2482 self
2483 }
2484
2485 pub(crate) fn size_for_parent(&self) -> Size {
2486 if self.window_root {
2487 Size::new(0.0, 0.0)
2488 } else {
2489 self.size
2490 }
2491 }
2492
2493 #[cfg(test)]
2494 pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2495 Self::new(
2496 node_id,
2497 size,
2498 Point::default(),
2499 Point::default(),
2500 Vec::new(),
2501 )
2502 }
2503
2504 pub(crate) fn node_id(&self) -> NodeId {
2505 self.node_id
2506 }
2507
2508 pub(crate) fn size(&self) -> Size {
2509 self.size
2510 }
2511}
2512
2513#[derive(Debug, Clone)]
2514struct MeasuredChild {
2515 node: Rc<MeasuredNode>,
2516 offset: Point,
2517}
2518
2519struct ChildRecord {
2520 state: Rc<LayoutChildMeasureState>,
2521}
2522
2523struct CoordinatorFrame<'a> {
2524 measure_policy: &'a Rc<dyn MeasurePolicy>,
2525 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2526 measurables: &'a [Box<dyn Measurable>],
2527 placements: RefCell<&'a mut Vec<Placement>>,
2528 context: RefCell<LayoutNodeContext>,
2529}
2530
2531impl<'a> CoordinatorFrame<'a> {
2532 fn new(
2533 measure_policy: &'a Rc<dyn MeasurePolicy>,
2534 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2535 measurables: &'a [Box<dyn Measurable>],
2536 placements: &'a mut Vec<Placement>,
2537 ) -> Self {
2538 Self {
2539 measure_policy,
2540 scope,
2541 measurables,
2542 placements: RefCell::new(placements),
2543 context: RefCell::new(LayoutNodeContext::new(scope.density())),
2544 }
2545 }
2546
2547 fn take_invalidations(&self) -> Vec<InvalidationKind> {
2548 self.context.borrow_mut().take_invalidations()
2549 }
2550}
2551
2552struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2553 chain: &'chain CoordinatorChain,
2554 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2555 index: usize,
2556}
2557
2558impl Measurable for CoordinatorLink<'_, '_, '_> {
2559 fn measure(&self, constraints: Constraints) -> Placeable {
2560 self.chain.measure_from(self.index, self.frame, constraints)
2561 }
2562
2563 fn min_intrinsic_width(&self, height: f32) -> f32 {
2564 self.chain
2565 .min_intrinsic_width_from(self.index, self.frame, height)
2566 }
2567
2568 fn max_intrinsic_width(&self, height: f32) -> f32 {
2569 self.chain
2570 .max_intrinsic_width_from(self.index, self.frame, height)
2571 }
2572
2573 fn min_intrinsic_height(&self, width: f32) -> f32 {
2574 self.chain
2575 .min_intrinsic_height_from(self.index, self.frame, width)
2576 }
2577
2578 fn max_intrinsic_height(&self, width: f32) -> f32 {
2579 self.chain
2580 .max_intrinsic_height_from(self.index, self.frame, width)
2581 }
2582}
2583
2584struct CoordinatorNode {
2585 modifier_index: usize,
2586 node: Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2587 measured_size: Cell<Size>,
2588 accumulated_offset: Cell<Point>,
2589}
2590
2591impl CoordinatorNode {
2592 fn new(
2593 modifier_index: usize,
2594 node: Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2595 ) -> Self {
2596 Self {
2597 modifier_index,
2598 node,
2599 measured_size: Cell::new(Size::default()),
2600 accumulated_offset: Cell::new(Point::default()),
2601 }
2602 }
2603
2604 fn matches(
2605 &self,
2606 modifier_index: usize,
2607 node: &Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2608 ) -> bool {
2609 self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2610 }
2611
2612 #[cfg(test)]
2613 fn ptr(&self) -> usize {
2614 Rc::as_ptr(&self.node) as *const () as usize
2615 }
2616}
2617
2618#[derive(Default)]
2619struct CoordinatorChain {
2620 nodes: Vec<CoordinatorNode>,
2621 inner_size: Cell<Size>,
2623}
2624
2625impl CoordinatorChain {
2626 fn reconcile(&mut self, layout_node_data: &[LayoutModifierNodeData]) {
2627 if self.matches(layout_node_data) {
2628 return;
2629 }
2630
2631 let mut previous_nodes = std::mem::take(&mut self.nodes);
2632 self.nodes.reserve(layout_node_data.len());
2633
2634 for (modifier_index, node) in layout_node_data {
2635 if let Some(position) = previous_nodes
2636 .iter()
2637 .position(|candidate| candidate.matches(*modifier_index, node))
2638 {
2639 self.nodes.push(previous_nodes.swap_remove(position));
2640 } else {
2641 self.nodes
2642 .push(CoordinatorNode::new(*modifier_index, Rc::clone(node)));
2643 }
2644 }
2645 }
2646
2647 fn matches(&self, layout_node_data: &[LayoutModifierNodeData]) -> bool {
2648 self.nodes.len() == layout_node_data.len()
2649 && self
2650 .nodes
2651 .iter()
2652 .zip(layout_node_data.iter())
2653 .all(|(node, (modifier_index, node_rc))| node.matches(*modifier_index, node_rc))
2654 }
2655
2656 fn measure_from(
2657 &self,
2658 index: usize,
2659 frame: &CoordinatorFrame<'_>,
2660 constraints: Constraints,
2661 ) -> Placeable {
2662 let Some(node) = self.nodes.get(index) else {
2663 let mut placements = frame.placements.borrow_mut();
2664 let size = frame.measure_policy.measure_into(
2665 frame.scope,
2666 frame.measurables,
2667 constraints,
2668 &mut placements,
2669 );
2670 self.inner_size.set(size);
2671 return Placeable::value(size.width, size.height, NodeId::default());
2672 };
2673
2674 let wrapped = CoordinatorLink {
2675 chain: self,
2676 frame,
2677 index: index + 1,
2678 };
2679 let node_borrow = node.node.borrow();
2680
2681 let Some(layout_node) = node_borrow.as_layout_node() else {
2682 let placeable = wrapped.measure(constraints);
2683 node.measured_size.set(Size {
2684 width: placeable.width(),
2685 height: placeable.height(),
2686 });
2687 let child_accumulated = self.total_content_offset_from(index + 1);
2688 node.accumulated_offset.set(child_accumulated);
2689 return Placeable::value_with_offset(
2690 placeable.width(),
2691 placeable.height(),
2692 NodeId::default(),
2693 (child_accumulated.x, child_accumulated.y),
2694 );
2695 };
2696
2697 let result = match frame.context.try_borrow_mut() {
2698 Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
2699 Err(_) => {
2700 let mut temp = LayoutNodeContext::new(frame.scope.density());
2701 let result = layout_node.measure(&mut temp, &wrapped, constraints);
2702 if let Ok(mut context) = frame.context.try_borrow_mut() {
2703 for kind in temp.take_invalidations() {
2704 context.invalidate(kind);
2705 }
2706 }
2707 result
2708 }
2709 };
2710
2711 node.measured_size.set(result.size);
2712 let local_offset = Point {
2713 x: result.placement_offset_x,
2714 y: result.placement_offset_y,
2715 };
2716 let child_accumulated = self.total_content_offset_from(index + 1);
2717 let accumulated = Point {
2718 x: local_offset.x + child_accumulated.x,
2719 y: local_offset.y + child_accumulated.y,
2720 };
2721 node.accumulated_offset.set(accumulated);
2722
2723 Placeable::value_with_offset(
2724 result.size.width,
2725 result.size.height,
2726 NodeId::default(),
2727 (accumulated.x, accumulated.y),
2728 )
2729 }
2730
2731 fn min_intrinsic_width_from(
2732 &self,
2733 index: usize,
2734 frame: &CoordinatorFrame<'_>,
2735 height: f32,
2736 ) -> f32 {
2737 let Some(node) = self.nodes.get(index) else {
2738 return frame
2739 .measure_policy
2740 .min_intrinsic_width(frame.measurables, height);
2741 };
2742 let wrapped = CoordinatorLink {
2743 chain: self,
2744 frame,
2745 index: index + 1,
2746 };
2747 let node_borrow = node.node.borrow();
2748 node_borrow.as_layout_node().map_or_else(
2749 || wrapped.min_intrinsic_width(height),
2750 |layout_node| layout_node.min_intrinsic_width(&wrapped, height, frame.scope.density()),
2751 )
2752 }
2753
2754 fn max_intrinsic_width_from(
2755 &self,
2756 index: usize,
2757 frame: &CoordinatorFrame<'_>,
2758 height: f32,
2759 ) -> f32 {
2760 let Some(node) = self.nodes.get(index) else {
2761 return frame
2762 .measure_policy
2763 .max_intrinsic_width(frame.measurables, height);
2764 };
2765 let wrapped = CoordinatorLink {
2766 chain: self,
2767 frame,
2768 index: index + 1,
2769 };
2770 let node_borrow = node.node.borrow();
2771 node_borrow.as_layout_node().map_or_else(
2772 || wrapped.max_intrinsic_width(height),
2773 |layout_node| layout_node.max_intrinsic_width(&wrapped, height, frame.scope.density()),
2774 )
2775 }
2776
2777 fn min_intrinsic_height_from(
2778 &self,
2779 index: usize,
2780 frame: &CoordinatorFrame<'_>,
2781 width: f32,
2782 ) -> f32 {
2783 let Some(node) = self.nodes.get(index) else {
2784 return frame
2785 .measure_policy
2786 .min_intrinsic_height(frame.measurables, width);
2787 };
2788 let wrapped = CoordinatorLink {
2789 chain: self,
2790 frame,
2791 index: index + 1,
2792 };
2793 let node_borrow = node.node.borrow();
2794 node_borrow.as_layout_node().map_or_else(
2795 || wrapped.min_intrinsic_height(width),
2796 |layout_node| layout_node.min_intrinsic_height(&wrapped, width, frame.scope.density()),
2797 )
2798 }
2799
2800 fn max_intrinsic_height_from(
2801 &self,
2802 index: usize,
2803 frame: &CoordinatorFrame<'_>,
2804 width: f32,
2805 ) -> f32 {
2806 let Some(node) = self.nodes.get(index) else {
2807 return frame
2808 .measure_policy
2809 .max_intrinsic_height(frame.measurables, width);
2810 };
2811 let wrapped = CoordinatorLink {
2812 chain: self,
2813 frame,
2814 index: index + 1,
2815 };
2816 let node_borrow = node.node.borrow();
2817 node_borrow.as_layout_node().map_or_else(
2818 || wrapped.max_intrinsic_height(width),
2819 |layout_node| layout_node.max_intrinsic_height(&wrapped, width, frame.scope.density()),
2820 )
2821 }
2822
2823 fn write_geometry(
2827 &self,
2828 geometry: &crate::modifier::CoordinatorGeometry,
2829 node_offset: Point,
2830 ) -> bool {
2831 let content = self.total_content_offset_from(0);
2832 let placed = |inner_offset: Point, size: Size| GeometryRect {
2833 x: content.x - inner_offset.x - node_offset.x,
2834 y: content.y - inner_offset.y - node_offset.y,
2835 width: size.width,
2836 height: size.height,
2837 };
2838 geometry.replace(
2839 self.nodes
2840 .iter()
2841 .map(|node| placed(node.accumulated_offset.get(), node.measured_size.get()))
2842 .chain(std::iter::once(placed(
2843 Point::default(),
2844 self.inner_size.get(),
2845 ))),
2846 )
2847 }
2848
2849 fn total_content_offset_from(&self, index: usize) -> Point {
2850 self.nodes
2851 .get(index)
2852 .map(|node| node.accumulated_offset.get())
2853 .unwrap_or_default()
2854 }
2855
2856 #[cfg(test)]
2857 fn debug_ptrs(&self) -> Vec<usize> {
2858 self.nodes.iter().map(CoordinatorNode::ptr).collect()
2859 }
2860}
2861
2862#[derive(Default)]
2863pub(crate) struct LayoutRuntimeState {
2864 child_ids: Vec<NodeId>,
2865 child_states: Vec<Rc<LayoutChildMeasureState>>,
2866 child_measurables: Vec<Box<dyn Measurable>>,
2867 coordinator_chain: CoordinatorChain,
2868 error: Rc<RefCell<Option<NodeError>>>,
2871}
2872
2873impl LayoutRuntimeState {
2874 fn reconcile_child_measurables(&mut self, child_ids: &[NodeId]) {
2875 if self.child_ids == child_ids {
2876 return;
2877 }
2878
2879 let mut previous_ids = std::mem::take(&mut self.child_ids);
2880 let mut previous_states = std::mem::take(&mut self.child_states);
2881 let mut previous_measurables = std::mem::take(&mut self.child_measurables);
2882
2883 self.child_ids.reserve(child_ids.len());
2884 self.child_states.reserve(child_ids.len());
2885 self.child_measurables.reserve(child_ids.len());
2886
2887 for &child_id in child_ids {
2888 if let Some(position) = previous_ids.iter().position(|&id| id == child_id) {
2889 self.child_ids.push(previous_ids.swap_remove(position));
2890 self.child_states
2891 .push(previous_states.swap_remove(position));
2892 self.child_measurables
2893 .push(previous_measurables.swap_remove(position));
2894 } else {
2895 let state = LayoutChildMeasureState::new(child_id);
2896 self.child_ids.push(child_id);
2897 self.child_states.push(Rc::clone(&state));
2898 self.child_measurables
2899 .push(Box::new(LayoutChildMeasurable::new(state)));
2900 }
2901 }
2902 }
2903
2904 fn child_state(&self, index: usize) -> Rc<LayoutChildMeasureState> {
2905 Rc::clone(&self.child_states[index])
2906 }
2907
2908 fn child_measurables(&self) -> &[Box<dyn Measurable>] {
2909 self.child_measurables.as_slice()
2910 }
2911
2912 fn reconcile_coordinator_chain(&mut self, layout_node_data: &[LayoutModifierNodeData]) {
2913 self.coordinator_chain.reconcile(layout_node_data);
2914 }
2915
2916 fn coordinator_chain(&self) -> &CoordinatorChain {
2917 &self.coordinator_chain
2918 }
2919
2920 fn clear_frame_bindings(&self) {
2921 for child_state in &self.child_states {
2922 child_state.clear_frame_bindings();
2923 }
2924 }
2925
2926 #[cfg(test)]
2927 pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
2928 LayoutRuntimeDebugStats {
2929 child_ids: self.child_ids.clone(),
2930 child_state_ptrs: self
2931 .child_states
2932 .iter()
2933 .map(|state| Rc::as_ptr(state) as *const () as usize)
2934 .collect(),
2935 child_measurable_ptrs: self
2936 .child_measurables
2937 .iter()
2938 .map(|measurable| {
2939 measurable.as_ref() as *const dyn Measurable as *const () as usize
2940 })
2941 .collect(),
2942 child_measurable_count: self.child_measurables.len(),
2943 coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
2944 coordinator_node_count: self.coordinator_chain.nodes.len(),
2945 }
2946 }
2947}
2948
2949#[cfg(test)]
2950#[derive(Debug, Clone, PartialEq, Eq)]
2951pub(crate) struct LayoutRuntimeDebugStats {
2952 pub(crate) child_ids: Vec<NodeId>,
2953 pub(crate) child_state_ptrs: Vec<usize>,
2954 pub(crate) child_measurable_ptrs: Vec<usize>,
2955 pub(crate) child_measurable_count: usize,
2956 pub(crate) coordinator_node_ptrs: Vec<usize>,
2957 pub(crate) coordinator_node_count: usize,
2958}
2959
2960struct LayoutChildMeasureConfig {
2961 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
2962 node_id: NodeId,
2963 error: Rc<RefCell<Option<NodeError>>>,
2964 runtime_handle: Option<RuntimeHandle>,
2965 cache: LayoutNodeCacheHandles,
2966 cache_epoch: u64,
2967 force_remeasure: bool,
2968 measure_handle: Option<LayoutMeasureHandle>,
2969 layout_state: Option<Rc<RefCell<LayoutState>>>,
2970}
2971
2972struct LayoutChildMeasureState {
2973 applier: RefCell<Option<Rc<ConcreteApplierHost<MemoryApplier>>>>,
2974 node_id: Cell<NodeId>,
2975 measured: RefCell<Option<Rc<MeasuredNode>>>,
2976 last_position: Cell<Option<Point>>,
2977 error: RefCell<Option<Rc<RefCell<Option<NodeError>>>>>,
2978 runtime_handle: RefCell<Option<RuntimeHandle>>,
2979 cache: RefCell<LayoutNodeCacheHandles>,
2980 cache_epoch: Cell<u64>,
2981 force_remeasure: Cell<bool>,
2982 measure_handle: RefCell<Option<LayoutMeasureHandle>>,
2983 layout_state: RefCell<Option<Rc<RefCell<LayoutState>>>>,
2984}
2985
2986impl LayoutChildMeasureState {
2987 fn new(node_id: NodeId) -> Rc<Self> {
2988 Rc::new(Self {
2989 applier: RefCell::new(None),
2990 node_id: Cell::new(node_id),
2991 measured: RefCell::new(None),
2992 last_position: Cell::new(None),
2993 error: RefCell::new(None),
2994 runtime_handle: RefCell::new(None),
2995 cache: RefCell::new(LayoutNodeCacheHandles::default()),
2996 cache_epoch: Cell::new(0),
2997 force_remeasure: Cell::new(true),
2998 measure_handle: RefCell::new(None),
2999 layout_state: RefCell::new(None),
3000 })
3001 }
3002
3003 fn configure(&self, config: LayoutChildMeasureConfig) {
3004 config.cache.activate(config.cache_epoch);
3005 *self.applier.borrow_mut() = Some(config.applier);
3006 self.node_id.set(config.node_id);
3007 self.measured.borrow_mut().take();
3008 self.last_position.set(None);
3009 *self.error.borrow_mut() = Some(config.error);
3010 *self.runtime_handle.borrow_mut() = config.runtime_handle;
3011 *self.cache.borrow_mut() = config.cache;
3012 self.cache_epoch.set(config.cache_epoch);
3013 self.force_remeasure.set(config.force_remeasure);
3014 *self.measure_handle.borrow_mut() = config.measure_handle;
3015 *self.layout_state.borrow_mut() = config.layout_state;
3016 }
3017
3018 fn clear_frame_bindings(&self) {
3019 self.measured.borrow_mut().take();
3020 *self.applier.borrow_mut() = None;
3021 *self.error.borrow_mut() = None;
3022 *self.runtime_handle.borrow_mut() = None;
3023 *self.measure_handle.borrow_mut() = None;
3024 *self.layout_state.borrow_mut() = None;
3025 }
3026
3027 fn node_id(&self) -> NodeId {
3028 self.node_id.get()
3029 }
3030
3031 fn cache(&self) -> LayoutNodeCacheHandles {
3032 self.cache.borrow().clone()
3033 }
3034
3035 fn applier(&self) -> Option<Rc<ConcreteApplierHost<MemoryApplier>>> {
3036 self.applier.borrow().clone()
3037 }
3038
3039 fn layout_state(&self) -> Option<Rc<RefCell<LayoutState>>> {
3040 self.layout_state.borrow().clone()
3041 }
3042
3043 fn take_measured(&self) -> Option<Rc<MeasuredNode>> {
3044 self.measured.borrow_mut().take()
3045 }
3046
3047 fn last_position(&self) -> Option<Point> {
3048 self.last_position.get()
3049 }
3050
3051 fn set_last_position(&self, position: Point) {
3052 self.last_position.set(Some(position));
3053 }
3054
3055 fn place_retained(&self, position: Point) {
3056 self.set_last_position(position);
3057 if let Some(layout_state) = self.layout_state() {
3058 layout_state.borrow_mut().place(position);
3059 return;
3060 }
3061 let Some(applier) = self.applier() else {
3062 return;
3063 };
3064 let Ok(mut applier) = applier.try_borrow_typed() else {
3065 return;
3066 };
3067 let node_id = self.node_id();
3068 if applier
3069 .with_node::<LayoutNode, _>(node_id, |node| {
3070 node.set_position(position);
3071 })
3072 .is_err()
3073 {
3074 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3075 node.set_position(position);
3076 });
3077 }
3078 }
3079
3080 fn set_measured(&self, measured: Option<Rc<MeasuredNode>>) {
3081 *self.measured.borrow_mut() = measured;
3082 }
3083
3084 fn record_error(&self, err: NodeError) {
3085 let Some(error) = self.error.borrow().clone() else {
3086 return;
3087 };
3088 let mut slot = error.borrow_mut();
3089 if slot.is_none() {
3090 *slot = Some(err);
3091 }
3092 }
3093
3094 fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
3095 let node_id = self.node_id();
3096 if let Some(handle) = self.measure_handle.borrow().clone() {
3097 return handle.measure(node_id, constraints);
3098 }
3099 let applier = self.applier().ok_or(NodeError::MissingContext {
3100 id: node_id,
3101 reason: "layout child applier not configured",
3102 })?;
3103 measure_node_with_host(
3104 applier,
3105 self.runtime_handle.borrow().clone(),
3106 node_id,
3107 constraints,
3108 self.cache_epoch.get(),
3109 )
3110 }
3111
3112 fn intrinsic_measure(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
3113 let cache = self.cache();
3114 cache.activate(self.cache_epoch.get());
3115 if !self.force_remeasure.get()
3116 && let Some(cached) = cache.get_measurement(constraints)
3117 {
3118 return Some(cached);
3119 }
3120
3121 match self.perform_measure(constraints) {
3122 Ok(measured) => {
3123 self.force_remeasure.set(false);
3124 cache.store_measurement(constraints, Rc::clone(&measured));
3125 Some(measured)
3126 }
3127 Err(err) => {
3128 self.record_error(err);
3129 None
3130 }
3131 }
3132 }
3133}
3134
3135struct LayoutChildMeasurable {
3136 state: Rc<LayoutChildMeasureState>,
3137}
3138
3139impl LayoutChildMeasurable {
3140 fn new(state: Rc<LayoutChildMeasureState>) -> Self {
3141 Self { state }
3142 }
3143
3144 fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
3145 let applier = self.state.applier()?;
3146 let node_id = self.state.node_id();
3147 let Ok(mut applier) = applier.try_borrow_typed() else {
3148 return None;
3149 };
3150
3151 applier
3152 .with_node::<LayoutNode, _>(node_id, |layout_node| {
3153 let props = layout_node.resolved_modifiers().layout_properties();
3154 let weight = props.weight().unwrap_or_default();
3155 cranpose_ui_layout::ParentData {
3156 weight: weight.weight,
3157 fill: weight.fill,
3158 box_alignment: props.box_alignment(),
3159 row_alignment: props.row_alignment(),
3160 column_alignment: props.column_alignment(),
3161 }
3162 })
3163 .ok()
3164 }
3165}
3166
3167impl PlaceTarget for LayoutChildMeasureState {
3168 fn place(&self, x: f32, y: f32) {
3169 let internal_offset = self
3170 .measured
3171 .borrow()
3172 .as_ref()
3173 .map(|measured| measured.offset)
3174 .unwrap_or_default();
3175 self.place_retained(Point {
3176 x: x + internal_offset.x,
3177 y: y + internal_offset.y,
3178 });
3179 }
3180}
3181
3182impl Measurable for LayoutChildMeasurable {
3183 fn measure(&self, constraints: Constraints) -> Placeable {
3184 let state = &self.state;
3185 let cache = state.cache();
3186 cache.activate(state.cache_epoch.get());
3187 let measured_size;
3188 if !state.force_remeasure.get() {
3189 if let Some(cached) = cache.get_measurement(constraints) {
3190 measured_size = cached.size;
3191 state.set_measured(Some(Rc::clone(&cached)));
3192 } else {
3193 match state.perform_measure(constraints) {
3194 Ok(measured) => {
3195 state.force_remeasure.set(false);
3196 measured_size = measured.size;
3197 cache.store_measurement(constraints, Rc::clone(&measured));
3198 state.set_measured(Some(measured));
3199 }
3200 Err(err) => {
3201 state.record_error(err);
3202 state.set_measured(None);
3203 measured_size = Size {
3204 width: 0.0,
3205 height: 0.0,
3206 };
3207 }
3208 }
3209 }
3210 } else {
3211 match state.perform_measure(constraints) {
3212 Ok(measured) => {
3213 state.force_remeasure.set(false);
3214 measured_size = measured.size;
3215 cache.store_measurement(constraints, Rc::clone(&measured));
3216 state.set_measured(Some(measured));
3217 }
3218 Err(err) => {
3219 state.record_error(err);
3220 state.set_measured(None);
3221 measured_size = Size {
3222 width: 0.0,
3223 height: 0.0,
3224 };
3225 }
3226 }
3227 }
3228
3229 if let Some(layout_state) = state.layout_state() {
3230 let mut layout_state = layout_state.borrow_mut();
3231 layout_state.set_size(measured_size);
3232 } else if let Some(applier) = state.applier() {
3233 let Ok(mut applier) = applier.try_borrow_typed() else {
3234 return Placeable::value(
3235 measured_size.width,
3236 measured_size.height,
3237 state.node_id(),
3238 );
3239 };
3240 let _ = applier.with_node::<LayoutNode, _>(state.node_id(), |node| {
3241 node.set_measured_size(measured_size);
3242 });
3243 }
3244
3245 let size_for_parent = state
3246 .measured
3247 .borrow()
3248 .as_ref()
3249 .map_or(measured_size, |measured| measured.size_for_parent());
3250
3251 Placeable::with_place_target(
3252 size_for_parent.width,
3253 size_for_parent.height,
3254 state.node_id(),
3255 Rc::clone(&self.state) as Rc<dyn PlaceTarget>,
3256 )
3257 }
3258
3259 fn min_intrinsic_width(&self, height: f32) -> f32 {
3260 let kind = IntrinsicKind::MinWidth(height);
3261 let cache = self.state.cache();
3262 cache.activate(self.state.cache_epoch.get());
3263 if !self.state.force_remeasure.get()
3264 && let Some(value) = cache.get_intrinsic(&kind)
3265 {
3266 return value;
3267 }
3268 let constraints = Constraints {
3269 min_width: 0.0,
3270 max_width: f32::INFINITY,
3271 min_height: height,
3272 max_height: height,
3273 };
3274 if let Some(node) = self.state.intrinsic_measure(constraints) {
3275 let value = node.size_for_parent().width;
3276 cache.store_intrinsic(kind, value);
3277 value
3278 } else {
3279 0.0
3280 }
3281 }
3282
3283 fn max_intrinsic_width(&self, height: f32) -> f32 {
3284 let kind = IntrinsicKind::MaxWidth(height);
3285 let cache = self.state.cache();
3286 cache.activate(self.state.cache_epoch.get());
3287 if !self.state.force_remeasure.get()
3288 && let Some(value) = cache.get_intrinsic(&kind)
3289 {
3290 return value;
3291 }
3292 let constraints = Constraints {
3293 min_width: 0.0,
3294 max_width: f32::INFINITY,
3295 min_height: 0.0,
3296 max_height: height,
3297 };
3298 if let Some(node) = self.state.intrinsic_measure(constraints) {
3299 let value = node.size_for_parent().width;
3300 cache.store_intrinsic(kind, value);
3301 value
3302 } else {
3303 0.0
3304 }
3305 }
3306
3307 fn min_intrinsic_height(&self, width: f32) -> f32 {
3308 let kind = IntrinsicKind::MinHeight(width);
3309 let cache = self.state.cache();
3310 cache.activate(self.state.cache_epoch.get());
3311 if !self.state.force_remeasure.get()
3312 && let Some(value) = cache.get_intrinsic(&kind)
3313 {
3314 return value;
3315 }
3316 let constraints = Constraints {
3317 min_width: width,
3318 max_width: width,
3319 min_height: 0.0,
3320 max_height: f32::INFINITY,
3321 };
3322 if let Some(node) = self.state.intrinsic_measure(constraints) {
3323 let value = node.size_for_parent().height;
3324 cache.store_intrinsic(kind, value);
3325 value
3326 } else {
3327 0.0
3328 }
3329 }
3330
3331 fn max_intrinsic_height(&self, width: f32) -> f32 {
3332 let kind = IntrinsicKind::MaxHeight(width);
3333 let cache = self.state.cache();
3334 cache.activate(self.state.cache_epoch.get());
3335 if !self.state.force_remeasure.get()
3336 && let Some(value) = cache.get_intrinsic(&kind)
3337 {
3338 return value;
3339 }
3340 let constraints = Constraints {
3341 min_width: 0.0,
3342 max_width: width,
3343 min_height: 0.0,
3344 max_height: f32::INFINITY,
3345 };
3346 if let Some(node) = self.state.intrinsic_measure(constraints) {
3347 let value = node.size_for_parent().height;
3348 cache.store_intrinsic(kind, value);
3349 value
3350 } else {
3351 0.0
3352 }
3353 }
3354
3355 fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3356 let parent_data = self.resolved_parent_data()?;
3357 if !parent_data.has_weight() {
3358 return None;
3359 }
3360 Some(cranpose_ui_layout::FlexParentData::new(
3361 parent_data.weight,
3362 parent_data.fill,
3363 ))
3364 }
3365
3366 fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3367 self.resolved_parent_data().unwrap_or_default()
3368 }
3369}
3370
3371fn measure_node_with_host(
3372 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
3373 runtime_handle: Option<RuntimeHandle>,
3374 node_id: NodeId,
3375 constraints: Constraints,
3376 epoch: u64,
3377) -> Result<Rc<MeasuredNode>, NodeError> {
3378 let runtime_handle = match runtime_handle {
3379 Some(handle) => Some(handle),
3380 None => applier.borrow_typed().runtime_handle(),
3381 };
3382 let mut builder = LayoutBuilder::new_with_epoch(
3383 applier,
3384 epoch,
3385 Rc::new(RefCell::new(SlotTable::default())),
3386 FrameLayoutArena::default(),
3387 );
3388 builder.set_runtime_handle(runtime_handle);
3389 builder.measure_node(node_id, constraints)
3390}
3391
3392#[derive(Clone)]
3393struct RuntimeNodeMetadata {
3394 modifier: Modifier,
3395 resolved_modifiers: ResolvedModifiers,
3396 modifier_slices: Rc<ModifierNodeSlices>,
3397 semantics: Option<Rc<SemanticsConfiguration>>,
3398 role: SemanticsRole,
3399 button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3400}
3401
3402impl Default for RuntimeNodeMetadata {
3403 fn default() -> Self {
3404 Self {
3405 modifier: Modifier::empty(),
3406 resolved_modifiers: ResolvedModifiers::default(),
3407 modifier_slices: Rc::default(),
3408 semantics: None,
3409 role: SemanticsRole::Unknown,
3410 button_handler: None,
3411 }
3412 }
3413}
3414
3415fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3416 modifier_slices
3417 .annotated_text()
3418 .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3419 value: SemanticsText(Rc::clone(text)),
3420 })
3421}
3422
3423fn runtime_metadata_for(
3424 applier: &mut MemoryApplier,
3425 node_id: NodeId,
3426) -> Result<RuntimeNodeMetadata, NodeError> {
3427 if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3428 let modifier = layout.modifier.clone();
3429 let resolved_modifiers = layout.resolved_modifiers();
3430 let modifier_slices = layout.modifier_slices_snapshot();
3431 let role = role_from_modifier_slices(&modifier_slices);
3432
3433 RuntimeNodeMetadata {
3434 modifier,
3435 resolved_modifiers,
3436 modifier_slices,
3437 semantics: layout.semantics_configuration().map(Rc::new),
3438 role,
3439 button_handler: None,
3440 }
3441 }) {
3442 return Ok(meta);
3443 }
3444
3445 if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
3446 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3447 (
3448 node.modifier(),
3449 node.resolved_modifiers(),
3450 node.modifier_slices_snapshot(),
3451 node.semantics_configuration().map(Rc::new),
3452 )
3453 })
3454 {
3455 return Ok(RuntimeNodeMetadata {
3456 modifier,
3457 resolved_modifiers,
3458 modifier_slices,
3459 semantics,
3460 role: SemanticsRole::Subcompose,
3461 button_handler: None,
3462 });
3463 }
3464 Ok(RuntimeNodeMetadata::default())
3465}
3466
3467fn clear_semantics_dirty_flags(
3468 applier: &mut MemoryApplier,
3469 node: &MeasuredNode,
3470) -> Result<(), NodeError> {
3471 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3472 layout.clear_needs_semantics();
3473 }) {
3474 Ok(()) => {}
3475 Err(NodeError::Missing { .. }) => {}
3476 Err(NodeError::TypeMismatch { .. }) => {
3477 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3478 subcompose.clear_needs_semantics();
3479 }) {
3480 Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
3481 Err(err) => return Err(err),
3482 }
3483 }
3484 Err(err) => return Err(err),
3485 }
3486
3487 for child in &node.children {
3488 clear_semantics_dirty_flags(applier, &child.node)?;
3489 }
3490
3491 Ok(())
3492}
3493
3494fn build_semantics_tree_from_live_nodes(
3495 applier: &mut MemoryApplier,
3496 node: &MeasuredNode,
3497) -> Result<SemanticsTree, NodeError> {
3498 Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3499 applier, node, None,
3500 )?))
3501}
3502
3503fn semantics_node_from_parts(
3504 node_id: NodeId,
3505 node_generation: u32,
3506 mut role: SemanticsRole,
3507 config: Option<SemanticsConfiguration>,
3508 children: Vec<SemanticsNode>,
3509 bounds: GeometryRect,
3510) -> SemanticsNode {
3511 let mut node = SemanticsNode {
3512 node_id,
3513 bounds,
3514 node_generation,
3515 children,
3516 ..SemanticsNode::default()
3517 };
3518
3519 if let Some(config) = config {
3520 if config.role == Some(SemanticsWidgetRole::Button) {
3521 role = SemanticsRole::Button;
3522 }
3523 if config.is_activatable() {
3524 node.actions.push(SemanticsAction::Click {
3525 handler: SemanticsCallback::new(node_id),
3526 });
3527 }
3528 node.widget_role = config.role;
3529 node.description = config.content_description;
3530 node.state_description = config.state_description;
3531 node.on_click_label = config.on_click_label.or_else(|| {
3532 config
3533 .on_click
3534 .as_ref()
3535 .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
3536 });
3537 node.on_click = config.on_click;
3538 node.on_long_click = config.on_long_click;
3539 node.on_long_click_label = config.on_long_click_label;
3540 node.on_magic_tap = config.on_magic_tap;
3541 node.on_magic_tap_label = config.on_magic_tap_label;
3542 node.input_labels = config.input_labels;
3543 node.language = config.language;
3544 node.selected = config.selected;
3545 node.toggled = config.toggled;
3546 node.enabled = config.enabled;
3547 node.custom_actions = config.custom_actions;
3548 node.canvas_children = config.canvas_children;
3549 node.editable_text = config.is_editable_text;
3550 node.multiline = config.multiline;
3551 node.hidden = config.hidden;
3552 node.is_modal = config.is_modal
3553 && modal_takes_space(Size {
3554 width: bounds.width,
3555 height: bounds.height,
3556 });
3557 node.merge_descendants = config.merge_descendants;
3558 node.selectable_group = config.selectable_group;
3559 node.pane_title = config.pane_title;
3560 node.error = config.error;
3561 node.password = config.password;
3562 node.traversal_index = config.traversal_index;
3563 node.text = config.text;
3564 node.text_selection = config.text_selection;
3565 node.live_region = config.live_region;
3566 node.progress = config.progress;
3567 node.set_progress = config.set_progress;
3568 node.set_text = config.set_text;
3569 node.set_selection = config.set_selection;
3570 node.expand = config.expand;
3571 node.collapse = config.collapse;
3572 node.dismiss = config.dismiss;
3573 node.vertical_scroll = config.vertical_scroll;
3574 node.horizontal_scroll = config.horizontal_scroll;
3575 node.scroll_by = config.scroll_by;
3576 node.scroll_to_index = config.scroll_to_index;
3577 node.collection = config.collection;
3578 }
3579
3580 node.focusable = crate::focus_dispatch::has_focus_target(node_id);
3581 node.focused = node.focusable && crate::focus_dispatch::active_focus_target() == Some(node_id);
3582
3583 node.role = role;
3584 node
3585}
3586
3587fn build_semantics_node_from_live_nodes(
3588 applier: &mut MemoryApplier,
3589 node: &MeasuredNode,
3590 origin: Option<Point>,
3591) -> Result<SemanticsNode, NodeError> {
3592 let (role, config, (bounds, content)) =
3593 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3594 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
3595 let config = layout.semantics_configuration();
3596 layout.clear_needs_semantics();
3597 (
3598 role,
3599 config,
3600 semantics_placement(&layout.layout_state(), origin),
3601 )
3602 }) {
3603 Ok(data) => data,
3604 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3605 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3606 subcompose.clear_needs_semantics();
3607 (
3608 SemanticsRole::Subcompose,
3609 subcompose.semantics_configuration(),
3610 semantics_placement(&subcompose.layout_state(), origin),
3611 )
3612 }) {
3613 Ok(data) => data,
3614 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3615 let top_left = origin.unwrap_or_default();
3616 (
3617 SemanticsRole::Unknown,
3618 None,
3619 (
3620 GeometryRect::from_origin_size(top_left, node.size),
3621 top_left,
3622 ),
3623 )
3624 }
3625 Err(err) => return Err(err),
3626 }
3627 }
3628 Err(err) => return Err(err),
3629 };
3630
3631 let mut children = Vec::with_capacity(node.children.len());
3632 for child in &node.children {
3633 children.push(build_semantics_node_from_live_nodes(
3634 applier,
3635 &child.node,
3636 Some(content),
3637 )?);
3638 }
3639
3640 Ok(semantics_node_from_parts(
3641 node.node_id,
3642 applier.node_generation(node.node_id),
3643 role,
3644 config,
3645 children,
3646 bounds,
3647 ))
3648}
3649
3650fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
3651 stats.semantics_node_count += 1;
3652 stats.semantics_action_count += node.actions.len();
3653 stats.semantics_action_capacity += node.actions.capacity();
3654 stats.semantics_child_count += node.children.len();
3655 stats.semantics_child_capacity += node.children.capacity();
3656 stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
3657 stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
3658
3659 if let Some(description) = &node.description {
3660 stats.semantics_description_count += 1;
3661 stats.semantics_description_bytes += description.capacity();
3662 stats.semantics_heap_bytes += description.capacity();
3663 }
3664
3665 for child in &node.children {
3666 record_semantics_allocation_stats(child, stats);
3667 }
3668}
3669
3670fn record_layout_box_allocation_stats(
3671 layout_box: &LayoutBox,
3672 stats: &mut LayoutAllocationDebugStats,
3673) {
3674 stats.layout_box_count += 1;
3675 stats.layout_box_child_count += layout_box.children.len();
3676 stats.layout_box_child_capacity += layout_box.children.capacity();
3677 stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
3678 stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
3679
3680 for child in &layout_box.children {
3681 record_layout_box_allocation_stats(child, stats);
3682 }
3683}
3684
3685fn build_layout_tree(
3686 applier: &mut MemoryApplier,
3687 node: &MeasuredNode,
3688) -> Result<LayoutTree, NodeError> {
3689 fn place(
3690 applier: &mut MemoryApplier,
3691 node: &MeasuredNode,
3692 origin: Point,
3693 parent_layer_translation: Point,
3694 ) -> Result<LayoutBox, NodeError> {
3695 let top_left = Point {
3696 x: origin.x + node.offset.x,
3697 y: origin.y + node.offset.y,
3698 };
3699 let rect = GeometryRect {
3700 x: top_left.x,
3701 y: top_left.y,
3702 width: node.size.width,
3703 height: node.size.height,
3704 };
3705 let (data, layer_translation) = snapshot_node_data(
3706 applier,
3707 node.node_id,
3708 top_left,
3709 node.size,
3710 parent_layer_translation,
3711 )?;
3712 let mut children = Vec::with_capacity(node.children.len());
3713 for child in &node.children {
3714 if crate::modifier::is_window_root(applier, child.node.node_id) {
3715 continue;
3716 }
3717 let child_origin = Point {
3718 x: top_left.x + child.offset.x,
3719 y: top_left.y + child.offset.y,
3720 };
3721 children.push(place(
3722 applier,
3723 &child.node,
3724 child_origin,
3725 layer_translation,
3726 )?);
3727 }
3728 Ok(LayoutBox {
3729 node_generation: applier.node_generation(node.node_id),
3730 ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
3731 })
3732 }
3733
3734 Ok(LayoutTree::new(place(
3735 applier,
3736 node,
3737 Point { x: 0.0, y: 0.0 },
3738 Point { x: 0.0, y: 0.0 },
3739 )?))
3740}
3741
3742fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
3743 match &layout_box.node_data.kind {
3744 LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
3745 LayoutNodeKind::Spacer => SemanticsRole::Spacer,
3746 LayoutNodeKind::Unknown => SemanticsRole::Unknown,
3747 LayoutNodeKind::Button { .. } => SemanticsRole::Button,
3748 LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
3749 }
3750}
3751
3752fn build_semantics_node_from_layout_box(layout_box: &LayoutBox) -> SemanticsNode {
3753 let children = layout_box
3754 .children
3755 .iter()
3756 .map(build_semantics_node_from_layout_box)
3757 .collect();
3758
3759 semantics_node_from_parts(
3760 layout_box.node_id,
3761 layout_box.node_generation,
3762 semantics_role_from_layout_box(layout_box),
3763 layout_box.node_data.semantics().cloned(),
3764 children,
3765 layout_box.rect,
3766 )
3767}
3768
3769fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
3770 match &info.role {
3771 SemanticsRole::Layout => LayoutNodeKind::Layout,
3772 SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
3773 SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
3774 SemanticsRole::Spacer => LayoutNodeKind::Spacer,
3775 SemanticsRole::Button => {
3776 let handler = info
3777 .button_handler
3778 .as_ref()
3779 .cloned()
3780 .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
3781 LayoutNodeKind::Button { on_click: handler }
3782 }
3783 SemanticsRole::Unknown => LayoutNodeKind::Unknown,
3784 }
3785}
3786
3787fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
3788 let horizontal = padding.horizontal_sum();
3789 let vertical = padding.vertical_sum();
3790 let min_width = (constraints.min_width - horizontal).max(0.0);
3791 let mut max_width = constraints.max_width;
3792 if max_width.is_finite() {
3793 max_width = (max_width - horizontal).max(0.0);
3794 }
3795 let min_height = (constraints.min_height - vertical).max(0.0);
3796 let mut max_height = constraints.max_height;
3797 if max_height.is_finite() {
3798 max_height = (max_height - vertical).max(0.0);
3799 }
3800 normalize_constraints(Constraints {
3801 min_width,
3802 max_width,
3803 min_height,
3804 max_height,
3805 })
3806}
3807
3808fn resolve_dimension(
3809 base: f32,
3810 explicit: DimensionConstraint,
3811 min_override: Option<f32>,
3812 max_override: Option<f32>,
3813 min_limit: f32,
3814 max_limit: f32,
3815) -> f32 {
3816 let mut min_bound = min_limit;
3817 if let Some(min_value) = min_override {
3818 min_bound = min_bound.max(min_value);
3819 }
3820
3821 let mut max_bound = if max_limit.is_finite() {
3822 max_limit
3823 } else {
3824 max_override.unwrap_or(max_limit)
3825 };
3826 if let Some(max_value) = max_override {
3827 if max_bound.is_finite() {
3828 max_bound = max_bound.min(max_value);
3829 } else {
3830 max_bound = max_value;
3831 }
3832 }
3833 if max_bound < min_bound {
3834 max_bound = min_bound;
3835 }
3836
3837 let mut size = match explicit {
3838 DimensionConstraint::Points(points) => points,
3839 DimensionConstraint::Fraction(fraction) => {
3840 if max_limit.is_finite() {
3841 max_limit * fraction.clamp(0.0, 1.0)
3842 } else {
3843 base
3844 }
3845 }
3846 DimensionConstraint::Unspecified => base,
3847 DimensionConstraint::Intrinsic(_) => base,
3848 };
3849
3850 size = clamp_dimension(size, min_bound, max_bound);
3851 size = clamp_dimension(size, min_limit, max_limit);
3852 size.max(0.0)
3853}
3854
3855fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
3856 let mut result = value.max(min);
3857 if max.is_finite() {
3858 result = result.min(max);
3859 }
3860 result
3861}
3862
3863fn normalize_constraints(mut constraints: Constraints) -> Constraints {
3864 if constraints.max_width < constraints.min_width {
3865 constraints.max_width = constraints.min_width;
3866 }
3867 if constraints.max_height < constraints.min_height {
3868 constraints.max_height = constraints.min_height;
3869 }
3870 constraints
3871}
3872
3873#[cfg(test)]
3874#[path = "tests/layout_tests.rs"]
3875mod tests;
3876
3877#[cfg(test)]
3878#[path = "tests/coordinator_geometry_tests.rs"]
3879mod coordinator_geometry_tests;