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