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