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