1pub mod core;
2pub mod policies;
3mod reveal;
4mod semantics_details;
5mod semantics_labels;
6mod semantics_update;
7
8use std::{
9 cell::{Cell, RefCell},
10 fmt,
11 mem::size_of,
12 rc::Rc,
13 sync::OnceLock,
14};
15
16use cranpose_core::{
17 Applier, ApplierHost, Composer, ConcreteApplierHost, MemoryApplier, Node, NodeError, NodeId,
18 Phase, RuntimeHandle, SlotTable, SlotsHost,
19};
20use cranpose_foundation::{
21 InvalidationKind, ModifierNodeContext, NodeCapabilities, SemanticsConfiguration,
22 SemanticsCustomAction, SemanticsWidgetRole,
23};
24use cranpose_ui_graphics::{ProjectiveTransform, layer_transform::layer_transform_to_window};
25use cranpose_ui_layout::{AlignmentLines, Constraints, MeasurePolicy, PlaceTarget, Placement};
26use web_time::Instant;
27
28#[cfg(test)]
29use self::core::{HorizontalAlignment, VerticalAlignment};
30use self::{
31 core::{Measurable, Placeable},
32 semantics_update::semantics_placement,
33};
34pub use self::{
35 reveal::can_skip_scroll_reveal_from_applier,
36 semantics_details::SemanticsDetails,
37 semantics_update::{build_semantics_tree_from_applier, update_semantics_tree_from_applier},
38};
39use crate::{
40 modifier::{
41 DimensionConstraint, EdgeInsets, Modifier, ModifierNodeSlices,
42 ModifierNodeSlicesDebugStats, Point, Rect as GeometryRect, ResolvedModifiers, Size,
43 },
44 subcompose_layout::{CachedBatchMeasureInputs, SubcomposeLayoutNode},
45 widgets::nodes::{
46 IntrinsicKind, LayoutNode, LayoutNodeCacheHandles, LayoutState, begin_placement_pass,
47 placement_pass_with_unplaced_nodes,
48 },
49};
50
51#[derive(Default)]
52pub(crate) struct LayoutNodeContext {
53 invalidations: Vec<InvalidationKind>,
54 update_requested: bool,
55 active_capabilities: Vec<NodeCapabilities>,
56 density: f32,
57}
58
59impl LayoutNodeContext {
60 pub(crate) fn new(density: f32) -> Self {
61 Self {
62 density,
63 ..Self::default()
64 }
65 }
66
67 pub(crate) fn take_invalidations(&mut self) -> Vec<InvalidationKind> {
68 std::mem::take(&mut self.invalidations)
69 }
70}
71
72impl ModifierNodeContext for LayoutNodeContext {
73 fn invalidate(&mut self, kind: InvalidationKind) {
74 if !self.invalidations.contains(&kind) {
75 self.invalidations.push(kind);
76 }
77 }
78
79 fn request_update(&mut self) {
80 self.update_requested = true;
81 }
82
83 fn push_active_capabilities(&mut self, capabilities: NodeCapabilities) {
84 self.active_capabilities.push(capabilities);
85 }
86
87 fn pop_active_capabilities(&mut self) {
88 self.active_capabilities.pop();
89 }
90
91 fn density(&self) -> f32 {
92 self.density
93 }
94}
95
96#[doc(hidden)]
97pub fn invalidate_all_layout_caches() {
98 crate::render_state::invalidate_layout_cache_epoch();
99}
100
101fn layout_measure_telemetry_threshold_ms() -> Option<f64> {
102 static THRESHOLD_MS: OnceLock<Option<f64>> = OnceLock::new();
103 *THRESHOLD_MS.get_or_init(|| {
104 std::env::var("CRANPOSE_LAYOUT_MEASURE_TELEMETRY_MS")
105 .ok()
106 .and_then(|value| value.parse::<f64>().ok())
107 .filter(|value| value.is_finite() && *value >= 0.0)
108 .or_else(|| {
109 std::env::var_os("CRANPOSE_LAYOUT_MEASURE_TELEMETRY")
110 .is_some()
111 .then_some(4.0)
112 })
113 })
114}
115
116struct LayoutMeasureTelemetry {
117 root: NodeId,
118 start: Instant,
119 after_repasses: Instant,
120 after_guard: Instant,
121 after_builder: Instant,
122 after_measure: Instant,
123 after_root_place: Instant,
124 after_aux: Instant,
125 after_builder_drop: Instant,
126 after_guard_drop: Instant,
127}
128
129fn log_layout_measure_telemetry(times: LayoutMeasureTelemetry) {
130 let Some(threshold_ms) = layout_measure_telemetry_threshold_ms() else {
131 return;
132 };
133
134 let total_ms = times
135 .after_guard_drop
136 .duration_since(times.start)
137 .as_secs_f64()
138 * 1000.0;
139 if total_ms < threshold_ms {
140 return;
141 }
142
143 let repass_ms = times
144 .after_repasses
145 .duration_since(times.start)
146 .as_secs_f64()
147 * 1000.0;
148 let guard_ms = times
149 .after_guard
150 .duration_since(times.after_repasses)
151 .as_secs_f64()
152 * 1000.0;
153 let builder_ms = times
154 .after_builder
155 .duration_since(times.after_guard)
156 .as_secs_f64()
157 * 1000.0;
158 let measure_ms = times
159 .after_measure
160 .duration_since(times.after_builder)
161 .as_secs_f64()
162 * 1000.0;
163 let root_place_ms = times
164 .after_root_place
165 .duration_since(times.after_measure)
166 .as_secs_f64()
167 * 1000.0;
168 let aux_ms = times
169 .after_aux
170 .duration_since(times.after_root_place)
171 .as_secs_f64()
172 * 1000.0;
173 let builder_drop_ms = times
174 .after_builder_drop
175 .duration_since(times.after_aux)
176 .as_secs_f64()
177 * 1000.0;
178 let guard_drop_ms = times
179 .after_guard_drop
180 .duration_since(times.after_builder_drop)
181 .as_secs_f64()
182 * 1000.0;
183 log::warn!(
184 "[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}",
185 times.root
186 );
187}
188
189fn log_node_measure_telemetry(
190 kind: &'static str,
191 node_id: NodeId,
192 constraints: Constraints,
193 measured: &MeasuredNode,
194 (threshold_ms, start): (f64, Instant),
195) {
196 let size = measured.size;
197 let children = measured.children.len();
198 let total_ms = start.elapsed().as_secs_f64() * 1000.0;
199 if total_ms < threshold_ms {
200 return;
201 }
202
203 log::warn!(
204 "[layout-node-telemetry] kind={kind} node={} total_ms={total_ms:.2} constraints=({:.1},{:.1},{:.1},{:.1}) size=({:.1},{:.1}) children={children}",
205 node_id,
206 constraints.min_width,
207 constraints.max_width,
208 constraints.min_height,
209 constraints.max_height,
210 size.width,
211 size.height,
212 );
213}
214
215struct ApplierSlotGuard<'a> {
216 target: &'a mut MemoryApplier,
217 host: Rc<ConcreteApplierHost<MemoryApplier>>,
218 slots: Rc<RefCell<SlotTable>>,
219}
220
221impl<'a> ApplierSlotGuard<'a> {
222 fn new(target: &'a mut MemoryApplier) -> Self {
223 let original_applier = std::mem::replace(target, MemoryApplier::new());
224 let host = Rc::new(ConcreteApplierHost::new(original_applier));
225
226 let slots = {
227 let mut applier_ref = host.borrow_typed();
228 std::mem::take(applier_ref.slots())
229 };
230 let slots = Rc::new(RefCell::new(slots));
231
232 Self {
233 target,
234 host,
235 slots,
236 }
237 }
238
239 fn host(&self) -> Rc<ConcreteApplierHost<MemoryApplier>> {
240 Rc::clone(&self.host)
241 }
242
243 fn slots_handle(&self) -> Rc<RefCell<SlotTable>> {
244 Rc::clone(&self.slots)
245 }
246}
247
248impl Drop for ApplierSlotGuard<'_> {
249 fn drop(&mut self) {
250 {
251 let mut applier_ref = self.host.borrow_typed();
252 *applier_ref.slots() = std::mem::take(&mut *self.slots.borrow_mut());
253 }
254
255 {
256 let mut applier_ref = self.host.borrow_typed();
257 let original_applier = std::mem::take(&mut *applier_ref);
258 let _ = std::mem::replace(self.target, original_applier);
259 }
260 }
261}
262
263#[derive(Clone, Copy, PartialEq)]
264struct ModifierChainInputs {
265 density: crate::density::Density,
266 window_root: bool,
267 offset: Point,
268 uses_chain: bool,
269}
270
271struct ModifierChainMeasurement {
272 size: Size,
273 alignment_lines: AlignmentLines,
274 content_offset: Point,
275 offset: Point,
276 window_root: bool,
277 uses_chain: bool,
278 hold: Option<cranpose_ui_layout::ConstraintsHold>,
280}
281
282struct ScratchVecPool<T> {
283 available: Vec<Vec<T>>,
284}
285
286impl<T> ScratchVecPool<T> {
287 fn acquire(&mut self) -> Vec<T> {
288 self.available.pop().unwrap_or_default()
289 }
290
291 fn release(&mut self, mut values: Vec<T>) {
292 values.clear();
293 self.available.push(values);
294 }
295
296 #[cfg(test)]
297 fn available_count(&self) -> usize {
298 self.available.len()
299 }
300}
301
302impl<T> Default for ScratchVecPool<T> {
303 fn default() -> Self {
304 Self {
305 available: Vec::new(),
306 }
307 }
308}
309
310#[derive(Default)]
311pub(crate) struct FrameLayoutArena {
312 tmp_child_ids: ScratchVecPool<NodeId>,
313 tmp_placements: ScratchVecPool<Placement>,
314}
315
316#[cfg(test)]
317impl FrameLayoutArena {
318 pub(crate) fn available_placement_scratch_count(&self) -> usize {
319 self.tmp_placements.available_count()
320 }
321
322 pub(crate) fn seed_placement_scratch_for_test(&mut self) {
323 self.tmp_placements.release(Vec::with_capacity(1));
324 }
325}
326
327#[derive(Clone, Debug, PartialEq, Eq)]
329pub struct SemanticsCallback {
330 node_id: NodeId,
331}
332
333impl SemanticsCallback {
334 pub fn new(node_id: NodeId) -> Self {
335 Self { node_id }
336 }
337
338 pub fn node_id(&self) -> NodeId {
339 self.node_id
340 }
341}
342
343#[derive(Clone, Debug, PartialEq, Eq)]
345pub enum SemanticsAction {
346 Click { handler: SemanticsCallback },
347}
348
349#[derive(Clone, Debug)]
352pub struct SemanticsText(Rc<crate::text::AnnotatedString>);
353
354impl SemanticsText {
355 pub fn as_str(&self) -> &str {
357 &self.0.text
358 }
359}
360
361impl PartialEq for SemanticsText {
362 fn eq(&self, other: &Self) -> bool {
363 self.as_str() == other.as_str()
364 }
365}
366
367impl Eq for SemanticsText {}
368
369impl From<&str> for SemanticsText {
370 fn from(text: &str) -> Self {
371 Self(Rc::new(crate::text::AnnotatedString::from(text)))
372 }
373}
374
375#[derive(Clone, Debug, PartialEq, Eq)]
378pub enum SemanticsRole {
379 Layout,
381 Subcompose,
383 Text { value: SemanticsText },
385 Spacer,
387 Button,
389 Unknown,
391}
392
393#[derive(Clone, Debug, PartialEq)]
400pub struct SemanticsNode {
401 pub node_id: NodeId,
402 pub bounds: GeometryRect,
405 pub placement: SemanticsPlacement,
408 pub node_generation: u32,
410 pub role: SemanticsRole,
412 pub widget_role: Option<SemanticsWidgetRole>,
416 pub actions: Vec<SemanticsAction>,
417 pub children: Vec<SemanticsNode>,
418 pub description: Option<String>,
419 pub on_click: Option<SemanticsCustomAction>,
421 pub selected: Option<bool>,
422 pub toggled: Option<bool>,
423 pub enabled: bool,
424 pub hidden: bool,
426 pub merge_descendants: bool,
429 pub traversal_index: f32,
431 pub text: Option<String>,
433 pub focusable: bool,
436 pub focused: bool,
438 pub details: Option<Box<SemanticsDetails>>,
442}
443
444impl Default for SemanticsNode {
445 fn default() -> Self {
446 Self {
447 node_id: 0,
448 bounds: GeometryRect::EMPTY,
449 placement: SemanticsPlacement::default(),
450 node_generation: 0,
451 role: SemanticsRole::Unknown,
452 widget_role: None,
453 actions: Vec::new(),
454 children: Vec::new(),
455 description: None,
456 on_click: None,
457 selected: None,
458 toggled: None,
459 enabled: true,
460 hidden: false,
461 merge_descendants: false,
462 traversal_index: 0.0,
463 text: None,
464 focusable: false,
465 focused: false,
466 details: None,
467 }
468 }
469}
470
471#[derive(Clone, Copy, Debug, Default, PartialEq)]
474pub struct SemanticsPlacement {
475 position: Point,
476 content_offset: Point,
477}
478
479impl SemanticsPlacement {
480 fn of(state: &LayoutState) -> Self {
481 Self {
482 position: state.position(),
483 content_offset: state.content_offset(),
484 }
485 }
486
487 fn place(self, origin: Point) -> (Point, Point) {
488 let top_left = Point {
489 x: origin.x + self.position.x,
490 y: origin.y + self.position.y,
491 };
492 let content = Point {
493 x: top_left.x + self.content_offset.x,
494 y: top_left.y + self.content_offset.y,
495 };
496 (top_left, content)
497 }
498}
499
500#[derive(Clone, Debug)]
505pub struct SemanticsTree {
506 full: SemanticsNode,
507 modal: Option<Vec<usize>>,
508 tracking: semantics_update::SemanticsTracking,
509}
510
511impl PartialEq for SemanticsTree {
512 fn eq(&self, other: &Self) -> bool {
513 self.full == other.full && self.modal == other.modal
514 }
515}
516
517impl SemanticsTree {
518 fn new(full: SemanticsNode) -> Self {
519 let modal = top_modal_path(&full);
520 Self {
521 full,
522 modal,
523 tracking: semantics_update::SemanticsTracking::default(),
524 }
525 }
526
527 pub fn root(&self) -> &SemanticsNode {
529 self.modal.as_deref().map_or(&self.full, |path| {
530 path.iter()
531 .try_fold(&self.full, |node, &index| node.children.get(index))
532 .unwrap_or(&self.full)
533 })
534 }
535}
536
537fn modal_takes_space(size: Size) -> bool {
539 size.width > 0.0 && size.height > 0.0 && size.width.is_finite() && size.height.is_finite()
540}
541
542fn top_modal_path(root: &SemanticsNode) -> Option<Vec<usize>> {
545 fn search(node: &SemanticsNode, path: &mut Vec<usize>) -> bool {
546 if node.hidden {
547 return false;
548 }
549 for (index, child) in node.children.iter().enumerate().rev() {
550 path.push(index);
551 if search(child, path) {
552 return true;
553 }
554 path.pop();
555 }
556 node.details().is_modal
557 }
558 let mut path = Vec::new();
559 search(root, &mut path).then_some(path)
560}
561
562#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
563pub struct LayoutAllocationDebugStats {
564 pub layout_box_count: usize,
565 pub layout_box_child_count: usize,
566 pub layout_box_child_capacity: usize,
567 pub layout_box_heap_bytes: usize,
568 pub modifier_slice_count: usize,
569 pub modifier_slice_heap_bytes: usize,
570 pub modifier_draw_command_count: usize,
571 pub modifier_draw_command_capacity: usize,
572 pub modifier_pointer_input_count: usize,
573 pub modifier_pointer_input_capacity: usize,
574 pub modifier_text_content_count: usize,
575 pub modifier_text_style_count: usize,
576 pub modifier_text_layout_options_count: usize,
577 pub modifier_prepared_text_layout_count: usize,
578 pub modifier_graphics_layer_count: usize,
579 pub modifier_graphics_layer_resolver_count: usize,
580 pub semantics_node_count: usize,
581 pub semantics_action_count: usize,
582 pub semantics_action_capacity: usize,
583 pub semantics_child_count: usize,
584 pub semantics_child_capacity: usize,
585 pub semantics_description_count: usize,
586 pub semantics_description_bytes: usize,
587 pub semantics_heap_bytes: usize,
588}
589
590impl LayoutAllocationDebugStats {
591 fn add_modifier_slice(&mut self, stats: ModifierNodeSlicesDebugStats) {
592 self.modifier_slice_count += 1;
593 self.modifier_slice_heap_bytes += stats.heap_bytes;
594 self.modifier_draw_command_count += stats.draw_command_count;
595 self.modifier_draw_command_capacity += stats.draw_command_capacity;
596 self.modifier_pointer_input_count += stats.pointer_input_count;
597 self.modifier_pointer_input_capacity += stats.pointer_input_capacity;
598 self.modifier_text_content_count += usize::from(stats.has_text_content);
599 self.modifier_text_style_count += usize::from(stats.has_text_style);
600 self.modifier_text_layout_options_count += usize::from(stats.has_text_layout_options);
601 self.modifier_prepared_text_layout_count += usize::from(stats.has_prepared_text_layout);
602 self.modifier_graphics_layer_count += usize::from(stats.has_graphics_layer);
603 self.modifier_graphics_layer_resolver_count +=
604 usize::from(stats.has_graphics_layer_resolver);
605 }
606}
607
608#[derive(Debug, Clone)]
610pub struct LayoutTree {
611 root: LayoutBox,
612}
613
614impl LayoutTree {
615 pub fn new(root: LayoutBox) -> Self {
616 Self { root }
617 }
618
619 pub fn root(&self) -> &LayoutBox {
620 &self.root
621 }
622
623 pub fn root_mut(&mut self) -> &mut LayoutBox {
624 &mut self.root
625 }
626
627 pub fn into_root(self) -> LayoutBox {
628 self.root
629 }
630
631 pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
632 let mut stats = LayoutAllocationDebugStats::default();
633 record_layout_box_allocation_stats(&self.root, &mut stats);
634 stats
635 }
636}
637
638#[derive(Debug, Clone)]
640pub struct LayoutBox {
641 pub node_id: NodeId,
642 pub node_generation: u32,
644 pub rect: GeometryRect,
645 pub content_offset: Point,
647 pub node_data: LayoutNodeData,
648 pub children: Vec<LayoutBox>,
649}
650
651impl LayoutBox {
652 pub fn new(
653 node_id: NodeId,
654 rect: GeometryRect,
655 content_offset: Point,
656 node_data: LayoutNodeData,
657 children: Vec<LayoutBox>,
658 ) -> Self {
659 Self {
660 node_id,
661 node_generation: 0,
662 rect,
663 content_offset,
664 node_data,
665 children,
666 }
667 }
668}
669
670#[derive(Debug, Clone)]
672pub struct LayoutNodeData {
673 #[cfg(feature = "inspection")]
675 pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
676 pub modifier: Modifier,
677 pub resolved_modifiers: ResolvedModifiers,
678 pub modifier_slices: Rc<ModifierNodeSlices>,
679 pub semantics: Option<Rc<SemanticsConfiguration>>,
680 pub kind: LayoutNodeKind,
681}
682
683impl LayoutNodeData {
684 pub fn new(
685 modifier: Modifier,
686 resolved_modifiers: ResolvedModifiers,
687 modifier_slices: Rc<ModifierNodeSlices>,
688 semantics: Option<Rc<SemanticsConfiguration>>,
689 kind: LayoutNodeKind,
690 ) -> Self {
691 Self {
692 #[cfg(feature = "inspection")]
693 source_trace: Rc::default(),
694 modifier,
695 resolved_modifiers,
696 modifier_slices,
697 semantics,
698 kind,
699 }
700 }
701
702 pub fn semantics(&self) -> Option<&SemanticsConfiguration> {
705 self.semantics.as_deref()
706 }
707
708 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
709 self.resolved_modifiers
710 }
711
712 pub fn modifier_slices(&self) -> &ModifierNodeSlices {
713 &self.modifier_slices
714 }
715}
716
717#[derive(Clone)]
724pub enum LayoutNodeKind {
725 Layout,
726 Subcompose,
727 Spacer,
728 Button { on_click: Rc<RefCell<dyn FnMut()>> },
729 Unknown,
730}
731
732impl fmt::Debug for LayoutNodeKind {
733 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
734 match self {
735 LayoutNodeKind::Layout => f.write_str("Layout"),
736 LayoutNodeKind::Subcompose => f.write_str("Subcompose"),
737 LayoutNodeKind::Spacer => f.write_str("Spacer"),
738 LayoutNodeKind::Button { .. } => f.write_str("Button"),
739 LayoutNodeKind::Unknown => f.write_str("Unknown"),
740 }
741 }
742}
743
744pub trait LayoutEngine {
746 fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError>;
747}
748
749impl LayoutEngine for MemoryApplier {
750 fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError> {
751 let measurements = measure_layout(self, root, max_size)?;
752 measurements
753 .into_layout_tree()
754 .ok_or(NodeError::MissingContext {
755 id: root,
756 reason: "layout tree was not requested",
757 })
758 }
759}
760
761#[derive(Debug, Clone)]
763pub struct LayoutMeasurements {
764 root: Rc<MeasuredNode>,
765 semantics: Option<SemanticsTree>,
766 layout_tree: Option<LayoutTree>,
767}
768
769impl LayoutMeasurements {
770 fn new(
771 root: Rc<MeasuredNode>,
772 semantics: Option<SemanticsTree>,
773 layout_tree: Option<LayoutTree>,
774 ) -> Self {
775 Self {
776 root,
777 semantics,
778 layout_tree,
779 }
780 }
781
782 pub fn root_size(&self) -> Size {
784 self.root.size
785 }
786
787 pub fn semantics_tree(&self) -> Option<&SemanticsTree> {
788 self.semantics.as_ref()
789 }
790
791 pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
792 let mut stats = self
793 .layout_tree
794 .as_ref()
795 .map(LayoutTree::debug_allocation_stats)
796 .unwrap_or_default();
797 if let Some(semantics) = &self.semantics {
798 record_semantics_allocation_stats(semantics.root(), &mut stats);
799 }
800 stats
801 }
802
803 pub fn into_layout_tree(self) -> Option<LayoutTree> {
805 self.layout_tree
806 }
807
808 pub fn layout_tree(&self) -> Option<LayoutTree> {
810 self.layout_tree.clone()
811 }
812}
813
814pub fn build_semantics_tree_from_layout_tree(layout_tree: &LayoutTree) -> SemanticsTree {
819 SemanticsTree::new(build_semantics_node_from_layout_box(
820 layout_tree.root(),
821 Point::default(),
822 ))
823}
824
825pub fn build_layout_tree_from_applier(
831 applier: &mut MemoryApplier,
832 root: NodeId,
833) -> Result<Option<LayoutTree>, NodeError> {
834 let origin = layout_tree_origin(read_layout_node(applier, root, |state, _| state)?);
835 let mut child_stack = Vec::new();
836 place_layout_box(
837 applier,
838 root,
839 origin,
840 ProjectiveTransform::identity(),
841 &mut child_stack,
842 )
843 .map(|root| root.map(LayoutTree::new))
844}
845
846pub fn has_placed_content(applier: &mut MemoryApplier, root: NodeId) -> Result<bool, NodeError> {
850 walk_placed_boxes(applier, root, |_| std::ops::ControlFlow::Break(()))
851}
852
853pub fn placed_content_extent(
858 applier: &mut MemoryApplier,
859 root: NodeId,
860) -> Result<Option<Size>, NodeError> {
861 let mut extent: Option<Size> = None;
862 walk_placed_boxes(applier, root, |rect| {
863 let (right, bottom) = (rect.x + rect.width, rect.y + rect.height);
864 extent = Some(extent.map_or_else(
865 || Size::new(right, bottom),
866 |extent| Size::new(extent.width.max(right), extent.height.max(bottom)),
867 ));
868 std::ops::ControlFlow::Continue(())
869 })?;
870 Ok(extent)
871}
872
873fn walk_placed_boxes(
878 applier: &mut MemoryApplier,
879 root: NodeId,
880 mut visit: impl FnMut(GeometryRect) -> std::ops::ControlFlow<()>,
881) -> Result<bool, NodeError> {
882 let mut pending: Vec<(NodeId, Point)> = Vec::new();
883 let placed = read_layout_node(applier, root, |state, children| {
884 if state.is_placed() {
885 pending.extend(
886 children
887 .iter()
888 .map(|&child| (child, state.content_offset())),
889 );
890 }
891 })?;
892 if placed.is_none() {
893 return Ok(false);
894 }
895 while let Some((node_id, origin)) = pending.pop() {
896 if crate::modifier::is_window_root(applier, node_id) {
897 continue;
898 }
899 let rect = read_layout_node(applier, node_id, |state, children| {
900 if !state.is_placed() {
901 return None;
902 }
903 let (rect, content) = semantics_placement(&state, Some(origin));
904 pending.extend(children.iter().map(|&child| (child, content)));
905 Some(rect)
906 })?
907 .flatten();
908 if let Some(rect) = rect
909 && rect.width > 0.0
910 && rect.height > 0.0
911 && visit(rect).is_break()
912 {
913 return Ok(true);
914 }
915 }
916 Ok(false)
917}
918
919fn layout_tree_origin(root: Option<LayoutState>) -> Point {
920 let Some(state) = root else {
921 return Point::default();
922 };
923 let position = state.position();
924 Point {
925 x: -position.x,
926 y: -position.y,
927 }
928}
929
930fn finish_placement(applier: &mut MemoryApplier, root: NodeId) {
933 if applier
934 .with_node::<LayoutNode, _>(root, |node| node.set_position(Point::default()))
935 .is_err()
936 {
937 let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
938 node.set_position(Point::default());
939 });
940 }
941 if let Some(pass) = placement_pass_with_unplaced_nodes() {
942 record_unplaced_parents(applier, root, pass);
943 }
944}
945
946fn record_unplaced_parents(applier: &mut MemoryApplier, root: NodeId, pass: u64) {
951 let mut parents = vec![root];
952 let mut children = Vec::new();
953 while let Some(parent) = parents.pop() {
954 children.clear();
955 if !matches!(
956 read_layout_node(applier, parent, |_, ids| children.extend_from_slice(ids)),
957 Ok(Some(()))
958 ) {
959 continue;
960 }
961 for &child in &children {
962 match read_layout_node(applier, child, |state, _| state.unplaced_in(pass)) {
963 Ok(Some(true)) => crate::render_state::record_geometry_scene_node(parent),
964 Ok(Some(false)) => parents.push(child),
965 Ok(None) | Err(_) => {}
966 }
967 }
968 }
969}
970
971fn read_layout_node<R>(
972 applier: &mut MemoryApplier,
973 node_id: NodeId,
974 mut read: impl FnMut(LayoutState, &[NodeId]) -> R,
975) -> Result<Option<R>, NodeError> {
976 read_live_layout_node(applier, node_id, |node| {
977 node.with_children(|children| read(node.state(), children))
978 })
979}
980
981enum LiveLayoutNode<'a> {
982 Layout(&'a LayoutNode),
983 Subcompose(&'a SubcomposeLayoutNode),
984}
985
986impl LiveLayoutNode<'_> {
987 fn state(&self) -> LayoutState {
988 match self {
989 Self::Layout(node) => node.layout_state(),
990 Self::Subcompose(node) => node.layout_state(),
991 }
992 }
993
994 fn parent(&self) -> Option<NodeId> {
995 match self {
996 Self::Layout(node) => node.parent(),
997 Self::Subcompose(node) => node.parent(),
998 }
999 }
1000
1001 fn is_window_root(&self) -> bool {
1002 matches!(self, Self::Layout(node) if node.is_window_root())
1003 }
1004
1005 fn with_children<R>(&self, read: impl FnOnce(&[NodeId]) -> R) -> R {
1006 match self {
1007 Self::Layout(node) => read(&node.children),
1008 Self::Subcompose(node) => node.with_active_children(read),
1009 }
1010 }
1011
1012 fn semantics(&self) -> Option<SemanticsConfiguration> {
1013 match self {
1014 Self::Layout(node) => node.semantics_configuration(),
1015 Self::Subcompose(node) => node.semantics_configuration(),
1016 }
1017 }
1018}
1019
1020fn read_live_layout_node<R>(
1021 applier: &mut MemoryApplier,
1022 node_id: NodeId,
1023 mut read: impl FnMut(LiveLayoutNode<'_>) -> R,
1024) -> Result<Option<R>, NodeError> {
1025 match applier.with_node::<LayoutNode, _>(node_id, |node| read(LiveLayoutNode::Layout(node))) {
1026 Ok(value) => return Ok(Some(value)),
1027 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {}
1028 Err(err) => return Err(err),
1029 }
1030 match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1031 read(LiveLayoutNode::Subcompose(node))
1032 }) {
1033 Ok(value) => Ok(Some(value)),
1034 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1035 Err(err) => Err(err),
1036 }
1037}
1038
1039fn snapshot_node_data(
1040 applier: &mut MemoryApplier,
1041 node_id: NodeId,
1042 top_left: Point,
1043 size: Size,
1044 parent_transform: ProjectiveTransform,
1045) -> Result<(LayoutNodeData, ProjectiveTransform), NodeError> {
1046 let info = runtime_metadata_for(applier, node_id)?;
1047 let kind = layout_kind_from_metadata(node_id, &info);
1048 let RuntimeNodeMetadata {
1049 modifier,
1050 resolved_modifiers,
1051 modifier_slices,
1052 semantics,
1053 ..
1054 } = info;
1055
1056 let window_transform = modifier_slices
1057 .graphics_layer()
1058 .map_or(parent_transform, |layer| {
1059 layer_transform_to_window(
1060 parent_transform,
1061 top_left,
1062 modifier_slices.layer_bounds(size),
1063 &layer,
1064 )
1065 });
1066 modifier_slices.publish_window_geometry(top_left, window_transform, size);
1067
1068 let data = LayoutNodeData {
1069 #[cfg(feature = "inspection")]
1070 source_trace: applier
1071 .with_node::<LayoutNode, _>(node_id, |node| node.source_trace.clone())
1072 .unwrap_or_default(),
1073 modifier,
1074 resolved_modifiers,
1075 modifier_slices,
1076 semantics,
1077 kind,
1078 };
1079 Ok((data, window_transform))
1080}
1081
1082fn place_layout_box(
1087 applier: &mut MemoryApplier,
1088 node_id: NodeId,
1089 parent_content_origin: Point,
1090 parent_transform: ProjectiveTransform,
1091 child_stack: &mut Vec<NodeId>,
1092) -> Result<Option<LayoutBox>, NodeError> {
1093 let first_child = child_stack.len();
1094 let Some(state) = read_layout_node(applier, node_id, |state, children| {
1095 if state.is_placed() {
1096 child_stack.extend_from_slice(children);
1097 }
1098 state
1099 })?
1100 else {
1101 return Ok(None);
1102 };
1103 if !state.is_placed() {
1104 return Ok(None);
1105 }
1106
1107 let top_left = Point {
1108 x: parent_content_origin.x + state.position().x,
1109 y: parent_content_origin.y + state.position().y,
1110 };
1111 let rect = GeometryRect {
1112 x: top_left.x,
1113 y: top_left.y,
1114 width: state.size().width,
1115 height: state.size().height,
1116 };
1117 let (data, window_transform) =
1118 snapshot_node_data(applier, node_id, top_left, state.size(), parent_transform)?;
1119 let child_origin = Point {
1120 x: top_left.x + state.content_offset().x,
1121 y: top_left.y + state.content_offset().y,
1122 };
1123 let end = child_stack.len();
1124 let mut children = Vec::with_capacity(end - first_child);
1125 for index in first_child..end {
1126 let child_id = child_stack[index];
1127 if crate::modifier::is_window_root(applier, child_id) {
1128 continue;
1129 }
1130 if let Some(child) = place_layout_box(
1131 applier,
1132 child_id,
1133 child_origin,
1134 window_transform,
1135 child_stack,
1136 )? {
1137 children.push(child);
1138 }
1139 }
1140 child_stack.truncate(first_child);
1141
1142 Ok(Some(LayoutBox {
1143 node_generation: applier.node_generation(node_id),
1144 ..LayoutBox::new(node_id, rect, state.content_offset(), data, children)
1145 }))
1146}
1147
1148pub fn top_modal_from_applier(
1153 applier: &mut MemoryApplier,
1154 root: NodeId,
1155) -> Result<Option<NodeId>, NodeError> {
1156 enum Step {
1161 Enter { node: NodeId, child: bool },
1162 Modal(NodeId),
1163 }
1164
1165 fn push_placed(
1166 steps: &mut Vec<Step>,
1167 node: NodeId,
1168 reach: cranpose_foundation::SemanticsReach,
1169 size: Size,
1170 children: impl IntoIterator<Item = NodeId>,
1171 ) {
1172 if reach.hidden {
1173 return;
1174 }
1175 if reach.is_modal && modal_takes_space(size) {
1176 steps.push(Step::Modal(node));
1177 }
1178 steps.extend(
1179 children
1180 .into_iter()
1181 .map(|node| Step::Enter { node, child: true }),
1182 );
1183 }
1184
1185 let mut steps = vec![Step::Enter {
1186 node: root,
1187 child: false,
1188 }];
1189 while let Some(step) = steps.pop() {
1190 let (node_id, child) = match step {
1191 Step::Modal(node) => return Ok(Some(node)),
1192 Step::Enter { node, child } => (node, child),
1193 };
1194 let node = match applier.get_mut(node_id) {
1195 Ok(node) => node.as_any_mut(),
1196 Err(NodeError::Missing { .. }) => continue,
1197 Err(error) => return Err(error),
1198 };
1199 if let Some(layout) = node.downcast_mut::<LayoutNode>() {
1200 let state = layout.layout_state();
1201 if state.is_placed() && !(child && layout.is_window_root()) {
1202 let children = layout.children.iter().copied();
1203 push_placed(
1204 &mut steps,
1205 node_id,
1206 layout.semantics_reach(),
1207 state.size(),
1208 children,
1209 );
1210 }
1211 } else if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
1212 let state = subcompose.layout_state();
1213 if state.is_placed() {
1214 let reach = subcompose.semantics_reach();
1215 subcompose.with_active_children(|children| {
1216 push_placed(
1217 &mut steps,
1218 node_id,
1219 reach,
1220 state.size(),
1221 children.iter().copied(),
1222 );
1223 });
1224 }
1225 }
1226 }
1227 Ok(None)
1228}
1229
1230#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1231pub struct MeasureLayoutOptions {
1232 pub collect_semantics: bool,
1233 pub build_layout_tree: bool,
1234}
1235
1236impl Default for MeasureLayoutOptions {
1237 fn default() -> Self {
1238 Self {
1239 collect_semantics: true,
1240 build_layout_tree: true,
1241 }
1242 }
1243}
1244
1245pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1253 Ok(applier.get_mut(root)?.layout_dirty())
1254}
1255
1256pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1262 Ok(applier.get_mut(root)?.needs_semantics())
1263}
1264
1265#[cfg(test)]
1266pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
1267 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1268}
1269
1270pub fn measure_layout(
1272 applier: &mut MemoryApplier,
1273 root: NodeId,
1274 max_size: Size,
1275) -> Result<LayoutMeasurements, NodeError> {
1276 measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
1277}
1278
1279pub fn measure_layout_with_options(
1280 applier: &mut MemoryApplier,
1281 root: NodeId,
1282 max_size: Size,
1283 options: MeasureLayoutOptions,
1284) -> Result<LayoutMeasurements, NodeError> {
1285 let telemetry_start = Instant::now();
1286 crate::render_state::begin_text_layout_pass();
1287 begin_placement_pass();
1288 process_pending_layout_repasses(applier, root)?;
1289 let after_repasses = Instant::now();
1290
1291 let constraints = Constraints {
1292 min_width: 0.0,
1293 max_width: max_size.width,
1294 min_height: 0.0,
1295 max_height: max_size.height,
1296 };
1297
1298 let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
1299 .with_node::<LayoutNode, _>(root, |node| {
1300 (
1301 node.needs_measure() || Node::descendant_needs_measure(node),
1302 node.needs_semantics(),
1303 node.cache_handles().epoch(),
1304 )
1305 }) {
1306 Ok(tuple) => tuple,
1307 Err(NodeError::TypeMismatch { .. }) => {
1308 let node = applier.get_mut(root)?;
1309 let measure_dirty = node.needs_measure() || node.descendant_needs_measure();
1310 let semantics_dirty = node.needs_semantics();
1311 (measure_dirty, semantics_dirty, 0)
1312 }
1313 Err(err) => return Err(err),
1314 };
1315
1316 let epoch = if needs_remeasure {
1317 crate::render_state::next_layout_cache_epoch()
1318 } else if cached_epoch != 0 {
1319 cached_epoch
1320 } else {
1321 crate::render_state::current_layout_cache_epoch()
1322 };
1323
1324 let guard = ApplierSlotGuard::new(applier);
1325 let applier_host = guard.host();
1326 let slots_handle = guard.slots_handle();
1327 let after_guard = Instant::now();
1328
1329 let frame_arena = crate::render_state::take_layout_frame_arena();
1330 let builder = LayoutBuilder::new_with_epoch(
1331 Rc::clone(&applier_host),
1332 epoch,
1333 Rc::clone(&slots_handle),
1334 frame_arena,
1335 );
1336 let after_builder = Instant::now();
1337
1338 let measured = builder
1339 .state
1340 .measure_node(root, normalize_constraints(constraints))?;
1341 let after_measure = Instant::now();
1342
1343 if let Ok(mut applier) = applier_host.try_borrow_typed() {
1344 finish_placement(&mut applier, root);
1345 }
1346 let after_root_place = Instant::now();
1347
1348 let (layout_tree, semantics) = {
1349 let mut applier_ref = applier_host.borrow_typed();
1350 let layout_tree = if options.build_layout_tree {
1351 Some(build_layout_tree(&mut applier_ref, &measured)?)
1352 } else {
1353 None
1354 };
1355 let semantics = if options.collect_semantics {
1356 let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
1357 clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
1358 build_semantics_tree_from_layout_tree(layout_tree)
1359 } else {
1360 build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
1361 };
1362 Some(semantics_tree)
1363 } else {
1364 None
1365 };
1366 (layout_tree, semantics)
1367 };
1368 let after_aux = Instant::now();
1369
1370 drop(builder);
1371 let after_builder_drop = Instant::now();
1372
1373 drop(guard);
1374 let after_guard_drop = Instant::now();
1375
1376 log_layout_measure_telemetry(LayoutMeasureTelemetry {
1377 root,
1378 start: telemetry_start,
1379 after_repasses,
1380 after_guard,
1381 after_builder,
1382 after_measure,
1383 after_root_place,
1384 after_aux,
1385 after_builder_drop,
1386 after_guard_drop,
1387 });
1388
1389 Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
1390}
1391
1392fn process_pending_layout_repasses(
1393 applier: &mut MemoryApplier,
1394 root: NodeId,
1395) -> Result<(), NodeError> {
1396 for node_id in crate::render_state::take_modifier_slice_repass_nodes() {
1397 if let Ok(node) = applier.get_mut(node_id) {
1398 let any = node.as_any_mut();
1399 if let Some(layout) = any.downcast_mut::<crate::widgets::nodes::LayoutNode>() {
1400 layout.mark_modifier_slices_dirty();
1401 } else if let Some(subcompose) =
1402 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1403 {
1404 subcompose.mark_modifier_slices_dirty();
1405 }
1406 }
1407 }
1408 let measure_repass_nodes = crate::take_measure_repass_nodes();
1409 let repass_nodes = crate::take_layout_repass_nodes();
1410 if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
1411 return Ok(());
1412 }
1413 for node_id in measure_repass_nodes {
1414 cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
1415 }
1416 for node_id in repass_nodes {
1417 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1418 }
1419 applier.get_mut(root)?.mark_descendant_needs_layout(false);
1420 Ok(())
1421}
1422
1423struct LayoutBuilder {
1424 state: Rc<LayoutBuilderState>,
1425}
1426
1427impl LayoutBuilder {
1428 fn new_with_epoch(
1429 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1430 epoch: u64,
1431 slots: Rc<RefCell<SlotTable>>,
1432 frame_arena: FrameLayoutArena,
1433 ) -> Self {
1434 Self {
1435 state: Rc::new(LayoutBuilderState::new_with_epoch(
1436 applier,
1437 epoch,
1438 slots,
1439 frame_arena,
1440 )),
1441 }
1442 }
1443}
1444
1445impl Drop for LayoutBuilder {
1446 fn drop(&mut self) {
1447 if Rc::strong_count(&self.state) != 1 {
1448 return;
1449 }
1450 let Ok(mut frame_arena) = self.state.frame_arena.try_borrow_mut() else {
1451 return;
1452 };
1453 crate::render_state::replace_layout_frame_arena(std::mem::take(&mut *frame_arena));
1454 }
1455}
1456
1457struct LayoutBuilderState {
1458 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1459 runtime_handle: RefCell<Option<RuntimeHandle>>,
1460 slots: Rc<RefCell<SlotTable>>,
1461 cache_epoch: u64,
1462 cache_floor: u64,
1463 frame_arena: RefCell<FrameLayoutArena>,
1464}
1465
1466struct LayoutRuntimeFrameBindingCleanup<'a> {
1467 state: &'a RefCell<LayoutRuntimeState>,
1468}
1469
1470impl Drop for LayoutRuntimeFrameBindingCleanup<'_> {
1471 fn drop(&mut self) {
1472 self.state.borrow().unbind_children();
1473 }
1474}
1475
1476enum LayoutNodeVisit<'a> {
1477 Cached(Rc<MeasuredNode>),
1478 Keep(Rc<MeasuredNode>),
1481 Measure(LayoutNodeMeasure<'a>),
1482}
1483
1484#[derive(Clone, Copy)]
1486struct ChangedChild {
1487 constraints: Constraints,
1488 placed: bool,
1489}
1490
1491enum ChildChange {
1493 Unchanged,
1494 Changed(ChangedChild),
1495 Refuse,
1497}
1498
1499struct ChildView {
1502 self_dirty: bool,
1503 parent_data: Option<cranpose_ui_layout::ParentData>,
1506 cached_constraints: Option<Constraints>,
1508 read_intrinsics: bool,
1509 placed: bool,
1510}
1511
1512impl ChildView {
1513 fn of(
1515 node: &dyn Node,
1516 cache: &LayoutNodeCacheHandles,
1517 props: &dyn Fn() -> crate::modifier::LayoutProperties,
1518 placed: bool,
1519 ) -> Option<Self> {
1520 let self_dirty = node.needs_measure() || node.needs_layout();
1521 if !self_dirty && !node.descendant_needs_layout() {
1522 return None;
1523 }
1524 Some(Self {
1525 self_dirty,
1526 parent_data: self_dirty.then(|| parent_data_of(props())),
1527 cached_constraints: cache.measured_constraints(),
1528 read_intrinsics: cache.has_intrinsics(),
1529 placed,
1530 })
1531 }
1532}
1533
1534struct LayoutNodeMeasure<'a> {
1535 runtime_state: Rc<RefCell<LayoutRuntimeState>>,
1536 chain: ModifierChainInputs,
1537 pools: VecPools<'a>,
1538}
1539
1540impl LayoutBuilderState {
1541 fn new_with_epoch(
1542 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1543 epoch: u64,
1544 slots: Rc<RefCell<SlotTable>>,
1545 frame_arena: FrameLayoutArena,
1546 ) -> Self {
1547 let runtime_handle = applier.borrow_typed().runtime_handle();
1548
1549 Self {
1550 applier,
1551 runtime_handle: RefCell::new(runtime_handle),
1552 slots,
1553 cache_epoch: epoch,
1554 cache_floor: crate::render_state::layout_cache_floor(),
1555 frame_arena: RefCell::new(frame_arena),
1556 }
1557 }
1558
1559 fn with_applier_result<R>(
1560 &self,
1561 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1562 ) -> Result<R, NodeError> {
1563 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1564 return Err(NodeError::MissingContext {
1565 id: NodeId::default(),
1566 reason: "applier already borrowed",
1567 });
1568 };
1569 f(&mut applier)
1570 }
1571
1572 fn measure_subcompose_node(
1573 self: &Rc<Self>,
1574 node_id: NodeId,
1575 constraints: Constraints,
1576 ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1577 self.clear_subcompose_placed(node_id);
1578 if let Some(cached) = self.subcompose_measurement_to_keep(node_id, constraints)
1579 && let Some(kept) = self.keep_subcompose_measurement(node_id, constraints, &cached)?
1580 {
1581 return Ok(("subcompose kept", kept));
1582 }
1583 Ok(match self.try_measure_subcompose(node_id, constraints)? {
1584 Some(measured) => {
1585 self.store_subcompose_measurement(node_id, constraints, &measured);
1586 ("subcompose", measured)
1587 }
1588 None => (
1589 "fallback",
1590 Rc::new(MeasuredNode::new(
1591 node_id,
1592 Size::default(),
1593 Point { x: 0.0, y: 0.0 },
1594 Point::default(),
1595 Vec::new(),
1596 )),
1597 ),
1598 })
1599 }
1600
1601 fn subcompose_measurement_to_keep(
1605 &self,
1606 node_id: NodeId,
1607 constraints: Constraints,
1608 ) -> Option<Rc<MeasuredNode>> {
1609 let mut applier = self.applier.try_borrow_typed().ok()?;
1610 applier
1611 .with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1612 if node.needs_measure() || Node::needs_layout(node) {
1613 return None;
1614 }
1615 let cache = node.cache_handles();
1616 if cache.epoch() < self.cache_floor {
1617 return None;
1618 }
1619 cache.get_measurement(constraints)
1620 })
1621 .ok()
1622 .flatten()
1623 }
1624
1625 fn keep_subcompose_measurement(
1631 self: &Rc<Self>,
1632 node_id: NodeId,
1633 constraints: Constraints,
1634 cached: &Rc<MeasuredNode>,
1635 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1636 let kept = self.remeasure_changed_children(
1637 cached,
1638 |child_id| self.subcompose_child_change(child_id),
1639 |child_id| self.mark_measured_without_policy(child_id),
1640 )?;
1641 let Some(kept) = kept else {
1642 return Ok(None);
1643 };
1644 self.with_applier_result(|applier| {
1645 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1646 node.cache_handles()
1647 .store_measurement(constraints, Rc::clone(&kept));
1648 node.clear_needs_layout();
1649 })
1650 })?;
1651 Ok(Some(kept))
1652 }
1653
1654 fn subcompose_child_change(&self, child_id: NodeId) -> Result<ChildChange, NodeError> {
1658 self.child_change(child_id, |view| {
1659 match view.cached_constraints.filter(|_| !view.self_dirty) {
1660 Some(constraints) if !view.read_intrinsics => ChildChange::Changed(ChangedChild {
1661 constraints,
1662 placed: view.placed,
1663 }),
1664 _ => ChildChange::Refuse,
1665 }
1666 })
1667 }
1668
1669 fn child_change(
1672 &self,
1673 child_id: NodeId,
1674 decide: impl FnOnce(ChildView) -> ChildChange,
1675 ) -> Result<ChildChange, NodeError> {
1676 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1677 return Ok(ChildChange::Refuse);
1678 };
1679 Ok(
1680 match read_layout_child(&mut applier, child_id, ChildView::of)? {
1681 None => ChildChange::Refuse,
1682 Some(None) => ChildChange::Unchanged,
1683 Some(Some(view)) => decide(view),
1684 },
1685 )
1686 }
1687
1688 fn mark_measured_without_policy(&self, child_id: NodeId) {
1692 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1693 return;
1694 };
1695 if let Ok(node) = applier.get_mut(child_id) {
1696 node.mark_descendant_needs_layout(true);
1697 }
1698 }
1699
1700 fn clear_subcompose_placed(&self, node_id: NodeId) {
1701 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1702 return;
1703 };
1704 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1705 node.clear_placed();
1706 });
1707 }
1708
1709 fn measure_node(
1710 self: &Rc<Self>,
1711 node_id: NodeId,
1712 constraints: Constraints,
1713 ) -> Result<Rc<MeasuredNode>, NodeError> {
1714 let telemetry = layout_measure_telemetry_threshold_ms().map(|ms| (ms, Instant::now()));
1715 let (kind, measured) =
1716 if let Some(measured) = self.measure_layout_node(node_id, constraints)? {
1717 ("layout", measured)
1718 } else {
1719 self.measure_subcompose_node(node_id, constraints)?
1720 };
1721 if let Some(telemetry) = telemetry {
1722 log_node_measure_telemetry(kind, node_id, constraints, &measured, telemetry);
1723 }
1724 Ok(measured)
1725 }
1726
1727 fn cached_measure_node_with_applier(
1730 applier: &mut MemoryApplier,
1731 node_id: NodeId,
1732 constraints: Constraints,
1733 current_epoch: u64,
1734 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1735 let served = |cache: &LayoutNodeCacheHandles, dirty: bool| {
1736 let epoch = cache.epoch();
1737 if dirty || epoch == 0 || epoch != current_epoch {
1738 return None;
1739 }
1740 cache.get_measurement(constraints)
1741 };
1742
1743 match applier.with_node::<LayoutNode, _>(node_id, |node| {
1744 let measured = served(node.cache_handles(), Node::layout_dirty(node))?;
1745 node.set_measured_size(measured.size);
1746 Some(measured)
1747 }) {
1748 Ok(measured) => Ok(measured),
1749 Err(NodeError::TypeMismatch { .. }) => {
1750 match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1751 let measured = served(node.cache_handles(), Node::layout_dirty(node))?;
1752 node.set_measured_size(measured.size);
1753 Some(measured)
1754 }) {
1755 Ok(measured) => Ok(measured),
1756 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1757 Err(err) => Err(err),
1758 }
1759 }
1760 Err(NodeError::Missing { .. }) => Ok(None),
1761 Err(err) => Err(err),
1762 }
1763 }
1764
1765 fn try_measure_subcompose(
1766 self: &Rc<Self>,
1767 node_id: NodeId,
1768 constraints: Constraints,
1769 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1770 let (node_handle, resolved_modifiers) = {
1771 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1772 return Ok(None);
1773 };
1774 let node = match applier.get_mut(node_id) {
1775 Ok(node) => node,
1776 Err(NodeError::Missing { .. }) => return Ok(None),
1777 Err(err) => return Err(err),
1778 };
1779 let any = node.as_any_mut();
1780 if let Some(subcompose) =
1781 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1782 {
1783 let handle = subcompose.handle();
1784 let resolved_modifiers = handle
1785 .resolved_modifiers()
1786 .on_device_grid(subcompose.density().density());
1787 (handle, resolved_modifiers)
1788 } else {
1789 return Ok(None);
1790 }
1791 };
1792
1793 let runtime_handle = {
1794 let mut runtime_handle = self.runtime_handle.borrow_mut();
1795 if runtime_handle.is_none()
1796 && let Ok(applier) = self.applier.try_borrow_typed()
1797 {
1798 *runtime_handle = applier.runtime_handle();
1799 }
1800 runtime_handle.clone().ok_or(NodeError::MissingContext {
1801 id: node_id,
1802 reason: "runtime handle required for subcomposition",
1803 })?
1804 };
1805
1806 let props = resolved_modifiers.layout_properties();
1807 let padding = resolved_modifiers.padding();
1808 let offset = resolved_modifiers.offset();
1809 let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1810
1811 if let DimensionConstraint::Points(width) = props.width() {
1812 let constrained_width = width - padding.horizontal_sum();
1813 inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1814 inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1815 }
1816 if let DimensionConstraint::Points(height) = props.height() {
1817 let constrained_height = height - padding.vertical_sum();
1818 inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1819 inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1820 }
1821
1822 let mut slots_guard = SlotsGuard::take(&self.slots);
1823 let slots_host = slots_guard.host();
1824 let applier_host_dyn: Rc<dyn ApplierHost> = Rc::clone(&self.applier) as Rc<dyn ApplierHost>;
1825 let composer = Composer::new(
1826 Rc::clone(&slots_host),
1827 applier_host_dyn,
1828 runtime_handle,
1829 Some(node_id),
1830 );
1831 composer.enter_phase(Phase::Measure);
1832
1833 let measure_error = RefCell::new(None);
1834 let measured_children = node_handle.measured_children_scratch();
1835
1836 let measure_result = node_handle.measure_with_cached_batch(
1837 &composer,
1838 node_id,
1839 inner_constraints,
1840 CachedBatchMeasureInputs {
1841 measurer: Box::new(|child_id: NodeId, child_constraints: Constraints| {
1842 match self.measure_node(child_id, child_constraints) {
1843 Ok(measured) => {
1844 measured_children
1845 .borrow_mut()
1846 .insert(child_id, Rc::clone(&measured));
1847 let size = measured.size_for_parent();
1848 Placeable::value(size.width, size.height, child_id)
1849 .with_alignment_lines(measured.alignment_lines_for_parent())
1850 }
1851 Err(err) => {
1852 let mut slot = measure_error.borrow_mut();
1853 if slot.is_none() {
1854 *slot = Some(err);
1855 }
1856 Placeable::value(0.0, 0.0, child_id)
1857 }
1858 }
1859 }),
1860 cached_measure_batch_registrar: Box::new(
1861 |child_ids: &[NodeId],
1862 child_constraints: Constraints,
1863 out: &mut Vec<Option<Size>>| {
1864 out.clear();
1865 out.resize(child_ids.len(), None);
1866
1867 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1868 return;
1869 };
1870
1871 let mut measured_children = measured_children.borrow_mut();
1872 let current_epoch = crate::render_state::current_layout_cache_epoch();
1873 for (index, &child_id) in child_ids.iter().enumerate() {
1874 match Self::cached_measure_node_with_applier(
1875 &mut applier,
1876 child_id,
1877 child_constraints,
1878 current_epoch,
1879 ) {
1880 Ok(Some(measured)) => {
1881 out[index] = Some(measured.size);
1882 measured_children.insert(child_id, Rc::clone(&measured));
1883 }
1884 Ok(None) => {}
1885 Err(err) => {
1886 let mut slot = measure_error.borrow_mut();
1887 if slot.is_none() {
1888 *slot = Some(err);
1889 }
1890 break;
1891 }
1892 }
1893 }
1894 },
1895 ),
1896 retained_measure_lookup: Box::new(|child_id| {
1897 measured_children.borrow().get(&child_id).cloned()
1898 }),
1899 retained_measure_registrar: Box::new(|measurements| {
1900 let mut measured_children = measured_children.borrow_mut();
1901 for measured in measurements {
1902 measured_children.insert(measured.node_id(), Rc::clone(measured));
1903 }
1904 }),
1905 error: &measure_error,
1906 },
1907 )?;
1908 drop(composer);
1909 slots_guard.restore(slots_host.into_table()?);
1910
1911 if let Some(err) = measure_error.borrow_mut().take() {
1912 return Err(err);
1913 }
1914
1915 let cranpose_ui_layout::MeasureResult {
1916 size: measured_size,
1917 alignment_lines: explicit_lines,
1918 placements,
1919 } = measure_result;
1920
1921 let mut width = measured_size.width + padding.horizontal_sum();
1922 let mut height = measured_size.height + padding.vertical_sum();
1923
1924 width = resolve_dimension(
1925 width,
1926 props.width(),
1927 props.min_width(),
1928 props.max_width(),
1929 constraints.min_width,
1930 constraints.max_width,
1931 );
1932 height = resolve_dimension(
1933 height,
1934 props.height(),
1935 props.min_height(),
1936 props.max_height(),
1937 constraints.min_height,
1938 constraints.max_height,
1939 );
1940
1941 let mut children = Vec::with_capacity(placements.len());
1942 let mut alignment_lines = AlignmentLines::default();
1943 let mut measured_children_by_id = measured_children.borrow_mut();
1944
1945 if let Ok(mut applier) = self.applier.try_borrow_typed() {
1946 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1947 parent_node.set_measured_size(Size { width, height });
1948 parent_node.clear_needs_measure();
1949 parent_node.clear_needs_layout();
1950 });
1951 }
1952
1953 for placement in &placements {
1954 let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1955 measured
1956 } else {
1957 self.measure_node(placement.node_id, inner_constraints)?
1958 };
1959 let policy_position = Point {
1960 x: padding.left + placement.x,
1961 y: padding.top + placement.y,
1962 };
1963 let retained_position = Point {
1964 x: policy_position.x + child.offset.x,
1965 y: policy_position.y + child.offset.y,
1966 };
1967 alignment_lines.merge(
1968 child
1969 .alignment_lines_for_parent()
1970 .translated(policy_position.y),
1971 );
1972
1973 if let Ok(mut applier) = self.applier.try_borrow_typed()
1974 && applier
1975 .with_node::<LayoutNode, _>(placement.node_id, |node| {
1976 node.set_position(retained_position);
1977 })
1978 .is_err()
1979 {
1980 let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1981 node.set_position(retained_position);
1982 });
1983 }
1984
1985 children.push(MeasuredChild {
1986 node: RefCell::new(child),
1987 offset: policy_position,
1988 });
1989 }
1990
1991 node_handle.set_active_children(children.iter().map(|c| c.node.borrow().node_id));
1992 node_handle.recycle_placement_scratch(placements);
1993
1994 Ok(Some(Rc::new(
1995 MeasuredNode::new(
1996 node_id,
1997 Size { width, height },
1998 offset,
1999 Point::default(),
2000 children,
2001 )
2002 .with_alignment_lines(
2003 alignment_lines
2004 .with_overrides(explicit_lines.translated(padding.top))
2005 .translated(offset.y),
2006 ),
2007 )))
2008 }
2009
2010 fn measure_layout_node(
2011 self: &Rc<Self>,
2012 node_id: NodeId,
2013 constraints: Constraints,
2014 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2015 let mut keep = true;
2016 let (mut applier, measure) = loop {
2017 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2018 return Ok(None);
2019 };
2020 match applier.with_node::<LayoutNode, _>(node_id, |node| {
2021 self.visit_layout_node(node, constraints, keep)
2022 }) {
2023 Ok(LayoutNodeVisit::Measure(measure)) => break (applier, measure),
2024 Ok(LayoutNodeVisit::Cached(measured)) => return Ok(Some(measured)),
2025 Ok(LayoutNodeVisit::Keep(cached)) => {
2026 drop(applier);
2027 if let Some(kept) = self.keep_measurement(node_id, constraints, &cached)? {
2028 return Ok(Some(kept));
2029 }
2030 keep = false;
2031 }
2032 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
2033 Err(err) => return Err(err),
2034 }
2035 };
2036 let LayoutNodeMeasure {
2037 runtime_state,
2038 chain,
2039 mut pools,
2040 } = measure;
2041
2042 let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
2043 state: &runtime_state,
2044 };
2045 self.bind_layout_children(
2046 &mut applier,
2047 &runtime_state,
2048 &pools.child_ids,
2049 ChildPass::Measure,
2050 )?;
2051 drop(applier);
2052 pools.child_ids.clear();
2053
2054 let runtime_state = runtime_state.borrow();
2055 let measurement = self.measure_through_modifier_chain(
2056 node_id,
2057 &runtime_state,
2058 chain,
2059 constraints,
2060 &mut pools.placements,
2061 &mut pools.child_ids,
2062 );
2063
2064 if let Some(err) = runtime_state.take_child_error() {
2065 for child_state in runtime_state.child_states() {
2066 child_state.measured.take();
2067 }
2068 self.with_applier_result(|applier| {
2069 applier.with_node::<LayoutNode, _>(node_id, |node| {
2070 runtime_state.write_node_geometry(node_id, node, &measurement);
2071 })
2072 })
2073 .ok();
2074 return Err(err);
2075 }
2076
2077 let measured = Rc::new(
2078 MeasuredNode::new(
2079 node_id,
2080 measurement.size,
2081 measurement.offset,
2082 measurement.content_offset,
2083 runtime_state.measured_children(
2084 &pools.placements,
2085 &pools.child_ids,
2086 measurement.content_offset,
2087 ),
2088 )
2089 .with_alignment_lines(measurement.alignment_lines)
2090 .with_window_root(measurement.window_root),
2091 );
2092
2093 self.with_applier_result(|applier| {
2094 applier.with_node::<LayoutNode, _>(node_id, |node| {
2095 node.cache_handles().store_held_measurement(
2096 constraints,
2097 Rc::clone(&measured),
2098 measurement.hold,
2099 );
2100 runtime_state.write_node_geometry(node_id, node, &measurement);
2101 node.clear_needs_measure();
2102 node.clear_needs_layout();
2103 node.set_measured_size(measurement.size);
2104 node.set_content_offset(measurement.content_offset);
2105 })
2106 })
2107 .ok();
2108
2109 Ok(Some(measured))
2110 }
2111
2112 fn layout_node_intrinsic(
2117 self: &Rc<Self>,
2118 node_id: NodeId,
2119 kind: IntrinsicKind,
2120 ) -> Result<Option<f32>, NodeError> {
2121 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2122 return Ok(None);
2123 };
2124 let mut pools = VecPools::acquire(&self.frame_arena);
2125 let bound = applier.with_node::<LayoutNode, _>(node_id, |node| {
2126 let runtime_state = node.layout_runtime_state_handle();
2127 let chain = runtime_state.borrow_mut().bind_node(node);
2128 pools.child_ids.extend_from_slice(&node.children);
2129 (runtime_state, chain)
2130 });
2131 let (runtime_state, chain) = match bound {
2132 Ok(bound) => bound,
2133 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
2134 Err(err) => return Err(err),
2135 };
2136 let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
2137 state: &runtime_state,
2138 };
2139 self.bind_layout_children(
2140 &mut applier,
2141 &runtime_state,
2142 &pools.child_ids,
2143 ChildPass::Intrinsics,
2144 )?;
2145 drop(applier);
2146 pools.child_ids.clear();
2147
2148 let runtime_state = runtime_state.borrow();
2149 let value = if chain.uses_chain {
2150 let scope = crate::density::DensityMeasureScope::new(chain.density);
2151 let frame = CoordinatorFrame::new(
2152 &runtime_state.measure_policy,
2153 &scope,
2154 runtime_state.child_measurables(),
2155 runtime_state.child_states(),
2156 &mut pools.placements,
2157 &mut pools.child_ids,
2158 );
2159 runtime_state
2160 .coordinator_chain
2161 .intrinsic_from(0, &frame, kind)
2162 } else {
2163 policy_intrinsic(
2164 runtime_state.measure_policy.as_ref(),
2165 runtime_state.child_measurables(),
2166 kind,
2167 )
2168 };
2169 match runtime_state.take_child_error() {
2170 Some(err) => Err(err),
2171 None => Ok(Some(value)),
2172 }
2173 }
2174
2175 fn measurement_to_keep(
2178 &self,
2179 node: &LayoutNode,
2180 constraints: Constraints,
2181 ) -> Option<Rc<MeasuredNode>> {
2182 if node.needs_measure() || node.needs_layout() || node.is_virtual() {
2183 return None;
2184 }
2185 let cache = node.cache_handles();
2186 if cache.epoch() < self.cache_floor {
2187 return None;
2188 }
2189 cache.get_measurement(constraints)
2190 }
2191
2192 fn keep_measurement(
2198 self: &Rc<Self>,
2199 node_id: NodeId,
2200 constraints: Constraints,
2201 cached: &Rc<MeasuredNode>,
2202 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2203 let runtime_state = self.with_applier_result(|applier| {
2204 applier.with_node::<LayoutNode, _>(node_id, |node| node.layout_runtime_state_handle())
2205 })?;
2206 let mut next_state = 0;
2207 let kept = self.remeasure_changed_children(
2208 cached,
2209 |child_id| self.layout_child_change(&runtime_state, &mut next_state, child_id),
2210 |_| {},
2211 )?;
2212 let Some(kept) = kept else {
2213 return Ok(None);
2214 };
2215 self.with_applier_result(|applier| {
2216 applier.with_node::<LayoutNode, _>(node_id, |node| {
2217 node.cache_handles()
2218 .store_measurement(constraints, Rc::clone(&kept));
2219 node.clear_needs_layout();
2220 })
2221 })?;
2222 Ok(Some(kept))
2223 }
2224
2225 fn remeasure_changed_children(
2231 self: &Rc<Self>,
2232 cached: &Rc<MeasuredNode>,
2233 mut change: impl FnMut(NodeId) -> Result<ChildChange, NodeError>,
2234 mut remeasured: impl FnMut(NodeId),
2235 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2236 let mut replaced = smallvec::SmallVec::<[(usize, Rc<MeasuredNode>); 4]>::new();
2237 for (index, previous) in cached.children.iter().enumerate() {
2238 let previous = Rc::clone(&previous.node.borrow());
2239 let child_id = previous.node_id();
2240 let child = match change(child_id)? {
2241 ChildChange::Unchanged => continue,
2242 ChildChange::Refuse => return Ok(None),
2243 ChildChange::Changed(child) => child,
2244 };
2245 let measured = self.measure_node(child_id, child.constraints)?;
2246 remeasured(child_id);
2247 if !measures_the_same_for_parent(&previous, &measured) {
2248 return Ok(None);
2249 }
2250 if child.placed {
2251 self.place_where_it_was(child_id);
2252 }
2253 if !Rc::ptr_eq(&previous, &measured) {
2254 replaced.push((index, measured));
2255 }
2256 }
2257 for (index, measured) in replaced {
2258 if let Some(child) = cached.children.get(index) {
2259 *child.node.borrow_mut() = measured;
2260 }
2261 }
2262 Ok(Some(Rc::clone(cached)))
2263 }
2264
2265 fn layout_child_change(
2270 &self,
2271 runtime_state: &RefCell<LayoutRuntimeState>,
2272 next_state: &mut usize,
2273 child_id: NodeId,
2274 ) -> Result<ChildChange, NodeError> {
2275 let runtime_state = runtime_state.borrow();
2276 let Some((offset, state)) = runtime_state
2277 .child_states()
2278 .get(*next_state..)
2279 .unwrap_or_default()
2280 .iter()
2281 .enumerate()
2282 .find(|(_, state)| state.node_id == child_id)
2283 else {
2284 return Ok(ChildChange::Refuse);
2285 };
2286 *next_state += offset + 1;
2287 self.child_change(child_id, |view| {
2288 let parent_data_changed = view
2289 .parent_data
2290 .is_some_and(|data| Some(data) != state.parent_data.get());
2291 match state.measured_constraints.get() {
2292 Some(constraints) if !state.read_intrinsics.get() && !parent_data_changed => {
2293 ChildChange::Changed(ChangedChild {
2294 constraints,
2295 placed: view.placed,
2296 })
2297 }
2298 _ => ChildChange::Refuse,
2299 }
2300 })
2301 }
2302
2303 fn store_subcompose_measurement(
2306 &self,
2307 node_id: NodeId,
2308 constraints: Constraints,
2309 measured: &Rc<MeasuredNode>,
2310 ) {
2311 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2312 return;
2313 };
2314 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
2315 node.cache_handles()
2316 .store_measurement(constraints, Rc::clone(measured));
2317 });
2318 }
2319
2320 fn place_where_it_was(&self, child_id: NodeId) {
2323 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2324 return;
2325 };
2326 if applier
2327 .with_node::<LayoutNode, _>(child_id, |child| child.set_position(child.position()))
2328 .is_err()
2329 {
2330 let _ = applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2331 child.set_position(child.layout_state().position());
2332 });
2333 }
2334 }
2335
2336 fn visit_layout_node(
2337 &self,
2338 node: &mut LayoutNode,
2339 constraints: Constraints,
2340 keep: bool,
2341 ) -> LayoutNodeVisit<'_> {
2342 node.clear_placed();
2343 if keep && let Some(cached) = self.measurement_to_keep(node, constraints) {
2344 return LayoutNodeVisit::Keep(cached);
2345 }
2346 let cache = node.cache_handles();
2347 cache.activate(self.cache_epoch);
2348 if !Node::layout_dirty(node)
2349 && let Some(cached) = cache.get_measurement(constraints)
2350 {
2351 node.clear_needs_measure();
2352 node.clear_needs_layout();
2353 return LayoutNodeVisit::Cached(cached);
2354 }
2355
2356 let runtime_state = node.layout_runtime_state_handle();
2357 let chain = runtime_state.borrow_mut().bind_node(node);
2358 let mut pools = VecPools::acquire(&self.frame_arena);
2359 pools.child_ids.extend_from_slice(&node.children);
2360 LayoutNodeVisit::Measure(LayoutNodeMeasure {
2361 runtime_state,
2362 chain,
2363 pools,
2364 })
2365 }
2366
2367 fn bind_layout_children(
2368 self: &Rc<Self>,
2369 applier: &mut MemoryApplier,
2370 runtime_state: &RefCell<LayoutRuntimeState>,
2371 child_ids: &[NodeId],
2372 pass: ChildPass,
2373 ) -> Result<(), NodeError> {
2374 let mut runtime_state = runtime_state.borrow_mut();
2375 runtime_state.bind_children(self, pass, child_ids.len());
2376 let mut bound = 0;
2377 for &child_id in child_ids {
2378 if self.bind_layout_child(applier, &mut runtime_state, bound, child_id)? {
2379 bound += 1;
2380 }
2381 }
2382 runtime_state.truncate_children(bound);
2383 Ok(())
2384 }
2385
2386 fn bind_layout_child(
2387 &self,
2388 applier: &mut MemoryApplier,
2389 runtime_state: &mut LayoutRuntimeState,
2390 position: usize,
2391 child_id: NodeId,
2392 ) -> Result<bool, NodeError> {
2393 let bound = applier.with_node::<LayoutNode, _>(child_id, |child| {
2394 runtime_state.child_state_at(position, child_id).bind(
2395 LayoutChildBinding {
2396 cache: child.cache_handles(),
2397 layout_state: Some(child.layout_state_handle()),
2398 parent_data: Some(child.parent_data()),
2399 dirty: child.needs_layout() || child.needs_measure(),
2400 descendant_dirty: Node::descendant_needs_layout(child),
2401 },
2402 self,
2403 );
2404 });
2405 match bound {
2406 Ok(()) => Ok(true),
2407 Err(NodeError::TypeMismatch { .. }) => {
2408 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2409 runtime_state.child_state_at(position, child_id).bind(
2410 LayoutChildBinding {
2411 cache: child.cache_handles(),
2412 layout_state: None,
2413 parent_data: Some(parent_data_of(
2414 child.resolved_modifiers().layout_properties(),
2415 )),
2416 dirty: child.needs_layout() || child.needs_measure(),
2417 descendant_dirty: Node::descendant_needs_layout(child),
2418 },
2419 self,
2420 );
2421 }) {
2422 Ok(()) => Ok(true),
2423 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(false),
2424 Err(err) => Err(err),
2425 }
2426 }
2427 Err(NodeError::Missing { .. }) => Ok(false),
2428 Err(err) => Err(err),
2429 }
2430 }
2431
2432 fn measure_through_modifier_chain(
2433 &self,
2434 node_id: NodeId,
2435 runtime_state: &LayoutRuntimeState,
2436 chain: ModifierChainInputs,
2437 constraints: Constraints,
2438 placements: &mut Vec<Placement>,
2439 placement_indices: &mut Vec<usize>,
2440 ) -> ModifierChainMeasurement {
2441 let scope = crate::density::DensityMeasureScope::new(chain.density);
2442
2443 if !chain.uses_chain {
2444 let measurement = runtime_state.measure_policy.measure_into(
2445 &scope,
2446 runtime_state.child_measurables(),
2447 constraints,
2448 placements,
2449 );
2450 return ModifierChainMeasurement {
2451 size: measurement.size,
2452 alignment_lines: inherited_alignment_lines(
2453 runtime_state.child_states(),
2454 placements,
2455 placement_indices,
2456 )
2457 .with_overrides(measurement.alignment_lines),
2458 content_offset: Point::default(),
2459 offset: chain.offset,
2460 window_root: chain.window_root,
2461 uses_chain: false,
2462 hold: if chain.window_root {
2463 None
2464 } else {
2465 runtime_state.measure_policy.measure_hold(
2466 runtime_state.child_measurables(),
2467 constraints,
2468 measurement.size,
2469 )
2470 },
2471 };
2472 }
2473
2474 let frame = CoordinatorFrame::new(
2475 &runtime_state.measure_policy,
2476 &scope,
2477 runtime_state.child_measurables(),
2478 runtime_state.child_states(),
2479 placements,
2480 placement_indices,
2481 );
2482 let placeable = runtime_state
2483 .coordinator_chain
2484 .measure_from(0, &frame, constraints);
2485 let (content_x, content_y) = placeable.content_offset();
2486
2487 let invalidations = frame.take_invalidations();
2488 if !invalidations.is_empty() {
2489 self.with_applier_result(|applier| {
2490 applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2491 for kind in invalidations {
2492 match kind {
2493 InvalidationKind::Layout => layout_node.mark_needs_measure(),
2494 InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2495 InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2496 InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2497 InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2498 }
2499 }
2500 })
2501 })
2502 .ok();
2503 }
2504
2505 ModifierChainMeasurement {
2506 alignment_lines: placeable.alignment_lines(),
2507 size: Size {
2508 width: placeable.width(),
2509 height: placeable.height(),
2510 },
2511 content_offset: Point {
2512 x: content_x - chain.offset.x,
2513 y: content_y - chain.offset.y,
2514 },
2515 offset: chain.offset,
2516 window_root: chain.window_root,
2517 uses_chain: true,
2518 hold: if chain.window_root {
2519 None
2520 } else {
2521 runtime_state.coordinator_chain.measured_hold(
2522 cranpose_ui_layout::MeasureScope::density(&scope),
2523 runtime_state.measure_policy.as_ref(),
2524 runtime_state.child_measurables(),
2525 )
2526 },
2527 }
2528 }
2529}
2530
2531struct VecPools<'a> {
2532 arena: &'a RefCell<FrameLayoutArena>,
2533 child_ids: Vec<NodeId>,
2534 placements: Vec<Placement>,
2535}
2536
2537impl<'a> VecPools<'a> {
2538 fn acquire(arena: &'a RefCell<FrameLayoutArena>) -> Self {
2539 let mut pools = arena.borrow_mut();
2540 let child_ids = pools.tmp_child_ids.acquire();
2541 let placements = pools.tmp_placements.acquire();
2542 Self {
2543 arena,
2544 child_ids,
2545 placements,
2546 }
2547 }
2548}
2549
2550impl Drop for VecPools<'_> {
2551 fn drop(&mut self) {
2552 let mut pools = self.arena.borrow_mut();
2553 pools
2554 .tmp_child_ids
2555 .release(std::mem::take(&mut self.child_ids));
2556 pools
2557 .tmp_placements
2558 .release(std::mem::take(&mut self.placements));
2559 }
2560}
2561
2562struct SlotsGuard<'a> {
2563 table: &'a RefCell<SlotTable>,
2564 slots: Option<SlotTable>,
2565}
2566
2567impl<'a> SlotsGuard<'a> {
2568 fn take(table: &'a RefCell<SlotTable>) -> Self {
2569 let slots = std::mem::take(&mut *table.borrow_mut());
2570 Self {
2571 table,
2572 slots: Some(slots),
2573 }
2574 }
2575
2576 fn host(&mut self) -> Rc<SlotsHost> {
2577 let slots = self.slots.take().unwrap_or_default();
2578 Rc::new(SlotsHost::new(slots))
2579 }
2580
2581 fn restore(&mut self, slots: SlotTable) {
2582 debug_assert!(self.slots.is_none());
2583 self.slots = Some(slots);
2584 }
2585}
2586
2587impl Drop for SlotsGuard<'_> {
2588 fn drop(&mut self) {
2589 if let Some(slots) = self.slots.take() {
2590 *self.table.borrow_mut() = slots;
2591 }
2592 }
2593}
2594
2595#[derive(Debug, Clone)]
2596pub(crate) struct MeasuredNode {
2597 node_id: NodeId,
2598 size: Size,
2599 offset: Point,
2600 content_offset: Point,
2601 alignment_lines: AlignmentLines,
2602 children: Vec<MeasuredChild>,
2603 window_root: bool,
2604}
2605
2606impl MeasuredNode {
2607 fn new(
2608 node_id: NodeId,
2609 size: Size,
2610 offset: Point,
2611 content_offset: Point,
2612 children: Vec<MeasuredChild>,
2613 ) -> Self {
2614 Self {
2615 node_id,
2616 size,
2617 offset,
2618 content_offset,
2619 alignment_lines: AlignmentLines::default(),
2620 children,
2621 window_root: false,
2622 }
2623 }
2624
2625 fn with_window_root(mut self, window_root: bool) -> Self {
2626 self.window_root = window_root;
2627 self
2628 }
2629
2630 fn with_alignment_lines(mut self, alignment_lines: AlignmentLines) -> Self {
2631 self.alignment_lines = alignment_lines;
2632 self
2633 }
2634
2635 pub(crate) fn alignment_lines_for_parent(&self) -> AlignmentLines {
2636 if self.window_root {
2637 AlignmentLines::default()
2638 } else {
2639 self.alignment_lines
2640 }
2641 }
2642
2643 pub(crate) fn size_for_parent(&self) -> Size {
2644 if self.window_root {
2645 Size::new(0.0, 0.0)
2646 } else {
2647 self.size
2648 }
2649 }
2650
2651 #[cfg(test)]
2652 pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2653 Self::new(
2654 node_id,
2655 size,
2656 Point::default(),
2657 Point::default(),
2658 Vec::new(),
2659 )
2660 }
2661
2662 pub(crate) fn node_id(&self) -> NodeId {
2663 self.node_id
2664 }
2665
2666 pub(crate) fn size(&self) -> Size {
2667 self.size
2668 }
2669}
2670
2671#[derive(Debug, Clone)]
2672struct MeasuredChild {
2673 node: RefCell<Rc<MeasuredNode>>,
2676 offset: Point,
2677}
2678
2679fn measurable_intrinsic(measurable: &dyn Measurable, kind: IntrinsicKind) -> f32 {
2681 match kind {
2682 IntrinsicKind::MinWidth(height) => measurable.min_intrinsic_width(height),
2683 IntrinsicKind::MaxWidth(height) => measurable.max_intrinsic_width(height),
2684 IntrinsicKind::MinHeight(width) => measurable.min_intrinsic_height(width),
2685 IntrinsicKind::MaxHeight(width) => measurable.max_intrinsic_height(width),
2686 }
2687}
2688
2689fn policy_intrinsic(
2691 policy: &dyn MeasurePolicy,
2692 measurables: &[Box<dyn Measurable>],
2693 kind: IntrinsicKind,
2694) -> f32 {
2695 match kind {
2696 IntrinsicKind::MinWidth(height) => policy.min_intrinsic_width(measurables, height),
2697 IntrinsicKind::MaxWidth(height) => policy.max_intrinsic_width(measurables, height),
2698 IntrinsicKind::MinHeight(width) => policy.min_intrinsic_height(measurables, width),
2699 IntrinsicKind::MaxHeight(width) => policy.max_intrinsic_height(measurables, width),
2700 }
2701}
2702
2703fn layout_modifier_intrinsic(
2705 node: &dyn cranpose_foundation::LayoutModifierNode,
2706 wrapped: &dyn Measurable,
2707 kind: IntrinsicKind,
2708 density: f32,
2709) -> f32 {
2710 match kind {
2711 IntrinsicKind::MinWidth(height) => node.min_intrinsic_width(wrapped, height, density),
2712 IntrinsicKind::MaxWidth(height) => node.max_intrinsic_width(wrapped, height, density),
2713 IntrinsicKind::MinHeight(width) => node.min_intrinsic_height(wrapped, width, density),
2714 IntrinsicKind::MaxHeight(width) => node.max_intrinsic_height(wrapped, width, density),
2715 }
2716}
2717
2718struct CoordinatorFrame<'a> {
2719 measure_policy: &'a Rc<dyn MeasurePolicy>,
2720 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2721 measurables: &'a [Box<dyn Measurable>],
2722 child_states: &'a [Rc<LayoutChildMeasureState>],
2723 placements: RefCell<&'a mut Vec<Placement>>,
2724 placement_indices: RefCell<&'a mut Vec<usize>>,
2725 context: RefCell<LayoutNodeContext>,
2726}
2727
2728impl<'a> CoordinatorFrame<'a> {
2729 fn new(
2730 measure_policy: &'a Rc<dyn MeasurePolicy>,
2731 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2732 measurables: &'a [Box<dyn Measurable>],
2733 child_states: &'a [Rc<LayoutChildMeasureState>],
2734 placements: &'a mut Vec<Placement>,
2735 placement_indices: &'a mut Vec<usize>,
2736 ) -> Self {
2737 Self {
2738 measure_policy,
2739 scope,
2740 measurables,
2741 child_states,
2742 placements: RefCell::new(placements),
2743 placement_indices: RefCell::new(placement_indices),
2744 context: RefCell::new(LayoutNodeContext::new(scope.density())),
2745 }
2746 }
2747
2748 fn take_invalidations(&self) -> Vec<InvalidationKind> {
2749 self.context.borrow_mut().take_invalidations()
2750 }
2751}
2752
2753struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2754 chain: &'chain CoordinatorChain,
2755 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2756 index: usize,
2757 alignment_lines: Cell<AlignmentLines>,
2758}
2759
2760impl<'chain, 'frame_ref, 'frame_data> CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2761 fn new(
2762 chain: &'chain CoordinatorChain,
2763 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2764 index: usize,
2765 ) -> Self {
2766 Self {
2767 chain,
2768 frame,
2769 index,
2770 alignment_lines: Cell::default(),
2771 }
2772 }
2773}
2774
2775impl Measurable for CoordinatorLink<'_, '_, '_> {
2776 fn measure(&self, constraints: Constraints) -> Placeable {
2777 let placeable = self.chain.measure_from(self.index, self.frame, constraints);
2778 self.alignment_lines.set(placeable.alignment_lines());
2779 placeable
2780 }
2781
2782 fn min_intrinsic_width(&self, height: f32) -> f32 {
2783 self.chain
2784 .intrinsic_from(self.index, self.frame, IntrinsicKind::MinWidth(height))
2785 }
2786
2787 fn max_intrinsic_width(&self, height: f32) -> f32 {
2788 self.chain
2789 .intrinsic_from(self.index, self.frame, IntrinsicKind::MaxWidth(height))
2790 }
2791
2792 fn min_intrinsic_height(&self, width: f32) -> f32 {
2793 self.chain
2794 .intrinsic_from(self.index, self.frame, IntrinsicKind::MinHeight(width))
2795 }
2796
2797 fn max_intrinsic_height(&self, width: f32) -> f32 {
2798 self.chain
2799 .intrinsic_from(self.index, self.frame, IntrinsicKind::MaxHeight(width))
2800 }
2801}
2802
2803struct CoordinatorNode {
2804 modifier_index: usize,
2805 node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2806 measured_size: Cell<Size>,
2807 measured_constraints: Cell<Constraints>,
2809 accumulated_offset: Cell<Point>,
2810}
2811
2812impl CoordinatorNode {
2813 fn new(
2814 modifier_index: usize,
2815 node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2816 ) -> Self {
2817 Self {
2818 modifier_index,
2819 node,
2820 measured_size: Cell::new(Size::default()),
2821 measured_constraints: Cell::new(Constraints::tight(0.0, 0.0)),
2822 accumulated_offset: Cell::new(Point::default()),
2823 }
2824 }
2825
2826 fn matches(
2827 &self,
2828 modifier_index: usize,
2829 node: &Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2830 ) -> bool {
2831 self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2832 }
2833
2834 #[cfg(test)]
2835 fn ptr(&self) -> usize {
2836 Rc::as_ptr(&self.node) as *const () as usize
2837 }
2838}
2839
2840#[derive(Default)]
2841struct CoordinatorChain {
2842 nodes: Vec<CoordinatorNode>,
2843 inner_size: Cell<Size>,
2845 inner_constraints: Cell<Option<Constraints>>,
2847 synced: Option<(u64, ModifierChainInputs)>,
2851}
2852
2853impl CoordinatorChain {
2854 fn sync(&mut self, node: &LayoutNode) -> ModifierChainInputs {
2855 let density = node.density();
2856 let revision = node.modifier_chain().revision();
2857 if let Some((synced, inputs)) = self.synced
2858 && synced == revision
2859 && inputs.density == density
2860 {
2861 return inputs;
2862 }
2863 let inputs = self.walk(node, density);
2864 self.synced = Some((revision, inputs));
2865 inputs
2866 }
2867
2868 fn walk(&mut self, node: &LayoutNode, density: crate::density::Density) -> ModifierChainInputs {
2871 let mut inputs = ModifierChainInputs {
2872 density,
2873 window_root: node.is_window_root(),
2874 offset: Point::default(),
2875 uses_chain: false,
2876 };
2877 let chain_handle = node.modifier_chain();
2878 if !chain_handle.has_layout_nodes() {
2879 return inputs;
2880 }
2881
2882 let chain = chain_handle.chain();
2883 let mut len = 0;
2884 let mut matches = true;
2885 chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2886 let Some(index) = node_ref.entry_index() else {
2887 return;
2888 };
2889 if let Some(node) = chain.get_node_rc(index) {
2890 matches = matches
2891 && self
2892 .nodes
2893 .get(len)
2894 .is_some_and(|candidate| candidate.matches(index, node));
2895 len += 1;
2896 }
2897 node_ref.with_node(|node| {
2898 if let Some(offset_node) = node
2899 .as_any()
2900 .downcast_ref::<crate::modifier_nodes::OffsetNode>()
2901 {
2902 let delta = offset_node.device_offset(density.density());
2903 inputs.offset.x += delta.x;
2904 inputs.offset.y += delta.y;
2905 }
2906 });
2907 });
2908
2909 inputs.uses_chain = len > 0;
2910 if inputs.uses_chain && !(matches && len == self.nodes.len()) {
2911 self.rebuild(chain, len);
2912 }
2913 inputs
2914 }
2915
2916 fn rebuild(&mut self, chain: &cranpose_foundation::ModifierNodeChain, len: usize) {
2917 let mut previous_nodes = std::mem::take(&mut self.nodes);
2918 self.nodes.reserve_exact(len);
2921 chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2922 let Some((index, node)) = node_ref
2923 .entry_index()
2924 .and_then(|index| chain.get_node_rc(index).map(|node| (index, node)))
2925 else {
2926 return;
2927 };
2928 match previous_nodes
2929 .iter()
2930 .position(|candidate| candidate.matches(index, node))
2931 {
2932 Some(position) => self.nodes.push(previous_nodes.swap_remove(position)),
2933 None => self
2934 .nodes
2935 .push(CoordinatorNode::new(index, Rc::clone(node))),
2936 }
2937 });
2938 }
2939
2940 fn measured_hold(
2945 &self,
2946 density: f32,
2947 policy: &dyn MeasurePolicy,
2948 measurables: &[Box<dyn Measurable>],
2949 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
2950 let inner_size = self.inner_size.get();
2951 let mut wrapped = cranpose_ui_layout::WrappedHold {
2952 size: inner_size,
2953 hold: self
2954 .inner_constraints
2955 .get()
2956 .and_then(|constraints| policy.measure_hold(measurables, constraints, inner_size)),
2957 };
2958 for node in self.nodes.iter().rev() {
2959 let size = node.measured_size.get();
2960 let hold = match node.node.borrow().as_layout_node() {
2961 Some(layout) => {
2962 layout.measure_hold(density, node.measured_constraints.get(), size, wrapped)
2963 }
2964 None => wrapped.hold,
2965 };
2966 wrapped = cranpose_ui_layout::WrappedHold { size, hold };
2967 }
2968 wrapped.hold
2969 }
2970
2971 fn measure_from(
2972 &self,
2973 index: usize,
2974 frame: &CoordinatorFrame<'_>,
2975 constraints: Constraints,
2976 ) -> Placeable {
2977 let Some(node) = self.nodes.get(index) else {
2978 let mut placements = frame.placements.borrow_mut();
2979 let measurement = frame.measure_policy.measure_into(
2980 frame.scope,
2981 frame.measurables,
2982 constraints,
2983 &mut placements,
2984 );
2985 self.inner_size.set(measurement.size);
2986 self.inner_constraints.set(Some(constraints));
2987 return Placeable::value(
2988 measurement.size.width,
2989 measurement.size.height,
2990 NodeId::default(),
2991 )
2992 .with_alignment_lines(
2993 inherited_alignment_lines(
2994 frame.child_states,
2995 &placements,
2996 &mut frame.placement_indices.borrow_mut(),
2997 )
2998 .with_overrides(measurement.alignment_lines),
2999 );
3000 };
3001
3002 let wrapped = CoordinatorLink::new(self, frame, index + 1);
3003 let node_borrow = node.node.borrow();
3004 node.measured_constraints.set(constraints);
3005
3006 let Some(layout_node) = node_borrow.as_layout_node() else {
3007 let placeable = wrapped.measure(constraints);
3008 node.measured_size.set(Size {
3009 width: placeable.width(),
3010 height: placeable.height(),
3011 });
3012 let child_accumulated = self.total_content_offset_from(index + 1);
3013 node.accumulated_offset.set(child_accumulated);
3014 return placeable;
3015 };
3016
3017 let result = match frame.context.try_borrow_mut() {
3018 Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
3019 Err(_) => {
3020 let mut temp = LayoutNodeContext::new(frame.scope.density());
3021 let result = layout_node.measure(&mut temp, &wrapped, constraints);
3022 if let Ok(mut context) = frame.context.try_borrow_mut() {
3023 for kind in temp.take_invalidations() {
3024 context.invalidate(kind);
3025 }
3026 }
3027 result
3028 }
3029 };
3030
3031 node.measured_size.set(result.size);
3032 let local_offset = Point {
3033 x: result.placement_offset_x,
3034 y: result.placement_offset_y,
3035 };
3036 let child_accumulated = self.total_content_offset_from(index + 1);
3037 let accumulated = Point {
3038 x: local_offset.x + child_accumulated.x,
3039 y: local_offset.y + child_accumulated.y,
3040 };
3041 node.accumulated_offset.set(accumulated);
3042
3043 Placeable::value_with_offset(
3044 result.size.width,
3045 result.size.height,
3046 NodeId::default(),
3047 (accumulated.x, accumulated.y),
3048 )
3049 .with_alignment_lines(
3050 wrapped
3051 .alignment_lines
3052 .get()
3053 .translated(local_offset.y)
3054 .with_overrides(result.alignment_lines),
3055 )
3056 }
3057
3058 fn intrinsic_from(
3059 &self,
3060 index: usize,
3061 frame: &CoordinatorFrame<'_>,
3062 kind: IntrinsicKind,
3063 ) -> f32 {
3064 let Some(node) = self.nodes.get(index) else {
3065 return policy_intrinsic(frame.measure_policy.as_ref(), frame.measurables, kind);
3066 };
3067 let wrapped = CoordinatorLink::new(self, frame, index + 1);
3068 let node_borrow = node.node.borrow();
3069 node_borrow.as_layout_node().map_or_else(
3070 || measurable_intrinsic(&wrapped, kind),
3071 |layout_node| {
3072 layout_modifier_intrinsic(layout_node, &wrapped, kind, frame.scope.density())
3073 },
3074 )
3075 }
3076
3077 fn write_geometry(
3081 &self,
3082 geometry: &crate::modifier::CoordinatorGeometry,
3083 node_offset: Point,
3084 ) -> bool {
3085 let content = self.total_content_offset_from(0);
3086 let placed = |inner_offset: Point, size: Size| GeometryRect {
3087 x: content.x - inner_offset.x - node_offset.x,
3088 y: content.y - inner_offset.y - node_offset.y,
3089 width: size.width,
3090 height: size.height,
3091 };
3092 geometry.replace(
3093 self.nodes
3094 .iter()
3095 .map(|node| placed(node.accumulated_offset.get(), node.measured_size.get()))
3096 .chain(std::iter::once(placed(
3097 Point::default(),
3098 self.inner_size.get(),
3099 ))),
3100 )
3101 }
3102
3103 fn total_content_offset_from(&self, index: usize) -> Point {
3104 self.nodes
3105 .get(index)
3106 .map(|node| node.accumulated_offset.get())
3107 .unwrap_or_default()
3108 }
3109
3110 #[cfg(test)]
3111 fn debug_ptrs(&self) -> Vec<usize> {
3112 self.nodes.iter().map(CoordinatorNode::ptr).collect()
3113 }
3114}
3115
3116fn inherited_alignment_lines(
3117 child_states: &[Rc<LayoutChildMeasureState>],
3118 placements: &[Placement],
3119 placement_indices: &mut Vec<usize>,
3120) -> AlignmentLines {
3121 placement_indices.clear();
3122 let in_child_order = placements.len() == child_states.len()
3123 && placements
3124 .iter()
3125 .zip(child_states)
3126 .all(|(placement, child)| placement.node_id == child.node_id);
3127 if !in_child_order {
3128 placement_indices.extend(0..placements.len());
3129 placement_indices.sort_unstable_by_key(|&index| (placements[index].node_id, index));
3130 }
3131 let mut lines = AlignmentLines::default();
3132 for (index, child) in child_states.iter().enumerate() {
3133 let placement = placement_for_child(placements, placement_indices, index, child.node_id);
3134 read_cell(&child.measured, |measured| {
3135 if let Some(measured) = measured {
3136 lines.merge(
3137 measured
3138 .alignment_lines_for_parent()
3139 .translated(child.placement_position(placement).y),
3140 );
3141 }
3142 });
3143 }
3144 lines
3145}
3146
3147fn placement_for_child<'a>(
3148 placements: &'a [Placement],
3149 indices: &[usize],
3150 child_index: usize,
3151 node_id: NodeId,
3152) -> Option<&'a Placement> {
3153 let index = if indices.is_empty() {
3154 child_index
3155 } else {
3156 let first = indices.partition_point(|&index| placements[index].node_id < node_id);
3157 *indices.get(first)?
3158 };
3159 placements
3160 .get(index)
3161 .filter(|placement| placement.node_id == node_id)
3162}
3163
3164pub(crate) struct LayoutRuntimeState {
3165 children: Option<Box<LayoutChildren>>,
3167 coordinator_chain: CoordinatorChain,
3168 measure_policy: Rc<dyn MeasurePolicy>,
3169}
3170
3171struct LayoutChildren {
3173 ids: Vec<NodeId>,
3174 states: Vec<Rc<LayoutChildMeasureState>>,
3175 measurables: Vec<Box<dyn Measurable>>,
3176 frame: Rc<LayoutChildFrame>,
3177}
3178
3179impl LayoutChildren {
3180 fn with_capacity(count: usize) -> Self {
3184 Self {
3185 ids: Vec::with_capacity(count),
3186 states: Vec::with_capacity(count),
3187 measurables: Vec::with_capacity(count),
3188 frame: Rc::default(),
3189 }
3190 }
3191}
3192
3193impl LayoutRuntimeState {
3194 pub(crate) fn new(measure_policy: Rc<dyn MeasurePolicy>) -> Self {
3195 Self {
3196 children: None,
3197 coordinator_chain: CoordinatorChain::default(),
3198 measure_policy,
3199 }
3200 }
3201
3202 fn bind_node(&mut self, node: &LayoutNode) -> ModifierChainInputs {
3203 if !Rc::ptr_eq(&self.measure_policy, &node.measure_policy) {
3204 self.measure_policy = Rc::clone(&node.measure_policy);
3205 }
3206 self.coordinator_chain.sync(node)
3207 }
3208
3209 fn child_states(&self) -> &[Rc<LayoutChildMeasureState>] {
3210 self.children
3211 .as_deref()
3212 .map_or(&[], |children| children.states.as_slice())
3213 }
3214
3215 fn child_measurables(&self) -> &[Box<dyn Measurable>] {
3216 self.children
3217 .as_deref()
3218 .map_or(&[], |children| children.measurables.as_slice())
3219 }
3220
3221 fn bind_children(&mut self, builder: &Rc<LayoutBuilderState>, pass: ChildPass, count: usize) {
3224 if count == 0 && self.children.is_none() {
3225 return;
3226 }
3227 self.children
3228 .get_or_insert_with(|| Box::new(LayoutChildren::with_capacity(count)))
3229 .frame
3230 .bind(builder, pass);
3231 }
3232
3233 fn unbind_children(&self) {
3234 if let Some(children) = &self.children {
3235 children.frame.unbind();
3236 }
3237 }
3238
3239 fn take_child_error(&self) -> Option<NodeError> {
3241 self.children
3242 .as_ref()
3243 .and_then(|children| children.frame.error.borrow_mut().take())
3244 }
3245
3246 fn child_state_at(&mut self, position: usize, child_id: NodeId) -> &LayoutChildMeasureState {
3249 let children = self
3250 .children
3251 .get_or_insert_with(|| Box::new(LayoutChildren::with_capacity(position + 1)));
3252 if children.ids.get(position) != Some(&child_id) {
3253 let from = match children.ids[position..]
3254 .iter()
3255 .position(|&id| id == child_id)
3256 {
3257 Some(offset) => position + offset,
3258 None => {
3259 let state = LayoutChildMeasureState::new(child_id, Rc::clone(&children.frame));
3260 children.ids.push(child_id);
3261 children.states.push(Rc::clone(&state));
3262 children
3263 .measurables
3264 .push(Box::new(LayoutChildMeasurable::new(state)));
3265 children.ids.len() - 1
3266 }
3267 };
3268 children.ids.swap(position, from);
3269 children.states.swap(position, from);
3270 children.measurables.swap(position, from);
3271 }
3272 &children.states[position]
3273 }
3274
3275 fn truncate_children(&mut self, len: usize) {
3276 if let Some(children) = &mut self.children {
3277 children.ids.truncate(len);
3278 children.states.truncate(len);
3279 children.measurables.truncate(len);
3280 }
3281 }
3282
3283 fn measured_children(
3284 &self,
3285 placements: &[Placement],
3286 placement_indices: &[usize],
3287 content_offset: Point,
3288 ) -> Vec<MeasuredChild> {
3289 let child_states = self.child_states();
3290 let mut measured_children = Vec::with_capacity(child_states.len());
3291 for (index, child_state) in child_states.iter().enumerate() {
3292 let Some(measured) = child_state.measured.take() else {
3293 continue;
3294 };
3295 let placement =
3296 placement_for_child(placements, placement_indices, index, child_state.node_id);
3297 let base_position = child_state.placement_position(placement);
3298 if placement.is_some() {
3299 child_state.place_retained(Point {
3300 x: base_position.x + measured.offset.x,
3301 y: base_position.y + measured.offset.y,
3302 });
3303 }
3304 measured_children.push(MeasuredChild {
3305 node: RefCell::new(measured),
3306 offset: Point {
3307 x: content_offset.x + base_position.x,
3308 y: content_offset.y + base_position.y,
3309 },
3310 });
3311 }
3312 measured_children
3313 }
3314
3315 fn write_node_geometry(
3316 &self,
3317 node_id: NodeId,
3318 node: &LayoutNode,
3319 measurement: &ModifierChainMeasurement,
3320 ) {
3321 let geometry = node.coordinator_geometry();
3322 let moved = if measurement.uses_chain {
3323 self.coordinator_chain
3324 .write_geometry(geometry, measurement.offset)
3325 } else {
3326 geometry.replace([GeometryRect {
3327 x: 0.0,
3328 y: 0.0,
3329 width: measurement.size.width,
3330 height: measurement.size.height,
3331 }])
3332 };
3333 if moved {
3334 crate::render_state::record_geometry_scene_node(node_id);
3335 }
3336 }
3337
3338 #[cfg(test)]
3339 pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
3340 LayoutRuntimeDebugStats {
3341 child_ids: self
3342 .children
3343 .as_ref()
3344 .map(|children| children.ids.clone())
3345 .unwrap_or_default(),
3346 child_state_ptrs: self
3347 .child_states()
3348 .iter()
3349 .map(|state| Rc::as_ptr(state) as *const () as usize)
3350 .collect(),
3351 child_measurable_ptrs: self
3352 .child_measurables()
3353 .iter()
3354 .map(|measurable| {
3355 measurable.as_ref() as *const dyn Measurable as *const () as usize
3356 })
3357 .collect(),
3358 child_measurable_count: self.child_measurables().len(),
3359 coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
3360 coordinator_node_count: self.coordinator_chain.nodes.len(),
3361 }
3362 }
3363}
3364
3365#[cfg(test)]
3366#[derive(Debug, Clone, PartialEq, Eq)]
3367pub(crate) struct LayoutRuntimeDebugStats {
3368 pub(crate) child_ids: Vec<NodeId>,
3369 pub(crate) child_state_ptrs: Vec<usize>,
3370 pub(crate) child_measurable_ptrs: Vec<usize>,
3371 pub(crate) child_measurable_count: usize,
3372 pub(crate) coordinator_node_ptrs: Vec<usize>,
3373 pub(crate) coordinator_node_count: usize,
3374}
3375
3376#[derive(Default)]
3377struct LayoutChildFrame {
3378 builder: RefCell<Option<Rc<LayoutBuilderState>>>,
3379 error: RefCell<Option<NodeError>>,
3380 pass: Cell<ChildPass>,
3381}
3382
3383#[derive(Clone, Copy, Default, PartialEq, Eq)]
3385enum ChildPass {
3386 #[default]
3389 Measure,
3390 Intrinsics,
3393}
3394
3395impl LayoutChildFrame {
3396 fn bind(&self, builder: &Rc<LayoutBuilderState>, pass: ChildPass) {
3397 self.error.borrow_mut().take();
3398 *self.builder.borrow_mut() = Some(Rc::clone(builder));
3399 self.pass.set(pass);
3400 }
3401
3402 fn measures(&self) -> bool {
3403 self.pass.get() == ChildPass::Measure
3404 }
3405
3406 fn unbind(&self) {
3407 self.builder.borrow_mut().take();
3408 }
3409
3410 fn record_error(&self, err: NodeError) {
3411 if self.builder.borrow().is_none() {
3412 return;
3413 }
3414 let mut slot = self.error.borrow_mut();
3415 if slot.is_none() {
3416 *slot = Some(err);
3417 }
3418 }
3419}
3420
3421struct LayoutChildBinding<'a> {
3422 cache: &'a LayoutNodeCacheHandles,
3423 layout_state: Option<&'a Rc<RefCell<LayoutState>>>,
3424 parent_data: Option<cranpose_ui_layout::ParentData>,
3425 dirty: bool,
3427 descendant_dirty: bool,
3429}
3430
3431fn read_layout_child<R>(
3434 applier: &mut MemoryApplier,
3435 child_id: NodeId,
3436 read: impl Fn(
3437 &dyn Node,
3438 &LayoutNodeCacheHandles,
3439 &dyn Fn() -> crate::modifier::LayoutProperties,
3440 bool,
3441 ) -> R,
3442) -> Result<Option<R>, NodeError> {
3443 match applier.with_node::<LayoutNode, _>(child_id, |child| {
3444 read(
3445 &*child,
3446 child.cache_handles(),
3447 &|| child.resolved_modifiers().layout_properties(),
3448 child.is_placed(),
3449 )
3450 }) {
3451 Ok(value) => Ok(Some(value)),
3452 Err(NodeError::TypeMismatch { .. }) => {
3453 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
3454 read(
3455 &*child,
3456 child.cache_handles(),
3457 &|| child.resolved_modifiers().layout_properties(),
3458 child.layout_state().is_placed(),
3459 )
3460 }) {
3461 Ok(value) => Ok(Some(value)),
3462 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
3463 Err(err) => Err(err),
3464 }
3465 }
3466 Err(NodeError::Missing { .. }) => Ok(None),
3467 Err(err) => Err(err),
3468 }
3469}
3470
3471fn measures_the_same_for_parent(previous: &MeasuredNode, measured: &MeasuredNode) -> bool {
3475 let (before, after) = (
3476 previous.alignment_lines_for_parent(),
3477 measured.alignment_lines_for_parent(),
3478 );
3479 previous.size_for_parent() == measured.size_for_parent()
3480 && previous.offset == measured.offset
3481 && before.first_baseline() == after.first_baseline()
3482 && before.last_baseline() == after.last_baseline()
3483}
3484
3485pub(crate) fn parent_data_of(
3486 props: crate::modifier::LayoutProperties,
3487) -> cranpose_ui_layout::ParentData {
3488 let weight = props.weight().unwrap_or_default();
3489 cranpose_ui_layout::ParentData {
3490 weight: weight.weight,
3491 fill: weight.fill,
3492 box_alignment: props.box_alignment(),
3493 row_alignment: props.row_alignment(),
3494 row_baseline: props.row_baseline(),
3495 column_alignment: props.column_alignment(),
3496 }
3497}
3498
3499struct LayoutChildMeasureState {
3503 node_id: NodeId,
3504 frame: Rc<LayoutChildFrame>,
3505 cache: Cell<Option<LayoutNodeCacheHandles>>,
3507 cache_epoch: Cell<u64>,
3508 force_remeasure: Cell<bool>,
3509 parent_data: Cell<Option<cranpose_ui_layout::ParentData>>,
3510 measured_constraints: Cell<Option<Constraints>>,
3512 read_intrinsics: Cell<bool>,
3514 measured: Cell<Option<Rc<MeasuredNode>>>,
3515 last_position: Cell<Option<Point>>,
3516 layout_state: Cell<Option<Rc<RefCell<LayoutState>>>>,
3517}
3518
3519fn read_cell<T: Default, R>(cell: &Cell<T>, read: impl FnOnce(&T) -> R) -> R {
3522 let value = cell.take();
3523 let result = read(&value);
3524 cell.set(value);
3525 result
3526}
3527
3528impl LayoutChildMeasureState {
3529 fn placement_position(&self, placement: Option<&Placement>) -> Point {
3530 placement
3531 .map(|placement| Point {
3532 x: placement.x,
3533 y: placement.y,
3534 })
3535 .or_else(|| self.last_position.get())
3536 .unwrap_or_default()
3537 }
3538
3539 fn new(node_id: NodeId, frame: Rc<LayoutChildFrame>) -> Rc<Self> {
3540 Rc::new(Self {
3541 node_id,
3542 frame,
3543 cache: Cell::new(None),
3544 cache_epoch: Cell::new(0),
3545 force_remeasure: Cell::new(true),
3546 parent_data: Cell::new(None),
3547 measured_constraints: Cell::new(None),
3548 read_intrinsics: Cell::new(false),
3549 measured: Cell::new(None),
3550 last_position: Cell::new(None),
3551 layout_state: Cell::new(None),
3552 })
3553 }
3554
3555 fn bind(&self, binding: LayoutChildBinding<'_>, pass: &LayoutBuilderState) {
3559 let child_epoch = binding.cache.epoch();
3560 let stale = binding.dirty || child_epoch < pass.cache_floor;
3561 let cache_epoch = if stale { pass.cache_epoch } else { child_epoch };
3562 binding.cache.activate(cache_epoch);
3563 let mut cache = self.cache.take();
3564 match &mut cache {
3565 Some(cache) => cache.clone_from(binding.cache),
3566 None => cache = Some(binding.cache.clone()),
3567 }
3568 self.cache.set(cache);
3569 self.cache_epoch.set(cache_epoch);
3570 self.force_remeasure.set(stale || binding.descendant_dirty);
3571 self.parent_data.set(binding.parent_data);
3572 if self.frame.measures() {
3573 self.measured.take();
3574 self.last_position.set(None);
3575 self.measured_constraints.set(None);
3576 self.read_intrinsics.set(false);
3577 }
3578 let layout_state = self.layout_state.take();
3579 let shared = layout_state
3580 .as_ref()
3581 .zip(binding.layout_state)
3582 .is_some_and(|(current, bound)| Rc::ptr_eq(current, bound));
3583 self.layout_state.set(if shared {
3584 layout_state
3585 } else {
3586 binding.layout_state.cloned()
3587 });
3588 }
3589
3590 fn place_retained(&self, position: Point) {
3591 self.last_position.set(Some(position));
3592 let builder = self.frame.builder.borrow();
3593 let Some(builder) = builder.as_ref() else {
3594 return;
3595 };
3596 let placed = read_cell(&self.layout_state, |layout_state| {
3597 layout_state
3598 .as_ref()
3599 .map(|layout_state| layout_state.borrow_mut().place(position))
3600 .is_some()
3601 });
3602 if placed {
3603 return;
3604 }
3605 let Ok(mut applier) = builder.applier.try_borrow_typed() else {
3606 return;
3607 };
3608 let _ = applier.with_node::<SubcomposeLayoutNode, _>(self.node_id, |node| {
3609 node.set_position(position);
3610 });
3611 }
3612
3613 fn layout_intrinsic(&self, kind: IntrinsicKind) -> Option<f32> {
3617 let builder = self.frame.builder.borrow();
3618 let builder = builder.as_ref()?;
3619 match builder.layout_node_intrinsic(self.node_id, kind) {
3620 Ok(value) => value,
3621 Err(err) => {
3622 self.frame.record_error(err);
3623 Some(0.0)
3624 }
3625 }
3626 }
3627
3628 fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
3629 let builder = self.frame.builder.borrow();
3630 let builder = builder.as_ref().ok_or(NodeError::MissingContext {
3631 id: self.node_id,
3632 reason: "layout child applier not configured",
3633 })?;
3634 builder.measure_node(self.node_id, constraints)
3635 }
3636
3637 fn measure_cached(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
3638 if self.frame.measures() {
3639 self.measured_constraints.set(Some(constraints));
3640 }
3641 let cache = read_cell(&self.cache, Clone::clone);
3642 if let Some(cache) = &cache {
3643 cache.activate(self.cache_epoch.get());
3644 }
3645 if !self.force_remeasure.get()
3646 && let Some(cached) = cache
3647 .as_ref()
3648 .and_then(|cache| cache.get_measurement(constraints))
3649 {
3650 if self.frame.measures() {
3653 read_cell(&self.layout_state, |state| {
3654 if let Some(state) = state {
3655 state.borrow_mut().clear_placed();
3656 }
3657 });
3658 }
3659 return Some(cached);
3660 }
3661
3662 match self.perform_measure(constraints) {
3663 Ok(measured) => {
3664 self.force_remeasure.set(false);
3665 if let Some(cache) = &cache {
3666 cache.store_measurement(constraints, Rc::clone(&measured));
3667 }
3668 Some(measured)
3669 }
3670 Err(err) => {
3671 self.frame.record_error(err);
3672 None
3673 }
3674 }
3675 }
3676}
3677
3678struct LayoutChildMeasurable {
3679 state: Rc<LayoutChildMeasureState>,
3680}
3681
3682impl LayoutChildMeasurable {
3683 fn new(state: Rc<LayoutChildMeasureState>) -> Self {
3684 Self { state }
3685 }
3686
3687 fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
3688 if self.state.frame.builder.borrow().is_none() {
3689 return None;
3690 }
3691 self.state.parent_data.get()
3692 }
3693
3694 fn intrinsic(
3695 &self,
3696 kind: IntrinsicKind,
3697 constraints: Constraints,
3698 extent: fn(Size) -> f32,
3699 ) -> f32 {
3700 let state = &self.state;
3701 if state.frame.measures() {
3702 state.read_intrinsics.set(true);
3703 }
3704 let cache = read_cell(&state.cache, Clone::clone);
3705 if let Some(cache) = &cache {
3706 cache.activate(state.cache_epoch.get());
3707 }
3708 if !state.force_remeasure.get()
3709 && let Some(value) = cache.as_ref().and_then(|cache| cache.get_intrinsic(&kind))
3710 {
3711 return value;
3712 }
3713 let value = match state.layout_intrinsic(kind) {
3714 Some(value) => value,
3715 None => state
3716 .measure_cached(constraints)
3717 .map_or(0.0, |node| extent(node.size_for_parent())),
3718 };
3719 if let Some(cache) = &cache {
3720 cache.store_intrinsic(kind, value);
3721 }
3722 value
3723 }
3724}
3725
3726impl PlaceTarget for LayoutChildMeasureState {
3727 fn place(&self, x: f32, y: f32) {
3728 let internal_offset = read_cell(&self.measured, |measured| {
3729 measured
3730 .as_ref()
3731 .map(|measured| measured.offset)
3732 .unwrap_or_default()
3733 });
3734 self.place_retained(Point {
3735 x: x + internal_offset.x,
3736 y: y + internal_offset.y,
3737 });
3738 }
3739}
3740
3741impl Measurable for LayoutChildMeasurable {
3742 fn measured_hold(&self) -> Option<(Size, cranpose_ui_layout::ConstraintsHold)> {
3743 read_cell(&self.state.measured, |measured| {
3744 let measured = measured.as_ref()?;
3745 let hold = read_cell(&self.state.cache, |cache| cache.as_ref()?.hold_of(measured))?;
3746 Some((measured.size_for_parent(), hold))
3747 })
3748 }
3749
3750 fn measure(&self, constraints: Constraints) -> Placeable {
3751 let state = &self.state;
3752 let measured = state.measure_cached(constraints);
3753 let (measured_size, size_for_parent) = measured.as_ref().map_or(
3754 (
3755 Size {
3756 width: 0.0,
3757 height: 0.0,
3758 },
3759 Size {
3760 width: 0.0,
3761 height: 0.0,
3762 },
3763 ),
3764 |measured| (measured.size, measured.size_for_parent()),
3765 );
3766 read_cell(&state.layout_state, |layout_state| {
3767 if let Some(layout_state) = layout_state {
3768 layout_state.borrow_mut().set_size(measured_size);
3769 }
3770 });
3771 let alignment_lines = measured
3772 .as_ref()
3773 .map_or_else(AlignmentLines::default, |node| {
3774 node.alignment_lines_for_parent()
3775 });
3776 state.measured.set(measured);
3777
3778 Placeable::with_place_target(
3779 size_for_parent.width,
3780 size_for_parent.height,
3781 state.node_id,
3782 Rc::clone(&self.state) as Rc<dyn PlaceTarget>,
3783 )
3784 .with_alignment_lines(alignment_lines)
3785 }
3786
3787 fn min_intrinsic_width(&self, height: f32) -> f32 {
3788 self.intrinsic(
3789 IntrinsicKind::MinWidth(height),
3790 Constraints {
3791 min_width: 0.0,
3792 max_width: f32::INFINITY,
3793 min_height: height,
3794 max_height: height,
3795 },
3796 |size| size.width,
3797 )
3798 }
3799
3800 fn max_intrinsic_width(&self, height: f32) -> f32 {
3801 self.intrinsic(
3802 IntrinsicKind::MaxWidth(height),
3803 Constraints {
3804 min_width: 0.0,
3805 max_width: f32::INFINITY,
3806 min_height: 0.0,
3807 max_height: height,
3808 },
3809 |size| size.width,
3810 )
3811 }
3812
3813 fn min_intrinsic_height(&self, width: f32) -> f32 {
3814 self.intrinsic(
3815 IntrinsicKind::MinHeight(width),
3816 Constraints {
3817 min_width: width,
3818 max_width: width,
3819 min_height: 0.0,
3820 max_height: f32::INFINITY,
3821 },
3822 |size| size.height,
3823 )
3824 }
3825
3826 fn max_intrinsic_height(&self, width: f32) -> f32 {
3827 self.intrinsic(
3828 IntrinsicKind::MaxHeight(width),
3829 Constraints {
3830 min_width: 0.0,
3831 max_width: width,
3832 min_height: 0.0,
3833 max_height: f32::INFINITY,
3834 },
3835 |size| size.height,
3836 )
3837 }
3838
3839 fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3840 let parent_data = self.resolved_parent_data()?;
3841 if !parent_data.has_weight() {
3842 return None;
3843 }
3844 Some(cranpose_ui_layout::FlexParentData::new(
3845 parent_data.weight,
3846 parent_data.fill,
3847 ))
3848 }
3849
3850 fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3851 self.resolved_parent_data().unwrap_or_default()
3852 }
3853}
3854
3855#[derive(Clone)]
3856struct RuntimeNodeMetadata {
3857 modifier: Modifier,
3858 resolved_modifiers: ResolvedModifiers,
3859 modifier_slices: Rc<ModifierNodeSlices>,
3860 semantics: Option<Rc<SemanticsConfiguration>>,
3861 role: SemanticsRole,
3862 button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3863}
3864
3865impl Default for RuntimeNodeMetadata {
3866 fn default() -> Self {
3867 Self {
3868 modifier: Modifier::empty(),
3869 resolved_modifiers: ResolvedModifiers::default(),
3870 modifier_slices: Rc::default(),
3871 semantics: None,
3872 role: SemanticsRole::Unknown,
3873 button_handler: None,
3874 }
3875 }
3876}
3877
3878fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3879 modifier_slices
3880 .annotated_text()
3881 .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3882 value: SemanticsText(Rc::clone(&text)),
3883 })
3884}
3885
3886fn runtime_metadata_for(
3887 applier: &mut MemoryApplier,
3888 node_id: NodeId,
3889) -> Result<RuntimeNodeMetadata, NodeError> {
3890 if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3891 let modifier = layout.modifier.clone();
3892 let resolved_modifiers = layout.resolved_modifiers();
3893 let modifier_slices = layout.modifier_slices_snapshot();
3894 let role = role_from_modifier_slices(&modifier_slices);
3895
3896 RuntimeNodeMetadata {
3897 modifier,
3898 resolved_modifiers,
3899 modifier_slices,
3900 semantics: layout.semantics_configuration().map(Rc::new),
3901 role,
3902 button_handler: None,
3903 }
3904 }) {
3905 return Ok(meta);
3906 }
3907
3908 if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
3909 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3910 (
3911 node.modifier(),
3912 node.resolved_modifiers(),
3913 node.modifier_slices_snapshot(),
3914 node.semantics_configuration().map(Rc::new),
3915 )
3916 })
3917 {
3918 return Ok(RuntimeNodeMetadata {
3919 modifier,
3920 resolved_modifiers,
3921 modifier_slices,
3922 semantics,
3923 role: SemanticsRole::Subcompose,
3924 button_handler: None,
3925 });
3926 }
3927 Ok(RuntimeNodeMetadata::default())
3928}
3929
3930fn clear_semantics_dirty_flags(
3931 applier: &mut MemoryApplier,
3932 node: &MeasuredNode,
3933) -> Result<(), NodeError> {
3934 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3935 layout.clear_needs_semantics();
3936 }) {
3937 Ok(()) => {}
3938 Err(NodeError::Missing { .. }) => {}
3939 Err(NodeError::TypeMismatch { .. }) => {
3940 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3941 subcompose.clear_needs_semantics();
3942 }) {
3943 Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
3944 Err(err) => return Err(err),
3945 }
3946 }
3947 Err(err) => return Err(err),
3948 }
3949
3950 for child in &node.children {
3951 clear_semantics_dirty_flags(applier, &child.node.borrow())?;
3952 }
3953
3954 Ok(())
3955}
3956
3957fn build_semantics_tree_from_live_nodes(
3958 applier: &mut MemoryApplier,
3959 node: &MeasuredNode,
3960) -> Result<SemanticsTree, NodeError> {
3961 Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3962 applier, node, None,
3963 )?))
3964}
3965
3966fn semantics_node_from_parts(
3967 node_id: NodeId,
3968 node_generation: u32,
3969 mut role: SemanticsRole,
3970 config: Option<SemanticsConfiguration>,
3971 children: Vec<SemanticsNode>,
3972 held_details: Option<Box<SemanticsDetails>>,
3973 bounds: GeometryRect,
3974) -> SemanticsNode {
3975 let focusable = crate::focus_dispatch::has_focus_target(node_id);
3976 let mut node = SemanticsNode {
3977 node_id,
3978 bounds,
3979 node_generation,
3980 children,
3981 focusable,
3982 focused: focusable && crate::focus_dispatch::active_focus_target() == Some(node_id),
3983 details: held_details,
3984 ..SemanticsNode::default()
3985 };
3986 let details = match config {
3987 Some(mut config) => {
3988 if config.role == Some(SemanticsWidgetRole::Button) {
3989 role = SemanticsRole::Button;
3990 }
3991 if config.is_activatable() {
3992 node.actions.push(SemanticsAction::Click {
3993 handler: SemanticsCallback::new(node_id),
3994 });
3995 }
3996 let on_click_label = config.on_click_label.take().or_else(|| {
3997 config
3998 .on_click
3999 .as_ref()
4000 .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
4001 });
4002 node.widget_role = config.role;
4003 node.description = config.content_description.take();
4004 node.on_click = config.on_click.take();
4005 node.selected = config.selected;
4006 node.toggled = config.toggled;
4007 node.enabled = config.enabled;
4008 node.hidden = config.hidden;
4009 node.merge_descendants = config.merge_descendants;
4010 node.traversal_index = config.traversal_index;
4011 node.text = config.text.take();
4012 let is_modal = config.is_modal
4013 && modal_takes_space(Size {
4014 width: bounds.width,
4015 height: bounds.height,
4016 });
4017 SemanticsDetails::from_configuration(config, on_click_label, is_modal)
4018 }
4019 None => SemanticsDetails::NONE,
4020 };
4021 node.set_details(details);
4022 node.role = role;
4023 node
4024}
4025
4026#[inline(never)]
4027fn build_semantics_node_from_live_nodes(
4028 applier: &mut MemoryApplier,
4029 node: &MeasuredNode,
4030 origin: Option<Point>,
4031) -> Result<SemanticsNode, NodeError> {
4032 let (role, config, (bounds, content), placement) =
4033 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
4034 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
4035 let config = layout.semantics_configuration();
4036 layout.clear_needs_semantics();
4037 let state = layout.layout_state();
4038 (
4039 role,
4040 config,
4041 semantics_placement(&state, origin),
4042 SemanticsPlacement::of(&state),
4043 )
4044 }) {
4045 Ok(data) => data,
4046 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
4047 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
4048 subcompose.clear_needs_semantics();
4049 let state = subcompose.layout_state();
4050 (
4051 SemanticsRole::Subcompose,
4052 subcompose.semantics_configuration(),
4053 semantics_placement(&state, origin),
4054 SemanticsPlacement::of(&state),
4055 )
4056 }) {
4057 Ok(data) => data,
4058 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
4059 let top_left = origin.unwrap_or_default();
4060 (
4061 SemanticsRole::Unknown,
4062 None,
4063 (
4064 GeometryRect::from_origin_size(top_left, node.size),
4065 top_left,
4066 ),
4067 SemanticsPlacement::default(),
4068 )
4069 }
4070 Err(err) => return Err(err),
4071 }
4072 }
4073 Err(err) => return Err(err),
4074 };
4075
4076 let mut children = Vec::with_capacity(node.children.len());
4077 for child in &node.children {
4078 children.push(build_semantics_node_from_live_nodes(
4079 applier,
4080 &child.node.borrow(),
4081 Some(content),
4082 )?);
4083 }
4084
4085 Ok(SemanticsNode {
4086 placement,
4087 ..semantics_node_from_parts(
4088 node.node_id,
4089 applier.node_generation(node.node_id),
4090 role,
4091 config,
4092 children,
4093 None,
4094 bounds,
4095 )
4096 })
4097}
4098
4099fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
4100 stats.semantics_node_count += 1;
4101 stats.semantics_action_count += node.actions.len();
4102 stats.semantics_action_capacity += node.actions.capacity();
4103 stats.semantics_child_count += node.children.len();
4104 stats.semantics_child_capacity += node.children.capacity();
4105 stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
4106 stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
4107 if node.details.is_some() {
4108 stats.semantics_heap_bytes += size_of::<SemanticsDetails>();
4109 }
4110
4111 if let Some(description) = &node.description {
4112 stats.semantics_description_count += 1;
4113 stats.semantics_description_bytes += description.capacity();
4114 stats.semantics_heap_bytes += description.capacity();
4115 }
4116
4117 for child in &node.children {
4118 record_semantics_allocation_stats(child, stats);
4119 }
4120}
4121
4122fn record_layout_box_allocation_stats(
4123 layout_box: &LayoutBox,
4124 stats: &mut LayoutAllocationDebugStats,
4125) {
4126 stats.layout_box_count += 1;
4127 stats.layout_box_child_count += layout_box.children.len();
4128 stats.layout_box_child_capacity += layout_box.children.capacity();
4129 stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
4130 stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
4131
4132 for child in &layout_box.children {
4133 record_layout_box_allocation_stats(child, stats);
4134 }
4135}
4136
4137fn build_layout_tree(
4138 applier: &mut MemoryApplier,
4139 node: &MeasuredNode,
4140) -> Result<LayoutTree, NodeError> {
4141 fn place(
4142 applier: &mut MemoryApplier,
4143 node: &MeasuredNode,
4144 origin: Point,
4145 parent_transform: ProjectiveTransform,
4146 ) -> Result<LayoutBox, NodeError> {
4147 let top_left = Point {
4148 x: origin.x + node.offset.x,
4149 y: origin.y + node.offset.y,
4150 };
4151 let rect = GeometryRect {
4152 x: top_left.x,
4153 y: top_left.y,
4154 width: node.size.width,
4155 height: node.size.height,
4156 };
4157 let (data, window_transform) =
4158 snapshot_node_data(applier, node.node_id, top_left, node.size, parent_transform)?;
4159 let mut children = Vec::with_capacity(node.children.len());
4160 for child in &node.children {
4161 let child_node = child.node.borrow();
4162 if crate::modifier::is_window_root(applier, child_node.node_id) {
4163 continue;
4164 }
4165 let child_origin = Point {
4166 x: top_left.x + child.offset.x,
4167 y: top_left.y + child.offset.y,
4168 };
4169 children.push(place(applier, &child_node, child_origin, window_transform)?);
4170 }
4171 Ok(LayoutBox {
4172 node_generation: applier.node_generation(node.node_id),
4173 ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
4174 })
4175 }
4176
4177 Ok(LayoutTree::new(place(
4178 applier,
4179 node,
4180 Point { x: 0.0, y: 0.0 },
4181 ProjectiveTransform::identity(),
4182 )?))
4183}
4184
4185fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
4186 match &layout_box.node_data.kind {
4187 LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
4188 LayoutNodeKind::Spacer => SemanticsRole::Spacer,
4189 LayoutNodeKind::Unknown => SemanticsRole::Unknown,
4190 LayoutNodeKind::Button { .. } => SemanticsRole::Button,
4191 LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
4192 }
4193}
4194
4195#[inline(never)]
4196fn build_semantics_node_from_layout_box(layout_box: &LayoutBox, origin: Point) -> SemanticsNode {
4197 let rect = layout_box.rect;
4198 let content = Point {
4199 x: rect.x + layout_box.content_offset.x,
4200 y: rect.y + layout_box.content_offset.y,
4201 };
4202 let children = layout_box
4203 .children
4204 .iter()
4205 .map(|child| build_semantics_node_from_layout_box(child, content))
4206 .collect();
4207
4208 SemanticsNode {
4209 placement: SemanticsPlacement {
4210 position: Point {
4211 x: rect.x - origin.x,
4212 y: rect.y - origin.y,
4213 },
4214 content_offset: layout_box.content_offset,
4215 },
4216 ..semantics_node_from_parts(
4217 layout_box.node_id,
4218 layout_box.node_generation,
4219 semantics_role_from_layout_box(layout_box),
4220 layout_box.node_data.semantics().cloned(),
4221 children,
4222 None,
4223 rect,
4224 )
4225 }
4226}
4227
4228fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
4229 match &info.role {
4230 SemanticsRole::Layout => LayoutNodeKind::Layout,
4231 SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
4232 SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
4233 SemanticsRole::Spacer => LayoutNodeKind::Spacer,
4234 SemanticsRole::Button => {
4235 let handler = info
4236 .button_handler
4237 .as_ref()
4238 .cloned()
4239 .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
4240 LayoutNodeKind::Button { on_click: handler }
4241 }
4242 SemanticsRole::Unknown => LayoutNodeKind::Unknown,
4243 }
4244}
4245
4246fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
4247 let horizontal = padding.horizontal_sum();
4248 let vertical = padding.vertical_sum();
4249 let min_width = (constraints.min_width - horizontal).max(0.0);
4250 let mut max_width = constraints.max_width;
4251 if max_width.is_finite() {
4252 max_width = (max_width - horizontal).max(0.0);
4253 }
4254 let min_height = (constraints.min_height - vertical).max(0.0);
4255 let mut max_height = constraints.max_height;
4256 if max_height.is_finite() {
4257 max_height = (max_height - vertical).max(0.0);
4258 }
4259 normalize_constraints(Constraints {
4260 min_width,
4261 max_width,
4262 min_height,
4263 max_height,
4264 })
4265}
4266
4267fn resolve_dimension(
4268 base: f32,
4269 explicit: DimensionConstraint,
4270 min_override: Option<f32>,
4271 max_override: Option<f32>,
4272 min_limit: f32,
4273 max_limit: f32,
4274) -> f32 {
4275 let mut min_bound = min_limit;
4276 if let Some(min_value) = min_override {
4277 min_bound = min_bound.max(min_value);
4278 }
4279
4280 let mut max_bound = if max_limit.is_finite() {
4281 max_limit
4282 } else {
4283 max_override.unwrap_or(max_limit)
4284 };
4285 if let Some(max_value) = max_override {
4286 if max_bound.is_finite() {
4287 max_bound = max_bound.min(max_value);
4288 } else {
4289 max_bound = max_value;
4290 }
4291 }
4292 if max_bound < min_bound {
4293 max_bound = min_bound;
4294 }
4295
4296 let mut size = match explicit {
4297 DimensionConstraint::Points(points) => points,
4298 DimensionConstraint::Fraction(fraction) => {
4299 if max_limit.is_finite() {
4300 max_limit * fraction.clamp(0.0, 1.0)
4301 } else {
4302 base
4303 }
4304 }
4305 DimensionConstraint::Unspecified => base,
4306 DimensionConstraint::Intrinsic(_) => base,
4307 };
4308
4309 size = clamp_dimension(size, min_bound, max_bound);
4310 size = clamp_dimension(size, min_limit, max_limit);
4311 size.max(0.0)
4312}
4313
4314fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
4315 let mut result = value.max(min);
4316 if max.is_finite() {
4317 result = result.min(max);
4318 }
4319 result
4320}
4321
4322fn normalize_constraints(mut constraints: Constraints) -> Constraints {
4323 if constraints.max_width < constraints.min_width {
4324 constraints.max_width = constraints.min_width;
4325 }
4326 if constraints.max_height < constraints.min_height {
4327 constraints.max_height = constraints.min_height;
4328 }
4329 constraints
4330}
4331
4332#[cfg(test)]
4333#[path = "tests/layout_tests.rs"]
4334mod tests;
4335
4336#[cfg(test)]
4337#[path = "tests/semantics_update_tests.rs"]
4338mod semantics_update_tests;
4339
4340#[cfg(test)]
4341#[path = "tests/coordinator_geometry_tests.rs"]
4342mod coordinator_geometry_tests;