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