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