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