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