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::new(
1069 modifier,
1070 resolved_modifiers,
1071 modifier_slices,
1072 semantics,
1073 kind,
1074 );
1075 #[cfg(feature = "inspection")]
1076 let data = {
1077 let mut data = data;
1078 data.source_trace = applier
1079 .with_node::<LayoutNode, _>(node_id, |node| node.source_trace.clone())
1080 .unwrap_or_default();
1081 data
1082 };
1083 Ok((data, window_transform))
1084}
1085
1086fn place_layout_box(
1091 applier: &mut MemoryApplier,
1092 node_id: NodeId,
1093 parent_content_origin: Point,
1094 parent_transform: ProjectiveTransform,
1095 child_stack: &mut Vec<NodeId>,
1096) -> Result<Option<LayoutBox>, NodeError> {
1097 let first_child = child_stack.len();
1098 let Some(state) = read_layout_node(applier, node_id, |state, children| {
1099 if state.is_placed() {
1100 child_stack.extend_from_slice(children);
1101 }
1102 state
1103 })?
1104 else {
1105 return Ok(None);
1106 };
1107 if !state.is_placed() {
1108 return Ok(None);
1109 }
1110
1111 let top_left = Point {
1112 x: parent_content_origin.x + state.position().x,
1113 y: parent_content_origin.y + state.position().y,
1114 };
1115 let rect = GeometryRect {
1116 x: top_left.x,
1117 y: top_left.y,
1118 width: state.size().width,
1119 height: state.size().height,
1120 };
1121 let (data, window_transform) =
1122 snapshot_node_data(applier, node_id, top_left, state.size(), parent_transform)?;
1123 let child_origin = Point {
1124 x: top_left.x + state.content_offset().x,
1125 y: top_left.y + state.content_offset().y,
1126 };
1127 let end = child_stack.len();
1128 let mut children = Vec::with_capacity(end - first_child);
1129 for index in first_child..end {
1130 let child_id = child_stack[index];
1131 if crate::modifier::is_window_root(applier, child_id) {
1132 continue;
1133 }
1134 if let Some(child) = place_layout_box(
1135 applier,
1136 child_id,
1137 child_origin,
1138 window_transform,
1139 child_stack,
1140 )? {
1141 children.push(child);
1142 }
1143 }
1144 child_stack.truncate(first_child);
1145
1146 Ok(Some(LayoutBox {
1147 node_generation: applier.node_generation(node_id),
1148 ..LayoutBox::new(node_id, rect, state.content_offset(), data, children)
1149 }))
1150}
1151
1152pub fn top_modal_from_applier(
1157 applier: &mut MemoryApplier,
1158 root: NodeId,
1159) -> Result<Option<NodeId>, NodeError> {
1160 enum Step {
1165 Enter { node: NodeId, child: bool },
1166 Modal(NodeId),
1167 }
1168
1169 fn push_placed(
1170 steps: &mut Vec<Step>,
1171 node: NodeId,
1172 reach: cranpose_foundation::SemanticsReach,
1173 size: Size,
1174 children: impl IntoIterator<Item = NodeId>,
1175 ) {
1176 if reach.hidden {
1177 return;
1178 }
1179 if reach.is_modal && modal_takes_space(size) {
1180 steps.push(Step::Modal(node));
1181 }
1182 steps.extend(
1183 children
1184 .into_iter()
1185 .map(|node| Step::Enter { node, child: true }),
1186 );
1187 }
1188
1189 let mut steps = vec![Step::Enter {
1190 node: root,
1191 child: false,
1192 }];
1193 while let Some(step) = steps.pop() {
1194 let (node_id, child) = match step {
1195 Step::Modal(node) => return Ok(Some(node)),
1196 Step::Enter { node, child } => (node, child),
1197 };
1198 let node = match applier.get_mut(node_id) {
1199 Ok(node) => node.as_any_mut(),
1200 Err(NodeError::Missing { .. }) => continue,
1201 Err(error) => return Err(error),
1202 };
1203 if let Some(layout) = node.downcast_mut::<LayoutNode>() {
1204 let state = layout.layout_state();
1205 if state.is_placed() && !(child && layout.is_window_root()) {
1206 let children = layout.children.iter().copied();
1207 push_placed(
1208 &mut steps,
1209 node_id,
1210 layout.semantics_reach(),
1211 state.size(),
1212 children,
1213 );
1214 }
1215 } else if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
1216 let state = subcompose.layout_state();
1217 if state.is_placed() {
1218 let reach = subcompose.semantics_reach();
1219 subcompose.with_active_children(|children| {
1220 push_placed(
1221 &mut steps,
1222 node_id,
1223 reach,
1224 state.size(),
1225 children.iter().copied(),
1226 );
1227 });
1228 }
1229 }
1230 }
1231 Ok(None)
1232}
1233
1234#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1235pub struct MeasureLayoutOptions {
1236 pub collect_semantics: bool,
1237 pub build_layout_tree: bool,
1238}
1239
1240impl Default for MeasureLayoutOptions {
1241 fn default() -> Self {
1242 Self {
1243 collect_semantics: true,
1244 build_layout_tree: true,
1245 }
1246 }
1247}
1248
1249pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1257 Ok(applier.get_mut(root)?.layout_dirty())
1258}
1259
1260pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
1266 Ok(applier.get_mut(root)?.needs_semantics())
1267}
1268
1269#[cfg(test)]
1270pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
1271 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1272}
1273
1274pub fn measure_layout(
1276 applier: &mut MemoryApplier,
1277 root: NodeId,
1278 max_size: Size,
1279) -> Result<LayoutMeasurements, NodeError> {
1280 measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
1281}
1282
1283pub fn measure_layout_with_options(
1284 applier: &mut MemoryApplier,
1285 root: NodeId,
1286 max_size: Size,
1287 options: MeasureLayoutOptions,
1288) -> Result<LayoutMeasurements, NodeError> {
1289 let telemetry_start = Instant::now();
1290 crate::render_state::begin_text_layout_pass();
1291 begin_placement_pass();
1292 process_pending_layout_repasses(applier, root)?;
1293 let after_repasses = Instant::now();
1294
1295 let constraints = Constraints {
1296 min_width: 0.0,
1297 max_width: max_size.width,
1298 min_height: 0.0,
1299 max_height: max_size.height,
1300 };
1301
1302 let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
1303 .with_node::<LayoutNode, _>(root, |node| {
1304 (
1305 node.needs_measure() || Node::descendant_needs_measure(node),
1306 node.needs_semantics(),
1307 node.cache_handles().epoch(),
1308 )
1309 }) {
1310 Ok(tuple) => tuple,
1311 Err(NodeError::TypeMismatch { .. }) => {
1312 let node = applier.get_mut(root)?;
1313 let measure_dirty = node.needs_measure() || node.descendant_needs_measure();
1314 let semantics_dirty = node.needs_semantics();
1315 (measure_dirty, semantics_dirty, 0)
1316 }
1317 Err(err) => return Err(err),
1318 };
1319
1320 let epoch = if needs_remeasure {
1321 crate::render_state::next_layout_cache_epoch()
1322 } else if cached_epoch != 0 {
1323 cached_epoch
1324 } else {
1325 crate::render_state::current_layout_cache_epoch()
1326 };
1327
1328 let guard = ApplierSlotGuard::new(applier);
1329 let applier_host = guard.host();
1330 let slots_handle = guard.slots_handle();
1331 let after_guard = Instant::now();
1332
1333 let frame_arena = crate::render_state::take_layout_frame_arena();
1334 let builder = LayoutBuilder::new_with_epoch(
1335 Rc::clone(&applier_host),
1336 epoch,
1337 Rc::clone(&slots_handle),
1338 frame_arena,
1339 );
1340 let after_builder = Instant::now();
1341
1342 let measured = builder
1343 .state
1344 .measure_node(root, normalize_constraints(constraints))?;
1345 let after_measure = Instant::now();
1346
1347 if let Ok(mut applier) = applier_host.try_borrow_typed() {
1348 finish_placement(&mut applier, root);
1349 }
1350 let after_root_place = Instant::now();
1351
1352 let (layout_tree, semantics) = {
1353 let mut applier_ref = applier_host.borrow_typed();
1354 let layout_tree = if options.build_layout_tree {
1355 Some(build_layout_tree(&mut applier_ref, &measured)?)
1356 } else {
1357 None
1358 };
1359 let semantics = if options.collect_semantics {
1360 let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
1361 clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
1362 build_semantics_tree_from_layout_tree(layout_tree)
1363 } else {
1364 build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
1365 };
1366 Some(semantics_tree)
1367 } else {
1368 None
1369 };
1370 (layout_tree, semantics)
1371 };
1372 let after_aux = Instant::now();
1373
1374 drop(builder);
1375 let after_builder_drop = Instant::now();
1376
1377 drop(guard);
1378 let after_guard_drop = Instant::now();
1379
1380 log_layout_measure_telemetry(LayoutMeasureTelemetry {
1381 root,
1382 start: telemetry_start,
1383 after_repasses,
1384 after_guard,
1385 after_builder,
1386 after_measure,
1387 after_root_place,
1388 after_aux,
1389 after_builder_drop,
1390 after_guard_drop,
1391 });
1392
1393 Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
1394}
1395
1396fn process_pending_layout_repasses(
1397 applier: &mut MemoryApplier,
1398 root: NodeId,
1399) -> Result<(), NodeError> {
1400 for node_id in crate::render_state::take_modifier_slice_repass_nodes() {
1401 if let Ok(node) = applier.get_mut(node_id) {
1402 let any = node.as_any_mut();
1403 if let Some(layout) = any.downcast_mut::<crate::widgets::nodes::LayoutNode>() {
1404 layout.mark_modifier_slices_dirty();
1405 } else if let Some(subcompose) =
1406 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1407 {
1408 subcompose.mark_modifier_slices_dirty();
1409 }
1410 }
1411 }
1412 let measure_repass_nodes = crate::take_measure_repass_nodes();
1413 let repass_nodes = crate::take_layout_repass_nodes();
1414 if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
1415 return Ok(());
1416 }
1417 for node_id in measure_repass_nodes {
1418 cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
1419 }
1420 for node_id in repass_nodes {
1421 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1422 }
1423 applier.get_mut(root)?.mark_descendant_needs_layout(false);
1424 Ok(())
1425}
1426
1427struct LayoutBuilder {
1428 state: Rc<LayoutBuilderState>,
1429}
1430
1431impl LayoutBuilder {
1432 fn new_with_epoch(
1433 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1434 epoch: u64,
1435 slots: Rc<RefCell<SlotTable>>,
1436 frame_arena: FrameLayoutArena,
1437 ) -> Self {
1438 Self {
1439 state: Rc::new(LayoutBuilderState::new_with_epoch(
1440 applier,
1441 epoch,
1442 slots,
1443 frame_arena,
1444 )),
1445 }
1446 }
1447}
1448
1449impl Drop for LayoutBuilder {
1450 fn drop(&mut self) {
1451 if Rc::strong_count(&self.state) != 1 {
1452 return;
1453 }
1454 let Ok(mut frame_arena) = self.state.frame_arena.try_borrow_mut() else {
1455 return;
1456 };
1457 crate::render_state::replace_layout_frame_arena(std::mem::take(&mut *frame_arena));
1458 }
1459}
1460
1461struct LayoutBuilderState {
1462 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1463 runtime_handle: RefCell<Option<RuntimeHandle>>,
1464 slots: Rc<RefCell<SlotTable>>,
1465 cache_epoch: u64,
1466 cache_floor: u64,
1467 frame_arena: RefCell<FrameLayoutArena>,
1468}
1469
1470struct LayoutRuntimeFrameBindingCleanup<'a> {
1471 state: &'a RefCell<LayoutRuntimeState>,
1472}
1473
1474impl Drop for LayoutRuntimeFrameBindingCleanup<'_> {
1475 fn drop(&mut self) {
1476 self.state.borrow().unbind_children();
1477 }
1478}
1479
1480enum LayoutNodeVisit<'a> {
1481 Cached(Rc<MeasuredNode>),
1482 Keep(Rc<MeasuredNode>),
1485 Measure(LayoutNodeMeasure<'a>),
1486}
1487
1488#[derive(Clone, Copy)]
1490struct ChangedChild {
1491 constraints: Constraints,
1492 placed: bool,
1493}
1494
1495enum ChildChange {
1497 Unchanged,
1498 Changed(ChangedChild),
1499 Refuse,
1501}
1502
1503struct ChildView {
1506 self_dirty: bool,
1507 parent_data: Option<cranpose_ui_layout::ParentData>,
1510 cached_constraints: Option<Constraints>,
1512 read_intrinsics: bool,
1513 placed: bool,
1514}
1515
1516impl ChildView {
1517 fn of(
1519 node: &dyn Node,
1520 cache: &LayoutNodeCacheHandles,
1521 props: &dyn Fn() -> crate::modifier::LayoutProperties,
1522 placed: bool,
1523 ) -> Option<Self> {
1524 let self_dirty = node.needs_measure() || node.needs_layout();
1525 if !self_dirty && !node.descendant_needs_layout() {
1526 return None;
1527 }
1528 Some(Self {
1529 self_dirty,
1530 parent_data: self_dirty.then(|| parent_data_of(props())),
1531 cached_constraints: cache.measured_constraints(),
1532 read_intrinsics: cache.has_intrinsics(),
1533 placed,
1534 })
1535 }
1536}
1537
1538struct LayoutNodeMeasure<'a> {
1539 runtime_state: Rc<RefCell<LayoutRuntimeState>>,
1540 chain: ModifierChainInputs,
1541 pools: VecPools<'a>,
1542}
1543
1544impl LayoutBuilderState {
1545 fn new_with_epoch(
1546 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1547 epoch: u64,
1548 slots: Rc<RefCell<SlotTable>>,
1549 frame_arena: FrameLayoutArena,
1550 ) -> Self {
1551 let runtime_handle = applier.borrow_typed().runtime_handle();
1552
1553 Self {
1554 applier,
1555 runtime_handle: RefCell::new(runtime_handle),
1556 slots,
1557 cache_epoch: epoch,
1558 cache_floor: crate::render_state::layout_cache_floor(),
1559 frame_arena: RefCell::new(frame_arena),
1560 }
1561 }
1562
1563 fn with_applier_result<R>(
1564 &self,
1565 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1566 ) -> Result<R, NodeError> {
1567 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1568 return Err(NodeError::MissingContext {
1569 id: NodeId::default(),
1570 reason: "applier already borrowed",
1571 });
1572 };
1573 f(&mut applier)
1574 }
1575
1576 fn measure_subcompose_node(
1577 self: &Rc<Self>,
1578 node_id: NodeId,
1579 constraints: Constraints,
1580 ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1581 self.clear_subcompose_placed(node_id);
1582 if let Some(cached) = self.subcompose_measurement_to_keep(node_id, constraints)
1583 && let Some(kept) = self.keep_subcompose_measurement(node_id, constraints, &cached)?
1584 {
1585 return Ok(("subcompose kept", kept));
1586 }
1587 Ok(match self.try_measure_subcompose(node_id, constraints)? {
1588 Some(measured) => {
1589 self.store_subcompose_measurement(node_id, constraints, &measured);
1590 ("subcompose", measured)
1591 }
1592 None => (
1593 "fallback",
1594 Rc::new(MeasuredNode::new(
1595 node_id,
1596 Size::default(),
1597 Point { x: 0.0, y: 0.0 },
1598 Point::default(),
1599 Vec::new(),
1600 )),
1601 ),
1602 })
1603 }
1604
1605 fn subcompose_measurement_to_keep(
1609 &self,
1610 node_id: NodeId,
1611 constraints: Constraints,
1612 ) -> Option<Rc<MeasuredNode>> {
1613 let mut applier = self.applier.try_borrow_typed().ok()?;
1614 applier
1615 .with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1616 if node.needs_measure() || Node::needs_layout(node) {
1617 return None;
1618 }
1619 let cache = node.cache_handles();
1620 if cache.epoch() < self.cache_floor {
1621 return None;
1622 }
1623 cache.get_measurement(constraints)
1624 })
1625 .ok()
1626 .flatten()
1627 }
1628
1629 fn keep_subcompose_measurement(
1635 self: &Rc<Self>,
1636 node_id: NodeId,
1637 constraints: Constraints,
1638 cached: &Rc<MeasuredNode>,
1639 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1640 let kept = self.remeasure_changed_children(
1641 cached,
1642 |child_id| self.subcompose_child_change(child_id),
1643 |child_id| self.mark_measured_without_policy(child_id),
1644 )?;
1645 let Some(kept) = kept else {
1646 return Ok(None);
1647 };
1648 self.with_applier_result(|applier| {
1649 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1650 node.cache_handles()
1651 .store_measurement(constraints, Rc::clone(&kept));
1652 node.clear_needs_layout();
1653 })
1654 })?;
1655 Ok(Some(kept))
1656 }
1657
1658 fn subcompose_child_change(&self, child_id: NodeId) -> Result<ChildChange, NodeError> {
1662 self.child_change(child_id, |view| {
1663 match view.cached_constraints.filter(|_| !view.self_dirty) {
1664 Some(constraints) if !view.read_intrinsics => ChildChange::Changed(ChangedChild {
1665 constraints,
1666 placed: view.placed,
1667 }),
1668 _ => ChildChange::Refuse,
1669 }
1670 })
1671 }
1672
1673 fn child_change(
1676 &self,
1677 child_id: NodeId,
1678 decide: impl FnOnce(ChildView) -> ChildChange,
1679 ) -> Result<ChildChange, NodeError> {
1680 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1681 return Ok(ChildChange::Refuse);
1682 };
1683 Ok(
1684 match read_layout_child(&mut applier, child_id, ChildView::of)? {
1685 None => ChildChange::Refuse,
1686 Some(None) => ChildChange::Unchanged,
1687 Some(Some(view)) => decide(view),
1688 },
1689 )
1690 }
1691
1692 fn mark_measured_without_policy(&self, child_id: NodeId) {
1696 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1697 return;
1698 };
1699 if let Ok(node) = applier.get_mut(child_id) {
1700 node.mark_descendant_needs_layout(true);
1701 }
1702 }
1703
1704 fn clear_subcompose_placed(&self, node_id: NodeId) {
1705 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1706 return;
1707 };
1708 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1709 node.clear_placed();
1710 });
1711 }
1712
1713 fn measure_node(
1714 self: &Rc<Self>,
1715 node_id: NodeId,
1716 constraints: Constraints,
1717 ) -> Result<Rc<MeasuredNode>, NodeError> {
1718 let telemetry = layout_measure_telemetry_threshold_ms().map(|ms| (ms, Instant::now()));
1719 let (kind, measured) =
1720 if let Some(measured) = self.measure_layout_node(node_id, constraints)? {
1721 ("layout", measured)
1722 } else {
1723 self.measure_subcompose_node(node_id, constraints)?
1724 };
1725 if let Some(telemetry) = telemetry {
1726 log_node_measure_telemetry(kind, node_id, constraints, &measured, telemetry);
1727 }
1728 Ok(measured)
1729 }
1730
1731 fn cached_measure_node_with_applier(
1734 applier: &mut MemoryApplier,
1735 node_id: NodeId,
1736 constraints: Constraints,
1737 current_epoch: u64,
1738 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1739 let served = |cache: &LayoutNodeCacheHandles, dirty: bool| {
1740 let epoch = cache.epoch();
1741 if dirty || epoch == 0 || epoch != current_epoch {
1742 return None;
1743 }
1744 cache.get_measurement(constraints)
1745 };
1746
1747 match applier.with_node::<LayoutNode, _>(node_id, |node| {
1748 let measured = served(node.cache_handles(), Node::layout_dirty(node))?;
1749 node.set_measured_size(measured.size);
1750 Some(measured)
1751 }) {
1752 Ok(measured) => Ok(measured),
1753 Err(NodeError::TypeMismatch { .. }) => {
1754 match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1755 let measured = served(node.cache_handles(), Node::layout_dirty(node))?;
1756 node.set_measured_size(measured.size);
1757 Some(measured)
1758 }) {
1759 Ok(measured) => Ok(measured),
1760 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1761 Err(err) => Err(err),
1762 }
1763 }
1764 Err(NodeError::Missing { .. }) => Ok(None),
1765 Err(err) => Err(err),
1766 }
1767 }
1768
1769 fn try_measure_subcompose(
1770 self: &Rc<Self>,
1771 node_id: NodeId,
1772 constraints: Constraints,
1773 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1774 let (node_handle, resolved_modifiers) = {
1775 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1776 return Ok(None);
1777 };
1778 let node = match applier.get_mut(node_id) {
1779 Ok(node) => node,
1780 Err(NodeError::Missing { .. }) => return Ok(None),
1781 Err(err) => return Err(err),
1782 };
1783 let any = node.as_any_mut();
1784 if let Some(subcompose) =
1785 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1786 {
1787 let handle = subcompose.handle();
1788 let resolved_modifiers = handle
1789 .resolved_modifiers()
1790 .on_device_grid(subcompose.density().density());
1791 (handle, resolved_modifiers)
1792 } else {
1793 return Ok(None);
1794 }
1795 };
1796
1797 let runtime_handle = {
1798 let mut runtime_handle = self.runtime_handle.borrow_mut();
1799 if runtime_handle.is_none()
1800 && let Ok(applier) = self.applier.try_borrow_typed()
1801 {
1802 *runtime_handle = applier.runtime_handle();
1803 }
1804 runtime_handle.clone().ok_or(NodeError::MissingContext {
1805 id: node_id,
1806 reason: "runtime handle required for subcomposition",
1807 })?
1808 };
1809
1810 let props = resolved_modifiers.layout_properties();
1811 let padding = resolved_modifiers.padding();
1812 let offset = resolved_modifiers.offset();
1813 let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1814
1815 if let DimensionConstraint::Points(width) = props.width() {
1816 let constrained_width = width - padding.horizontal_sum();
1817 inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1818 inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1819 }
1820 if let DimensionConstraint::Points(height) = props.height() {
1821 let constrained_height = height - padding.vertical_sum();
1822 inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1823 inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1824 }
1825
1826 let mut slots_guard = SlotsGuard::take(&self.slots);
1827 let slots_host = slots_guard.host();
1828 let applier_host_dyn: Rc<dyn ApplierHost> = Rc::clone(&self.applier) as Rc<dyn ApplierHost>;
1829 let composer = Composer::new(
1830 Rc::clone(&slots_host),
1831 applier_host_dyn,
1832 runtime_handle,
1833 Some(node_id),
1834 );
1835 composer.enter_phase(Phase::Measure);
1836
1837 let measure_error = RefCell::new(None);
1838 let measured_children = node_handle.measured_children_scratch();
1839
1840 let measure_result = node_handle.measure_with_cached_batch(
1841 &composer,
1842 node_id,
1843 inner_constraints,
1844 CachedBatchMeasureInputs {
1845 measurer: Box::new(|child_id: NodeId, child_constraints: Constraints| {
1846 match self.measure_node(child_id, child_constraints) {
1847 Ok(measured) => {
1848 measured_children
1849 .borrow_mut()
1850 .insert(child_id, Rc::clone(&measured));
1851 let size = measured.size_for_parent();
1852 Placeable::value(size.width, size.height, child_id)
1853 .with_alignment_lines(measured.alignment_lines_for_parent())
1854 }
1855 Err(err) => {
1856 let mut slot = measure_error.borrow_mut();
1857 if slot.is_none() {
1858 *slot = Some(err);
1859 }
1860 Placeable::value(0.0, 0.0, child_id)
1861 }
1862 }
1863 }),
1864 cached_measure_batch_registrar: Box::new(
1865 |child_ids: &[NodeId],
1866 child_constraints: Constraints,
1867 out: &mut Vec<Option<Size>>| {
1868 out.clear();
1869 out.resize(child_ids.len(), None);
1870
1871 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1872 return;
1873 };
1874
1875 let mut measured_children = measured_children.borrow_mut();
1876 let current_epoch = crate::render_state::current_layout_cache_epoch();
1877 for (index, &child_id) in child_ids.iter().enumerate() {
1878 match Self::cached_measure_node_with_applier(
1879 &mut applier,
1880 child_id,
1881 child_constraints,
1882 current_epoch,
1883 ) {
1884 Ok(Some(measured)) => {
1885 out[index] = Some(measured.size);
1886 measured_children.insert(child_id, Rc::clone(&measured));
1887 }
1888 Ok(None) => {}
1889 Err(err) => {
1890 let mut slot = measure_error.borrow_mut();
1891 if slot.is_none() {
1892 *slot = Some(err);
1893 }
1894 break;
1895 }
1896 }
1897 }
1898 },
1899 ),
1900 retained_measure_lookup: Box::new(|child_id| {
1901 measured_children.borrow().get(&child_id).cloned()
1902 }),
1903 retained_measure_registrar: Box::new(|measurements| {
1904 let mut measured_children = measured_children.borrow_mut();
1905 for measured in measurements {
1906 measured_children.insert(measured.node_id(), Rc::clone(measured));
1907 }
1908 }),
1909 error: &measure_error,
1910 },
1911 )?;
1912 drop(composer);
1913 slots_guard.restore(slots_host.into_table()?);
1914
1915 if let Some(err) = measure_error.borrow_mut().take() {
1916 return Err(err);
1917 }
1918
1919 let cranpose_ui_layout::MeasureResult {
1920 size: measured_size,
1921 alignment_lines: explicit_lines,
1922 placements,
1923 } = measure_result;
1924
1925 let mut width = measured_size.width + padding.horizontal_sum();
1926 let mut height = measured_size.height + padding.vertical_sum();
1927
1928 width = resolve_dimension(
1929 width,
1930 props.width(),
1931 props.min_width(),
1932 props.max_width(),
1933 constraints.min_width,
1934 constraints.max_width,
1935 );
1936 height = resolve_dimension(
1937 height,
1938 props.height(),
1939 props.min_height(),
1940 props.max_height(),
1941 constraints.min_height,
1942 constraints.max_height,
1943 );
1944
1945 let mut children = Vec::with_capacity(placements.len());
1946 let mut alignment_lines = AlignmentLines::default();
1947 let mut measured_children_by_id = measured_children.borrow_mut();
1948
1949 if let Ok(mut applier) = self.applier.try_borrow_typed() {
1950 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1951 parent_node.set_measured_size(Size { width, height });
1952 parent_node.clear_needs_measure();
1953 parent_node.clear_needs_layout();
1954 });
1955 }
1956
1957 for placement in &placements {
1958 let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1959 measured
1960 } else {
1961 self.measure_node(placement.node_id, inner_constraints)?
1962 };
1963 let policy_position = Point {
1964 x: padding.left + placement.x,
1965 y: padding.top + placement.y,
1966 };
1967 let retained_position = Point {
1968 x: policy_position.x + child.offset.x,
1969 y: policy_position.y + child.offset.y,
1970 };
1971 alignment_lines.merge(
1972 child
1973 .alignment_lines_for_parent()
1974 .translated(policy_position.y),
1975 );
1976
1977 if let Ok(mut applier) = self.applier.try_borrow_typed()
1978 && applier
1979 .with_node::<LayoutNode, _>(placement.node_id, |node| {
1980 node.set_position(retained_position);
1981 })
1982 .is_err()
1983 {
1984 let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1985 node.set_position(retained_position);
1986 });
1987 }
1988
1989 children.push(MeasuredChild {
1990 node: RefCell::new(child),
1991 offset: policy_position,
1992 });
1993 }
1994
1995 node_handle.set_active_children(children.iter().map(|c| c.node.borrow().node_id));
1996 node_handle.recycle_placement_scratch(placements);
1997
1998 Ok(Some(Rc::new(
1999 MeasuredNode::new(
2000 node_id,
2001 Size { width, height },
2002 offset,
2003 Point::default(),
2004 children,
2005 )
2006 .with_alignment_lines(
2007 alignment_lines
2008 .with_overrides(explicit_lines.translated(padding.top))
2009 .translated(offset.y),
2010 ),
2011 )))
2012 }
2013
2014 fn measure_layout_node(
2015 self: &Rc<Self>,
2016 node_id: NodeId,
2017 constraints: Constraints,
2018 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2019 let mut keep = true;
2020 let (mut applier, measure) = loop {
2021 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2022 return Ok(None);
2023 };
2024 match applier.with_node::<LayoutNode, _>(node_id, |node| {
2025 self.visit_layout_node(node, constraints, keep)
2026 }) {
2027 Ok(LayoutNodeVisit::Measure(measure)) => break (applier, measure),
2028 Ok(LayoutNodeVisit::Cached(measured)) => return Ok(Some(measured)),
2029 Ok(LayoutNodeVisit::Keep(cached)) => {
2030 drop(applier);
2031 if let Some(kept) = self.keep_measurement(node_id, constraints, &cached)? {
2032 return Ok(Some(kept));
2033 }
2034 keep = false;
2035 }
2036 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
2037 Err(err) => return Err(err),
2038 }
2039 };
2040 let LayoutNodeMeasure {
2041 runtime_state,
2042 chain,
2043 mut pools,
2044 } = measure;
2045
2046 let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
2047 state: &runtime_state,
2048 };
2049 self.bind_layout_children(
2050 &mut applier,
2051 &runtime_state,
2052 &pools.child_ids,
2053 ChildPass::Measure,
2054 )?;
2055 drop(applier);
2056 pools.child_ids.clear();
2057
2058 let runtime_state = runtime_state.borrow();
2059 let measurement = self.measure_through_modifier_chain(
2060 node_id,
2061 &runtime_state,
2062 chain,
2063 constraints,
2064 &mut pools.placements,
2065 &mut pools.child_ids,
2066 );
2067
2068 if let Some(err) = runtime_state.take_child_error() {
2069 for child_state in runtime_state.child_states() {
2070 child_state.measured.take();
2071 }
2072 self.with_applier_result(|applier| {
2073 applier.with_node::<LayoutNode, _>(node_id, |node| {
2074 runtime_state.write_node_geometry(node_id, node, &measurement);
2075 })
2076 })
2077 .ok();
2078 return Err(err);
2079 }
2080
2081 let measured = Rc::new(
2082 MeasuredNode::new(
2083 node_id,
2084 measurement.size,
2085 measurement.offset,
2086 measurement.content_offset,
2087 runtime_state.measured_children(
2088 &pools.placements,
2089 &pools.child_ids,
2090 measurement.content_offset,
2091 ),
2092 )
2093 .with_alignment_lines(measurement.alignment_lines)
2094 .with_window_root(measurement.window_root),
2095 );
2096
2097 self.with_applier_result(|applier| {
2098 applier.with_node::<LayoutNode, _>(node_id, |node| {
2099 node.cache_handles().store_held_measurement(
2100 constraints,
2101 Rc::clone(&measured),
2102 measurement.hold,
2103 );
2104 runtime_state.write_node_geometry(node_id, node, &measurement);
2105 node.clear_needs_measure();
2106 node.clear_needs_layout();
2107 node.set_measured_size(measurement.size);
2108 node.set_content_offset(measurement.content_offset);
2109 })
2110 })
2111 .ok();
2112
2113 Ok(Some(measured))
2114 }
2115
2116 fn layout_node_intrinsic(
2121 self: &Rc<Self>,
2122 node_id: NodeId,
2123 kind: IntrinsicKind,
2124 ) -> Result<Option<f32>, NodeError> {
2125 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2126 return Ok(None);
2127 };
2128 let mut pools = VecPools::acquire(&self.frame_arena);
2129 let bound = applier.with_node::<LayoutNode, _>(node_id, |node| {
2130 let runtime_state = node.layout_runtime_state_handle();
2131 let chain = runtime_state.borrow_mut().bind_node(node);
2132 pools.child_ids.extend_from_slice(&node.children);
2133 (runtime_state, chain)
2134 });
2135 let (runtime_state, chain) = match bound {
2136 Ok(bound) => bound,
2137 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
2138 Err(err) => return Err(err),
2139 };
2140 let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
2141 state: &runtime_state,
2142 };
2143 self.bind_layout_children(
2144 &mut applier,
2145 &runtime_state,
2146 &pools.child_ids,
2147 ChildPass::Intrinsics,
2148 )?;
2149 drop(applier);
2150 pools.child_ids.clear();
2151
2152 let runtime_state = runtime_state.borrow();
2153 let value = if chain.uses_chain {
2154 let scope = crate::density::DensityMeasureScope::new(chain.density);
2155 let frame = CoordinatorFrame::new(
2156 &runtime_state.measure_policy,
2157 &scope,
2158 runtime_state.child_measurables(),
2159 runtime_state.child_states(),
2160 &mut pools.placements,
2161 &mut pools.child_ids,
2162 );
2163 runtime_state
2164 .coordinator_chain
2165 .intrinsic_from(0, &frame, kind)
2166 } else {
2167 policy_intrinsic(
2168 runtime_state.measure_policy.as_ref(),
2169 runtime_state.child_measurables(),
2170 kind,
2171 )
2172 };
2173 match runtime_state.take_child_error() {
2174 Some(err) => Err(err),
2175 None => Ok(Some(value)),
2176 }
2177 }
2178
2179 fn measurement_to_keep(
2182 &self,
2183 node: &LayoutNode,
2184 constraints: Constraints,
2185 ) -> Option<Rc<MeasuredNode>> {
2186 if node.needs_measure() || node.needs_layout() || node.is_virtual() {
2187 return None;
2188 }
2189 let cache = node.cache_handles();
2190 if cache.epoch() < self.cache_floor {
2191 return None;
2192 }
2193 cache.get_measurement(constraints)
2194 }
2195
2196 fn keep_measurement(
2202 self: &Rc<Self>,
2203 node_id: NodeId,
2204 constraints: Constraints,
2205 cached: &Rc<MeasuredNode>,
2206 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2207 let runtime_state = self.with_applier_result(|applier| {
2208 applier.with_node::<LayoutNode, _>(node_id, |node| node.layout_runtime_state_handle())
2209 })?;
2210 let mut next_state = 0;
2211 let kept = self.remeasure_changed_children(
2212 cached,
2213 |child_id| self.layout_child_change(&runtime_state, &mut next_state, child_id),
2214 |_| {},
2215 )?;
2216 let Some(kept) = kept else {
2217 return Ok(None);
2218 };
2219 self.with_applier_result(|applier| {
2220 applier.with_node::<LayoutNode, _>(node_id, |node| {
2221 node.cache_handles()
2222 .store_measurement(constraints, Rc::clone(&kept));
2223 node.clear_needs_layout();
2224 })
2225 })?;
2226 Ok(Some(kept))
2227 }
2228
2229 fn remeasure_changed_children(
2235 self: &Rc<Self>,
2236 cached: &Rc<MeasuredNode>,
2237 mut change: impl FnMut(NodeId) -> Result<ChildChange, NodeError>,
2238 mut remeasured: impl FnMut(NodeId),
2239 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2240 let mut replaced = smallvec::SmallVec::<[(usize, Rc<MeasuredNode>); 4]>::new();
2241 for (index, previous) in cached.children.iter().enumerate() {
2242 let previous = Rc::clone(&previous.node.borrow());
2243 let child_id = previous.node_id();
2244 let child = match change(child_id)? {
2245 ChildChange::Unchanged => continue,
2246 ChildChange::Refuse => return Ok(None),
2247 ChildChange::Changed(child) => child,
2248 };
2249 let measured = self.measure_node(child_id, child.constraints)?;
2250 remeasured(child_id);
2251 if !measures_the_same_for_parent(&previous, &measured) {
2252 return Ok(None);
2253 }
2254 if child.placed {
2255 self.place_where_it_was(child_id);
2256 }
2257 if !Rc::ptr_eq(&previous, &measured) {
2258 replaced.push((index, measured));
2259 }
2260 }
2261 for (index, measured) in replaced {
2262 if let Some(child) = cached.children.get(index) {
2263 *child.node.borrow_mut() = measured;
2264 }
2265 }
2266 Ok(Some(Rc::clone(cached)))
2267 }
2268
2269 fn layout_child_change(
2274 &self,
2275 runtime_state: &RefCell<LayoutRuntimeState>,
2276 next_state: &mut usize,
2277 child_id: NodeId,
2278 ) -> Result<ChildChange, NodeError> {
2279 let runtime_state = runtime_state.borrow();
2280 let Some((offset, state)) = runtime_state
2281 .child_states()
2282 .get(*next_state..)
2283 .unwrap_or_default()
2284 .iter()
2285 .enumerate()
2286 .find(|(_, state)| state.node_id == child_id)
2287 else {
2288 return Ok(ChildChange::Refuse);
2289 };
2290 *next_state += offset + 1;
2291 self.child_change(child_id, |view| {
2292 let parent_data_changed = view
2293 .parent_data
2294 .is_some_and(|data| Some(data) != state.parent_data.get());
2295 match state.measured_constraints.get() {
2296 Some(constraints) if !state.read_intrinsics.get() && !parent_data_changed => {
2297 ChildChange::Changed(ChangedChild {
2298 constraints,
2299 placed: view.placed,
2300 })
2301 }
2302 _ => ChildChange::Refuse,
2303 }
2304 })
2305 }
2306
2307 fn store_subcompose_measurement(
2310 &self,
2311 node_id: NodeId,
2312 constraints: Constraints,
2313 measured: &Rc<MeasuredNode>,
2314 ) {
2315 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2316 return;
2317 };
2318 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
2319 node.cache_handles()
2320 .store_measurement(constraints, Rc::clone(measured));
2321 });
2322 }
2323
2324 fn place_where_it_was(&self, child_id: NodeId) {
2327 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2328 return;
2329 };
2330 if applier
2331 .with_node::<LayoutNode, _>(child_id, |child| child.set_position(child.position()))
2332 .is_err()
2333 {
2334 let _ = applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2335 child.set_position(child.layout_state().position());
2336 });
2337 }
2338 }
2339
2340 fn visit_layout_node(
2341 &self,
2342 node: &mut LayoutNode,
2343 constraints: Constraints,
2344 keep: bool,
2345 ) -> LayoutNodeVisit<'_> {
2346 node.clear_placed();
2347 if keep && let Some(cached) = self.measurement_to_keep(node, constraints) {
2348 return LayoutNodeVisit::Keep(cached);
2349 }
2350 let cache = node.cache_handles();
2351 cache.activate(self.cache_epoch);
2352 if !Node::layout_dirty(node)
2353 && let Some(cached) = cache.get_measurement(constraints)
2354 {
2355 node.clear_needs_measure();
2356 node.clear_needs_layout();
2357 return LayoutNodeVisit::Cached(cached);
2358 }
2359
2360 let runtime_state = node.layout_runtime_state_handle();
2361 let chain = runtime_state.borrow_mut().bind_node(node);
2362 let mut pools = VecPools::acquire(&self.frame_arena);
2363 pools.child_ids.extend_from_slice(&node.children);
2364 LayoutNodeVisit::Measure(LayoutNodeMeasure {
2365 runtime_state,
2366 chain,
2367 pools,
2368 })
2369 }
2370
2371 fn bind_layout_children(
2372 self: &Rc<Self>,
2373 applier: &mut MemoryApplier,
2374 runtime_state: &RefCell<LayoutRuntimeState>,
2375 child_ids: &[NodeId],
2376 pass: ChildPass,
2377 ) -> Result<(), NodeError> {
2378 let mut runtime_state = runtime_state.borrow_mut();
2379 runtime_state.bind_children(self, pass, child_ids.len());
2380 let mut bound = 0;
2381 for &child_id in child_ids {
2382 if self.bind_layout_child(applier, &mut runtime_state, bound, child_id)? {
2383 bound += 1;
2384 }
2385 }
2386 runtime_state.truncate_children(bound);
2387 Ok(())
2388 }
2389
2390 fn bind_layout_child(
2391 &self,
2392 applier: &mut MemoryApplier,
2393 runtime_state: &mut LayoutRuntimeState,
2394 position: usize,
2395 child_id: NodeId,
2396 ) -> Result<bool, NodeError> {
2397 let bound = applier.with_node::<LayoutNode, _>(child_id, |child| {
2398 runtime_state.child_state_at(position, child_id).bind(
2399 LayoutChildBinding {
2400 cache: child.cache_handles(),
2401 layout_state: Some(child.layout_state_handle()),
2402 parent_data: Some(child.parent_data()),
2403 dirty: child.needs_layout() || child.needs_measure(),
2404 descendant_dirty: Node::descendant_needs_layout(child),
2405 },
2406 self,
2407 );
2408 });
2409 match bound {
2410 Ok(()) => Ok(true),
2411 Err(NodeError::TypeMismatch { .. }) => {
2412 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2413 runtime_state.child_state_at(position, child_id).bind(
2414 LayoutChildBinding {
2415 cache: child.cache_handles(),
2416 layout_state: None,
2417 parent_data: Some(parent_data_of(
2418 child.resolved_modifiers().layout_properties(),
2419 )),
2420 dirty: child.needs_layout() || child.needs_measure(),
2421 descendant_dirty: Node::descendant_needs_layout(child),
2422 },
2423 self,
2424 );
2425 }) {
2426 Ok(()) => Ok(true),
2427 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(false),
2428 Err(err) => Err(err),
2429 }
2430 }
2431 Err(NodeError::Missing { .. }) => Ok(false),
2432 Err(err) => Err(err),
2433 }
2434 }
2435
2436 fn measure_through_modifier_chain(
2437 &self,
2438 node_id: NodeId,
2439 runtime_state: &LayoutRuntimeState,
2440 chain: ModifierChainInputs,
2441 constraints: Constraints,
2442 placements: &mut Vec<Placement>,
2443 placement_indices: &mut Vec<usize>,
2444 ) -> ModifierChainMeasurement {
2445 let scope = crate::density::DensityMeasureScope::new(chain.density);
2446
2447 if !chain.uses_chain {
2448 let measurement = runtime_state.measure_policy.measure_into(
2449 &scope,
2450 runtime_state.child_measurables(),
2451 constraints,
2452 placements,
2453 );
2454 return ModifierChainMeasurement {
2455 size: measurement.size,
2456 alignment_lines: inherited_alignment_lines(
2457 runtime_state.child_states(),
2458 placements,
2459 placement_indices,
2460 )
2461 .with_overrides(measurement.alignment_lines),
2462 content_offset: Point::default(),
2463 offset: chain.offset,
2464 window_root: chain.window_root,
2465 uses_chain: false,
2466 hold: if chain.window_root {
2467 None
2468 } else {
2469 runtime_state.measure_policy.measure_hold(
2470 runtime_state.child_measurables(),
2471 constraints,
2472 measurement.size,
2473 )
2474 },
2475 };
2476 }
2477
2478 let frame = CoordinatorFrame::new(
2479 &runtime_state.measure_policy,
2480 &scope,
2481 runtime_state.child_measurables(),
2482 runtime_state.child_states(),
2483 placements,
2484 placement_indices,
2485 );
2486 let placeable = runtime_state
2487 .coordinator_chain
2488 .measure_from(0, &frame, constraints);
2489 let (content_x, content_y) = placeable.content_offset();
2490
2491 let invalidations = frame.take_invalidations();
2492 if !invalidations.is_empty() {
2493 self.with_applier_result(|applier| {
2494 applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2495 for kind in invalidations {
2496 match kind {
2497 InvalidationKind::Layout => layout_node.mark_needs_measure(),
2498 InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2499 InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2500 InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2501 InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2502 }
2503 }
2504 })
2505 })
2506 .ok();
2507 }
2508
2509 ModifierChainMeasurement {
2510 alignment_lines: placeable.alignment_lines(),
2511 size: Size {
2512 width: placeable.width(),
2513 height: placeable.height(),
2514 },
2515 content_offset: Point {
2516 x: content_x - chain.offset.x,
2517 y: content_y - chain.offset.y,
2518 },
2519 offset: chain.offset,
2520 window_root: chain.window_root,
2521 uses_chain: true,
2522 hold: if chain.window_root {
2523 None
2524 } else {
2525 runtime_state.coordinator_chain.measured_hold(
2526 cranpose_ui_layout::MeasureScope::density(&scope),
2527 runtime_state.measure_policy.as_ref(),
2528 runtime_state.child_measurables(),
2529 )
2530 },
2531 }
2532 }
2533}
2534
2535struct VecPools<'a> {
2536 arena: &'a RefCell<FrameLayoutArena>,
2537 child_ids: Vec<NodeId>,
2538 placements: Vec<Placement>,
2539}
2540
2541impl<'a> VecPools<'a> {
2542 fn acquire(arena: &'a RefCell<FrameLayoutArena>) -> Self {
2543 let mut pools = arena.borrow_mut();
2544 let child_ids = pools.tmp_child_ids.acquire();
2545 let placements = pools.tmp_placements.acquire();
2546 Self {
2547 arena,
2548 child_ids,
2549 placements,
2550 }
2551 }
2552}
2553
2554impl Drop for VecPools<'_> {
2555 fn drop(&mut self) {
2556 let mut pools = self.arena.borrow_mut();
2557 pools
2558 .tmp_child_ids
2559 .release(std::mem::take(&mut self.child_ids));
2560 pools
2561 .tmp_placements
2562 .release(std::mem::take(&mut self.placements));
2563 }
2564}
2565
2566struct SlotsGuard<'a> {
2567 table: &'a RefCell<SlotTable>,
2568 slots: Option<SlotTable>,
2569}
2570
2571impl<'a> SlotsGuard<'a> {
2572 fn take(table: &'a RefCell<SlotTable>) -> Self {
2573 let slots = std::mem::take(&mut *table.borrow_mut());
2574 Self {
2575 table,
2576 slots: Some(slots),
2577 }
2578 }
2579
2580 fn host(&mut self) -> Rc<SlotsHost> {
2581 let slots = self.slots.take().unwrap_or_default();
2582 Rc::new(SlotsHost::new(slots))
2583 }
2584
2585 fn restore(&mut self, slots: SlotTable) {
2586 debug_assert!(self.slots.is_none());
2587 self.slots = Some(slots);
2588 }
2589}
2590
2591impl Drop for SlotsGuard<'_> {
2592 fn drop(&mut self) {
2593 if let Some(slots) = self.slots.take() {
2594 *self.table.borrow_mut() = slots;
2595 }
2596 }
2597}
2598
2599#[derive(Debug, Clone)]
2600pub(crate) struct MeasuredNode {
2601 node_id: NodeId,
2602 size: Size,
2603 offset: Point,
2604 content_offset: Point,
2605 alignment_lines: AlignmentLines,
2606 children: Vec<MeasuredChild>,
2607 window_root: bool,
2608}
2609
2610impl MeasuredNode {
2611 fn new(
2612 node_id: NodeId,
2613 size: Size,
2614 offset: Point,
2615 content_offset: Point,
2616 children: Vec<MeasuredChild>,
2617 ) -> Self {
2618 Self {
2619 node_id,
2620 size,
2621 offset,
2622 content_offset,
2623 alignment_lines: AlignmentLines::default(),
2624 children,
2625 window_root: false,
2626 }
2627 }
2628
2629 fn with_window_root(mut self, window_root: bool) -> Self {
2630 self.window_root = window_root;
2631 self
2632 }
2633
2634 fn with_alignment_lines(mut self, alignment_lines: AlignmentLines) -> Self {
2635 self.alignment_lines = alignment_lines;
2636 self
2637 }
2638
2639 pub(crate) fn alignment_lines_for_parent(&self) -> AlignmentLines {
2640 if self.window_root {
2641 AlignmentLines::default()
2642 } else {
2643 self.alignment_lines
2644 }
2645 }
2646
2647 pub(crate) fn size_for_parent(&self) -> Size {
2648 if self.window_root {
2649 Size::new(0.0, 0.0)
2650 } else {
2651 self.size
2652 }
2653 }
2654
2655 #[cfg(test)]
2656 pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2657 Self::new(
2658 node_id,
2659 size,
2660 Point::default(),
2661 Point::default(),
2662 Vec::new(),
2663 )
2664 }
2665
2666 pub(crate) fn node_id(&self) -> NodeId {
2667 self.node_id
2668 }
2669
2670 pub(crate) fn size(&self) -> Size {
2671 self.size
2672 }
2673}
2674
2675#[derive(Debug, Clone)]
2676struct MeasuredChild {
2677 node: RefCell<Rc<MeasuredNode>>,
2680 offset: Point,
2681}
2682
2683fn measurable_intrinsic(measurable: &dyn Measurable, kind: IntrinsicKind) -> f32 {
2685 match kind {
2686 IntrinsicKind::MinWidth(height) => measurable.min_intrinsic_width(height),
2687 IntrinsicKind::MaxWidth(height) => measurable.max_intrinsic_width(height),
2688 IntrinsicKind::MinHeight(width) => measurable.min_intrinsic_height(width),
2689 IntrinsicKind::MaxHeight(width) => measurable.max_intrinsic_height(width),
2690 }
2691}
2692
2693fn policy_intrinsic(
2695 policy: &dyn MeasurePolicy,
2696 measurables: &[Box<dyn Measurable>],
2697 kind: IntrinsicKind,
2698) -> f32 {
2699 match kind {
2700 IntrinsicKind::MinWidth(height) => policy.min_intrinsic_width(measurables, height),
2701 IntrinsicKind::MaxWidth(height) => policy.max_intrinsic_width(measurables, height),
2702 IntrinsicKind::MinHeight(width) => policy.min_intrinsic_height(measurables, width),
2703 IntrinsicKind::MaxHeight(width) => policy.max_intrinsic_height(measurables, width),
2704 }
2705}
2706
2707fn layout_modifier_intrinsic(
2709 node: &dyn cranpose_foundation::LayoutModifierNode,
2710 wrapped: &dyn Measurable,
2711 kind: IntrinsicKind,
2712 density: f32,
2713) -> f32 {
2714 match kind {
2715 IntrinsicKind::MinWidth(height) => node.min_intrinsic_width(wrapped, height, density),
2716 IntrinsicKind::MaxWidth(height) => node.max_intrinsic_width(wrapped, height, density),
2717 IntrinsicKind::MinHeight(width) => node.min_intrinsic_height(wrapped, width, density),
2718 IntrinsicKind::MaxHeight(width) => node.max_intrinsic_height(wrapped, width, density),
2719 }
2720}
2721
2722struct CoordinatorFrame<'a> {
2723 measure_policy: &'a Rc<dyn MeasurePolicy>,
2724 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2725 measurables: &'a [Box<dyn Measurable>],
2726 child_states: &'a [Rc<LayoutChildMeasureState>],
2727 placements: RefCell<&'a mut Vec<Placement>>,
2728 placement_indices: RefCell<&'a mut Vec<usize>>,
2729 context: RefCell<LayoutNodeContext>,
2730}
2731
2732impl<'a> CoordinatorFrame<'a> {
2733 fn new(
2734 measure_policy: &'a Rc<dyn MeasurePolicy>,
2735 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2736 measurables: &'a [Box<dyn Measurable>],
2737 child_states: &'a [Rc<LayoutChildMeasureState>],
2738 placements: &'a mut Vec<Placement>,
2739 placement_indices: &'a mut Vec<usize>,
2740 ) -> Self {
2741 Self {
2742 measure_policy,
2743 scope,
2744 measurables,
2745 child_states,
2746 placements: RefCell::new(placements),
2747 placement_indices: RefCell::new(placement_indices),
2748 context: RefCell::new(LayoutNodeContext::new(scope.density())),
2749 }
2750 }
2751
2752 fn take_invalidations(&self) -> Vec<InvalidationKind> {
2753 self.context.borrow_mut().take_invalidations()
2754 }
2755}
2756
2757struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2758 chain: &'chain CoordinatorChain,
2759 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2760 index: usize,
2761 alignment_lines: Cell<AlignmentLines>,
2762}
2763
2764impl<'chain, 'frame_ref, 'frame_data> CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2765 fn new(
2766 chain: &'chain CoordinatorChain,
2767 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2768 index: usize,
2769 ) -> Self {
2770 Self {
2771 chain,
2772 frame,
2773 index,
2774 alignment_lines: Cell::default(),
2775 }
2776 }
2777}
2778
2779impl Measurable for CoordinatorLink<'_, '_, '_> {
2780 fn measure(&self, constraints: Constraints) -> Placeable {
2781 let placeable = self.chain.measure_from(self.index, self.frame, constraints);
2782 self.alignment_lines.set(placeable.alignment_lines());
2783 placeable
2784 }
2785
2786 fn min_intrinsic_width(&self, height: f32) -> f32 {
2787 self.chain
2788 .intrinsic_from(self.index, self.frame, IntrinsicKind::MinWidth(height))
2789 }
2790
2791 fn max_intrinsic_width(&self, height: f32) -> f32 {
2792 self.chain
2793 .intrinsic_from(self.index, self.frame, IntrinsicKind::MaxWidth(height))
2794 }
2795
2796 fn min_intrinsic_height(&self, width: f32) -> f32 {
2797 self.chain
2798 .intrinsic_from(self.index, self.frame, IntrinsicKind::MinHeight(width))
2799 }
2800
2801 fn max_intrinsic_height(&self, width: f32) -> f32 {
2802 self.chain
2803 .intrinsic_from(self.index, self.frame, IntrinsicKind::MaxHeight(width))
2804 }
2805}
2806
2807struct CoordinatorNode {
2808 modifier_index: usize,
2809 node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2810 measured_size: Cell<Size>,
2811 measured_constraints: Cell<Constraints>,
2813 accumulated_offset: Cell<Point>,
2814}
2815
2816impl CoordinatorNode {
2817 fn new(
2818 modifier_index: usize,
2819 node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2820 ) -> Self {
2821 Self {
2822 modifier_index,
2823 node,
2824 measured_size: Cell::new(Size::default()),
2825 measured_constraints: Cell::new(Constraints::tight(0.0, 0.0)),
2826 accumulated_offset: Cell::new(Point::default()),
2827 }
2828 }
2829
2830 fn matches(
2831 &self,
2832 modifier_index: usize,
2833 node: &Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2834 ) -> bool {
2835 self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2836 }
2837
2838 #[cfg(test)]
2839 fn ptr(&self) -> usize {
2840 Rc::as_ptr(&self.node) as *const () as usize
2841 }
2842}
2843
2844#[derive(Default)]
2845struct CoordinatorChain {
2846 nodes: Vec<CoordinatorNode>,
2847 inner_size: Cell<Size>,
2849 inner_constraints: Cell<Option<Constraints>>,
2851 synced: Option<(u64, ModifierChainInputs)>,
2855}
2856
2857impl CoordinatorChain {
2858 fn sync(&mut self, node: &LayoutNode) -> ModifierChainInputs {
2859 let density = node.density();
2860 let revision = node.modifier_chain().revision();
2861 if let Some((synced, inputs)) = self.synced
2862 && synced == revision
2863 && inputs.density == density
2864 {
2865 return inputs;
2866 }
2867 let inputs = self.walk(node, density);
2868 self.synced = Some((revision, inputs));
2869 inputs
2870 }
2871
2872 fn walk(&mut self, node: &LayoutNode, density: crate::density::Density) -> ModifierChainInputs {
2875 let mut inputs = ModifierChainInputs {
2876 density,
2877 window_root: node.is_window_root(),
2878 offset: Point::default(),
2879 uses_chain: false,
2880 };
2881 let chain_handle = node.modifier_chain();
2882 if !chain_handle.has_layout_nodes() {
2883 return inputs;
2884 }
2885
2886 let chain = chain_handle.chain();
2887 let mut len = 0;
2888 let mut matches = true;
2889 chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2890 let Some(index) = node_ref.entry_index() else {
2891 return;
2892 };
2893 if let Some(node) = chain.get_node_rc(index) {
2894 matches = matches
2895 && self
2896 .nodes
2897 .get(len)
2898 .is_some_and(|candidate| candidate.matches(index, node));
2899 len += 1;
2900 }
2901 node_ref.with_node(|node| {
2902 if let Some(offset_node) = node
2903 .as_any()
2904 .downcast_ref::<crate::modifier_nodes::OffsetNode>()
2905 {
2906 let delta = offset_node.device_offset(density.density());
2907 inputs.offset.x += delta.x;
2908 inputs.offset.y += delta.y;
2909 }
2910 });
2911 });
2912
2913 inputs.uses_chain = len > 0;
2914 if inputs.uses_chain && !(matches && len == self.nodes.len()) {
2915 self.rebuild(chain, len);
2916 }
2917 inputs
2918 }
2919
2920 fn rebuild(&mut self, chain: &cranpose_foundation::ModifierNodeChain, len: usize) {
2921 let mut previous_nodes = std::mem::take(&mut self.nodes);
2922 self.nodes.reserve_exact(len);
2925 chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2926 let Some((index, node)) = node_ref
2927 .entry_index()
2928 .and_then(|index| chain.get_node_rc(index).map(|node| (index, node)))
2929 else {
2930 return;
2931 };
2932 match previous_nodes
2933 .iter()
2934 .position(|candidate| candidate.matches(index, node))
2935 {
2936 Some(position) => self.nodes.push(previous_nodes.swap_remove(position)),
2937 None => self
2938 .nodes
2939 .push(CoordinatorNode::new(index, Rc::clone(node))),
2940 }
2941 });
2942 }
2943
2944 fn measured_hold(
2949 &self,
2950 density: f32,
2951 policy: &dyn MeasurePolicy,
2952 measurables: &[Box<dyn Measurable>],
2953 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
2954 let inner_size = self.inner_size.get();
2955 let mut wrapped = cranpose_ui_layout::WrappedHold {
2956 size: inner_size,
2957 hold: self
2958 .inner_constraints
2959 .get()
2960 .and_then(|constraints| policy.measure_hold(measurables, constraints, inner_size)),
2961 };
2962 for node in self.nodes.iter().rev() {
2963 let size = node.measured_size.get();
2964 let hold = match node.node.borrow().as_layout_node() {
2965 Some(layout) => {
2966 layout.measure_hold(density, node.measured_constraints.get(), size, wrapped)
2967 }
2968 None => wrapped.hold,
2969 };
2970 wrapped = cranpose_ui_layout::WrappedHold { size, hold };
2971 }
2972 wrapped.hold
2973 }
2974
2975 fn measure_from(
2976 &self,
2977 index: usize,
2978 frame: &CoordinatorFrame<'_>,
2979 constraints: Constraints,
2980 ) -> Placeable {
2981 let Some(node) = self.nodes.get(index) else {
2982 let mut placements = frame.placements.borrow_mut();
2983 let measurement = frame.measure_policy.measure_into(
2984 frame.scope,
2985 frame.measurables,
2986 constraints,
2987 &mut placements,
2988 );
2989 self.inner_size.set(measurement.size);
2990 self.inner_constraints.set(Some(constraints));
2991 return Placeable::value(
2992 measurement.size.width,
2993 measurement.size.height,
2994 NodeId::default(),
2995 )
2996 .with_alignment_lines(
2997 inherited_alignment_lines(
2998 frame.child_states,
2999 &placements,
3000 &mut frame.placement_indices.borrow_mut(),
3001 )
3002 .with_overrides(measurement.alignment_lines),
3003 );
3004 };
3005
3006 let wrapped = CoordinatorLink::new(self, frame, index + 1);
3007 let node_borrow = node.node.borrow();
3008 node.measured_constraints.set(constraints);
3009
3010 let Some(layout_node) = node_borrow.as_layout_node() else {
3011 let placeable = wrapped.measure(constraints);
3012 node.measured_size.set(Size {
3013 width: placeable.width(),
3014 height: placeable.height(),
3015 });
3016 let child_accumulated = self.total_content_offset_from(index + 1);
3017 node.accumulated_offset.set(child_accumulated);
3018 return placeable;
3019 };
3020
3021 let result = match frame.context.try_borrow_mut() {
3022 Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
3023 Err(_) => {
3024 let mut temp = LayoutNodeContext::new(frame.scope.density());
3025 let result = layout_node.measure(&mut temp, &wrapped, constraints);
3026 if let Ok(mut context) = frame.context.try_borrow_mut() {
3027 for kind in temp.take_invalidations() {
3028 context.invalidate(kind);
3029 }
3030 }
3031 result
3032 }
3033 };
3034
3035 node.measured_size.set(result.size);
3036 let local_offset = Point {
3037 x: result.placement_offset_x,
3038 y: result.placement_offset_y,
3039 };
3040 let child_accumulated = self.total_content_offset_from(index + 1);
3041 let accumulated = Point {
3042 x: local_offset.x + child_accumulated.x,
3043 y: local_offset.y + child_accumulated.y,
3044 };
3045 node.accumulated_offset.set(accumulated);
3046
3047 Placeable::value_with_offset(
3048 result.size.width,
3049 result.size.height,
3050 NodeId::default(),
3051 (accumulated.x, accumulated.y),
3052 )
3053 .with_alignment_lines(
3054 wrapped
3055 .alignment_lines
3056 .get()
3057 .translated(local_offset.y)
3058 .with_overrides(result.alignment_lines),
3059 )
3060 }
3061
3062 fn intrinsic_from(
3063 &self,
3064 index: usize,
3065 frame: &CoordinatorFrame<'_>,
3066 kind: IntrinsicKind,
3067 ) -> f32 {
3068 let Some(node) = self.nodes.get(index) else {
3069 return policy_intrinsic(frame.measure_policy.as_ref(), frame.measurables, kind);
3070 };
3071 let wrapped = CoordinatorLink::new(self, frame, index + 1);
3072 let node_borrow = node.node.borrow();
3073 node_borrow.as_layout_node().map_or_else(
3074 || measurable_intrinsic(&wrapped, kind),
3075 |layout_node| {
3076 layout_modifier_intrinsic(layout_node, &wrapped, kind, frame.scope.density())
3077 },
3078 )
3079 }
3080
3081 fn write_geometry(
3085 &self,
3086 geometry: &crate::modifier::CoordinatorGeometry,
3087 node_offset: Point,
3088 ) -> bool {
3089 let content = self.total_content_offset_from(0);
3090 let placed = |inner_offset: Point, size: Size| GeometryRect {
3091 x: content.x - inner_offset.x - node_offset.x,
3092 y: content.y - inner_offset.y - node_offset.y,
3093 width: size.width,
3094 height: size.height,
3095 };
3096 geometry.replace(
3097 self.nodes
3098 .iter()
3099 .map(|node| placed(node.accumulated_offset.get(), node.measured_size.get()))
3100 .chain(std::iter::once(placed(
3101 Point::default(),
3102 self.inner_size.get(),
3103 ))),
3104 )
3105 }
3106
3107 fn total_content_offset_from(&self, index: usize) -> Point {
3108 self.nodes
3109 .get(index)
3110 .map(|node| node.accumulated_offset.get())
3111 .unwrap_or_default()
3112 }
3113
3114 #[cfg(test)]
3115 fn debug_ptrs(&self) -> Vec<usize> {
3116 self.nodes.iter().map(CoordinatorNode::ptr).collect()
3117 }
3118}
3119
3120fn inherited_alignment_lines(
3121 child_states: &[Rc<LayoutChildMeasureState>],
3122 placements: &[Placement],
3123 placement_indices: &mut Vec<usize>,
3124) -> AlignmentLines {
3125 placement_indices.clear();
3126 let in_child_order = placements.len() == child_states.len()
3127 && placements
3128 .iter()
3129 .zip(child_states)
3130 .all(|(placement, child)| placement.node_id == child.node_id);
3131 if !in_child_order {
3132 placement_indices.extend(0..placements.len());
3133 placement_indices.sort_unstable_by_key(|&index| (placements[index].node_id, index));
3134 }
3135 let mut lines = AlignmentLines::default();
3136 for (index, child) in child_states.iter().enumerate() {
3137 let placement = placement_for_child(placements, placement_indices, index, child.node_id);
3138 read_cell(&child.measured, |measured| {
3139 if let Some(measured) = measured {
3140 lines.merge(
3141 measured
3142 .alignment_lines_for_parent()
3143 .translated(child.placement_position(placement).y),
3144 );
3145 }
3146 });
3147 }
3148 lines
3149}
3150
3151fn placement_for_child<'a>(
3152 placements: &'a [Placement],
3153 indices: &[usize],
3154 child_index: usize,
3155 node_id: NodeId,
3156) -> Option<&'a Placement> {
3157 let index = if indices.is_empty() {
3158 child_index
3159 } else {
3160 let first = indices.partition_point(|&index| placements[index].node_id < node_id);
3161 *indices.get(first)?
3162 };
3163 placements
3164 .get(index)
3165 .filter(|placement| placement.node_id == node_id)
3166}
3167
3168pub(crate) struct LayoutRuntimeState {
3169 children: Option<Box<LayoutChildren>>,
3171 coordinator_chain: CoordinatorChain,
3172 measure_policy: Rc<dyn MeasurePolicy>,
3173}
3174
3175struct LayoutChildren {
3177 ids: Vec<NodeId>,
3178 states: Vec<Rc<LayoutChildMeasureState>>,
3179 measurables: Vec<Box<dyn Measurable>>,
3180 frame: Rc<LayoutChildFrame>,
3181}
3182
3183impl LayoutChildren {
3184 fn with_capacity(count: usize) -> Self {
3188 Self {
3189 ids: Vec::with_capacity(count),
3190 states: Vec::with_capacity(count),
3191 measurables: Vec::with_capacity(count),
3192 frame: Rc::default(),
3193 }
3194 }
3195}
3196
3197impl LayoutRuntimeState {
3198 pub(crate) fn new(measure_policy: Rc<dyn MeasurePolicy>) -> Self {
3199 Self {
3200 children: None,
3201 coordinator_chain: CoordinatorChain::default(),
3202 measure_policy,
3203 }
3204 }
3205
3206 fn bind_node(&mut self, node: &LayoutNode) -> ModifierChainInputs {
3207 if !Rc::ptr_eq(&self.measure_policy, &node.measure_policy) {
3208 self.measure_policy = Rc::clone(&node.measure_policy);
3209 }
3210 self.coordinator_chain.sync(node)
3211 }
3212
3213 fn child_states(&self) -> &[Rc<LayoutChildMeasureState>] {
3214 self.children
3215 .as_deref()
3216 .map_or(&[], |children| children.states.as_slice())
3217 }
3218
3219 fn child_measurables(&self) -> &[Box<dyn Measurable>] {
3220 self.children
3221 .as_deref()
3222 .map_or(&[], |children| children.measurables.as_slice())
3223 }
3224
3225 fn bind_children(&mut self, builder: &Rc<LayoutBuilderState>, pass: ChildPass, count: usize) {
3228 if count == 0 && self.children.is_none() {
3229 return;
3230 }
3231 self.children
3232 .get_or_insert_with(|| Box::new(LayoutChildren::with_capacity(count)))
3233 .frame
3234 .bind(builder, pass);
3235 }
3236
3237 fn unbind_children(&self) {
3238 if let Some(children) = &self.children {
3239 children.frame.unbind();
3240 }
3241 }
3242
3243 fn take_child_error(&self) -> Option<NodeError> {
3245 self.children
3246 .as_ref()
3247 .and_then(|children| children.frame.error.borrow_mut().take())
3248 }
3249
3250 fn child_state_at(&mut self, position: usize, child_id: NodeId) -> &LayoutChildMeasureState {
3253 let children = self
3254 .children
3255 .get_or_insert_with(|| Box::new(LayoutChildren::with_capacity(position + 1)));
3256 if children.ids.get(position) != Some(&child_id) {
3257 let from = match children.ids[position..]
3258 .iter()
3259 .position(|&id| id == child_id)
3260 {
3261 Some(offset) => position + offset,
3262 None => {
3263 let state = LayoutChildMeasureState::new(child_id, Rc::clone(&children.frame));
3264 children.ids.push(child_id);
3265 children.states.push(Rc::clone(&state));
3266 children
3267 .measurables
3268 .push(Box::new(LayoutChildMeasurable::new(state)));
3269 children.ids.len() - 1
3270 }
3271 };
3272 children.ids.swap(position, from);
3273 children.states.swap(position, from);
3274 children.measurables.swap(position, from);
3275 }
3276 &children.states[position]
3277 }
3278
3279 fn truncate_children(&mut self, len: usize) {
3280 if let Some(children) = &mut self.children {
3281 children.ids.truncate(len);
3282 children.states.truncate(len);
3283 children.measurables.truncate(len);
3284 }
3285 }
3286
3287 fn measured_children(
3288 &self,
3289 placements: &[Placement],
3290 placement_indices: &[usize],
3291 content_offset: Point,
3292 ) -> Vec<MeasuredChild> {
3293 let child_states = self.child_states();
3294 let mut measured_children = Vec::with_capacity(child_states.len());
3295 for (index, child_state) in child_states.iter().enumerate() {
3296 let Some(measured) = child_state.measured.take() else {
3297 continue;
3298 };
3299 let placement =
3300 placement_for_child(placements, placement_indices, index, child_state.node_id);
3301 let base_position = child_state.placement_position(placement);
3302 if placement.is_some() {
3303 child_state.place_retained(Point {
3304 x: base_position.x + measured.offset.x,
3305 y: base_position.y + measured.offset.y,
3306 });
3307 }
3308 measured_children.push(MeasuredChild {
3309 node: RefCell::new(measured),
3310 offset: Point {
3311 x: content_offset.x + base_position.x,
3312 y: content_offset.y + base_position.y,
3313 },
3314 });
3315 }
3316 measured_children
3317 }
3318
3319 fn write_node_geometry(
3320 &self,
3321 node_id: NodeId,
3322 node: &LayoutNode,
3323 measurement: &ModifierChainMeasurement,
3324 ) {
3325 let geometry = node.coordinator_geometry();
3326 let moved = if measurement.uses_chain {
3327 self.coordinator_chain
3328 .write_geometry(geometry, measurement.offset)
3329 } else {
3330 geometry.replace([GeometryRect {
3331 x: 0.0,
3332 y: 0.0,
3333 width: measurement.size.width,
3334 height: measurement.size.height,
3335 }])
3336 };
3337 if moved {
3338 crate::render_state::record_geometry_scene_node(node_id);
3339 }
3340 }
3341
3342 #[cfg(test)]
3343 pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
3344 LayoutRuntimeDebugStats {
3345 child_ids: self
3346 .children
3347 .as_ref()
3348 .map(|children| children.ids.clone())
3349 .unwrap_or_default(),
3350 child_state_ptrs: self
3351 .child_states()
3352 .iter()
3353 .map(|state| Rc::as_ptr(state) as *const () as usize)
3354 .collect(),
3355 child_measurable_ptrs: self
3356 .child_measurables()
3357 .iter()
3358 .map(|measurable| {
3359 measurable.as_ref() as *const dyn Measurable as *const () as usize
3360 })
3361 .collect(),
3362 child_measurable_count: self.child_measurables().len(),
3363 coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
3364 coordinator_node_count: self.coordinator_chain.nodes.len(),
3365 }
3366 }
3367}
3368
3369#[cfg(test)]
3370#[derive(Debug, Clone, PartialEq, Eq)]
3371pub(crate) struct LayoutRuntimeDebugStats {
3372 pub(crate) child_ids: Vec<NodeId>,
3373 pub(crate) child_state_ptrs: Vec<usize>,
3374 pub(crate) child_measurable_ptrs: Vec<usize>,
3375 pub(crate) child_measurable_count: usize,
3376 pub(crate) coordinator_node_ptrs: Vec<usize>,
3377 pub(crate) coordinator_node_count: usize,
3378}
3379
3380#[derive(Default)]
3381struct LayoutChildFrame {
3382 builder: RefCell<Option<Rc<LayoutBuilderState>>>,
3383 error: RefCell<Option<NodeError>>,
3384 pass: Cell<ChildPass>,
3385}
3386
3387#[derive(Clone, Copy, Default, PartialEq, Eq)]
3389enum ChildPass {
3390 #[default]
3393 Measure,
3394 Intrinsics,
3397}
3398
3399impl LayoutChildFrame {
3400 fn bind(&self, builder: &Rc<LayoutBuilderState>, pass: ChildPass) {
3401 self.error.borrow_mut().take();
3402 *self.builder.borrow_mut() = Some(Rc::clone(builder));
3403 self.pass.set(pass);
3404 }
3405
3406 fn measures(&self) -> bool {
3407 self.pass.get() == ChildPass::Measure
3408 }
3409
3410 fn unbind(&self) {
3411 self.builder.borrow_mut().take();
3412 }
3413
3414 fn record_error(&self, err: NodeError) {
3415 if self.builder.borrow().is_none() {
3416 return;
3417 }
3418 let mut slot = self.error.borrow_mut();
3419 if slot.is_none() {
3420 *slot = Some(err);
3421 }
3422 }
3423}
3424
3425struct LayoutChildBinding<'a> {
3426 cache: &'a LayoutNodeCacheHandles,
3427 layout_state: Option<&'a Rc<RefCell<LayoutState>>>,
3428 parent_data: Option<cranpose_ui_layout::ParentData>,
3429 dirty: bool,
3431 descendant_dirty: bool,
3433}
3434
3435fn read_layout_child<R>(
3438 applier: &mut MemoryApplier,
3439 child_id: NodeId,
3440 read: impl Fn(
3441 &dyn Node,
3442 &LayoutNodeCacheHandles,
3443 &dyn Fn() -> crate::modifier::LayoutProperties,
3444 bool,
3445 ) -> R,
3446) -> Result<Option<R>, NodeError> {
3447 match applier.with_node::<LayoutNode, _>(child_id, |child| {
3448 read(
3449 &*child,
3450 child.cache_handles(),
3451 &|| child.resolved_modifiers().layout_properties(),
3452 child.is_placed(),
3453 )
3454 }) {
3455 Ok(value) => Ok(Some(value)),
3456 Err(NodeError::TypeMismatch { .. }) => {
3457 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
3458 read(
3459 &*child,
3460 child.cache_handles(),
3461 &|| child.resolved_modifiers().layout_properties(),
3462 child.layout_state().is_placed(),
3463 )
3464 }) {
3465 Ok(value) => Ok(Some(value)),
3466 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
3467 Err(err) => Err(err),
3468 }
3469 }
3470 Err(NodeError::Missing { .. }) => Ok(None),
3471 Err(err) => Err(err),
3472 }
3473}
3474
3475fn measures_the_same_for_parent(previous: &MeasuredNode, measured: &MeasuredNode) -> bool {
3479 let (before, after) = (
3480 previous.alignment_lines_for_parent(),
3481 measured.alignment_lines_for_parent(),
3482 );
3483 previous.size_for_parent() == measured.size_for_parent()
3484 && previous.offset == measured.offset
3485 && before.first_baseline() == after.first_baseline()
3486 && before.last_baseline() == after.last_baseline()
3487}
3488
3489pub(crate) fn parent_data_of(
3490 props: crate::modifier::LayoutProperties,
3491) -> cranpose_ui_layout::ParentData {
3492 let weight = props.weight().unwrap_or_default();
3493 cranpose_ui_layout::ParentData {
3494 weight: weight.weight,
3495 fill: weight.fill,
3496 box_alignment: props.box_alignment(),
3497 row_alignment: props.row_alignment(),
3498 row_baseline: props.row_baseline(),
3499 column_alignment: props.column_alignment(),
3500 }
3501}
3502
3503struct LayoutChildMeasureState {
3507 node_id: NodeId,
3508 frame: Rc<LayoutChildFrame>,
3509 cache: Cell<Option<LayoutNodeCacheHandles>>,
3511 cache_epoch: Cell<u64>,
3512 force_remeasure: Cell<bool>,
3513 parent_data: Cell<Option<cranpose_ui_layout::ParentData>>,
3514 measured_constraints: Cell<Option<Constraints>>,
3516 read_intrinsics: Cell<bool>,
3518 measured: Cell<Option<Rc<MeasuredNode>>>,
3519 last_position: Cell<Option<Point>>,
3520 layout_state: Cell<Option<Rc<RefCell<LayoutState>>>>,
3521}
3522
3523fn read_cell<T: Default, R>(cell: &Cell<T>, read: impl FnOnce(&T) -> R) -> R {
3526 let value = cell.take();
3527 let result = read(&value);
3528 cell.set(value);
3529 result
3530}
3531
3532impl LayoutChildMeasureState {
3533 fn placement_position(&self, placement: Option<&Placement>) -> Point {
3534 placement
3535 .map(|placement| Point {
3536 x: placement.x,
3537 y: placement.y,
3538 })
3539 .or_else(|| self.last_position.get())
3540 .unwrap_or_default()
3541 }
3542
3543 fn new(node_id: NodeId, frame: Rc<LayoutChildFrame>) -> Rc<Self> {
3544 Rc::new(Self {
3545 node_id,
3546 frame,
3547 cache: Cell::new(None),
3548 cache_epoch: Cell::new(0),
3549 force_remeasure: Cell::new(true),
3550 parent_data: Cell::new(None),
3551 measured_constraints: Cell::new(None),
3552 read_intrinsics: Cell::new(false),
3553 measured: Cell::new(None),
3554 last_position: Cell::new(None),
3555 layout_state: Cell::new(None),
3556 })
3557 }
3558
3559 fn bind(&self, binding: LayoutChildBinding<'_>, pass: &LayoutBuilderState) {
3563 let child_epoch = binding.cache.epoch();
3564 let stale = binding.dirty || child_epoch < pass.cache_floor;
3565 let cache_epoch = if stale { pass.cache_epoch } else { child_epoch };
3566 binding.cache.activate(cache_epoch);
3567 let mut cache = self.cache.take();
3568 match &mut cache {
3569 Some(cache) => cache.clone_from(binding.cache),
3570 None => cache = Some(binding.cache.clone()),
3571 }
3572 self.cache.set(cache);
3573 self.cache_epoch.set(cache_epoch);
3574 self.force_remeasure.set(stale || binding.descendant_dirty);
3575 self.parent_data.set(binding.parent_data);
3576 if self.frame.measures() {
3577 self.measured.take();
3578 self.last_position.set(None);
3579 self.measured_constraints.set(None);
3580 self.read_intrinsics.set(false);
3581 }
3582 let layout_state = self.layout_state.take();
3583 let shared = layout_state
3584 .as_ref()
3585 .zip(binding.layout_state)
3586 .is_some_and(|(current, bound)| Rc::ptr_eq(current, bound));
3587 self.layout_state.set(if shared {
3588 layout_state
3589 } else {
3590 binding.layout_state.cloned()
3591 });
3592 }
3593
3594 fn place_retained(&self, position: Point) {
3595 self.last_position.set(Some(position));
3596 let builder = self.frame.builder.borrow();
3597 let Some(builder) = builder.as_ref() else {
3598 return;
3599 };
3600 let placed = read_cell(&self.layout_state, |layout_state| {
3601 layout_state
3602 .as_ref()
3603 .map(|layout_state| layout_state.borrow_mut().place(position))
3604 .is_some()
3605 });
3606 if placed {
3607 return;
3608 }
3609 let Ok(mut applier) = builder.applier.try_borrow_typed() else {
3610 return;
3611 };
3612 let _ = applier.with_node::<SubcomposeLayoutNode, _>(self.node_id, |node| {
3613 node.set_position(position);
3614 });
3615 }
3616
3617 fn layout_intrinsic(&self, kind: IntrinsicKind) -> Option<f32> {
3621 let builder = self.frame.builder.borrow();
3622 let builder = builder.as_ref()?;
3623 match builder.layout_node_intrinsic(self.node_id, kind) {
3624 Ok(value) => value,
3625 Err(err) => {
3626 self.frame.record_error(err);
3627 Some(0.0)
3628 }
3629 }
3630 }
3631
3632 fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
3633 let builder = self.frame.builder.borrow();
3634 let builder = builder.as_ref().ok_or(NodeError::MissingContext {
3635 id: self.node_id,
3636 reason: "layout child applier not configured",
3637 })?;
3638 builder.measure_node(self.node_id, constraints)
3639 }
3640
3641 fn measure_cached(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
3642 if self.frame.measures() {
3643 self.measured_constraints.set(Some(constraints));
3644 }
3645 let cache = read_cell(&self.cache, Clone::clone);
3646 if let Some(cache) = &cache {
3647 cache.activate(self.cache_epoch.get());
3648 }
3649 if !self.force_remeasure.get()
3650 && let Some(cached) = cache
3651 .as_ref()
3652 .and_then(|cache| cache.get_measurement(constraints))
3653 {
3654 if self.frame.measures() {
3657 read_cell(&self.layout_state, |state| {
3658 if let Some(state) = state {
3659 state.borrow_mut().clear_placed();
3660 }
3661 });
3662 }
3663 return Some(cached);
3664 }
3665
3666 match self.perform_measure(constraints) {
3667 Ok(measured) => {
3668 self.force_remeasure.set(false);
3669 if let Some(cache) = &cache {
3670 cache.store_measurement(constraints, Rc::clone(&measured));
3671 }
3672 Some(measured)
3673 }
3674 Err(err) => {
3675 self.frame.record_error(err);
3676 None
3677 }
3678 }
3679 }
3680}
3681
3682struct LayoutChildMeasurable {
3683 state: Rc<LayoutChildMeasureState>,
3684}
3685
3686impl LayoutChildMeasurable {
3687 fn new(state: Rc<LayoutChildMeasureState>) -> Self {
3688 Self { state }
3689 }
3690
3691 fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
3692 if self.state.frame.builder.borrow().is_none() {
3693 return None;
3694 }
3695 self.state.parent_data.get()
3696 }
3697
3698 fn intrinsic(
3699 &self,
3700 kind: IntrinsicKind,
3701 constraints: Constraints,
3702 extent: fn(Size) -> f32,
3703 ) -> f32 {
3704 let state = &self.state;
3705 if state.frame.measures() {
3706 state.read_intrinsics.set(true);
3707 }
3708 let cache = read_cell(&state.cache, Clone::clone);
3709 if let Some(cache) = &cache {
3710 cache.activate(state.cache_epoch.get());
3711 }
3712 if !state.force_remeasure.get()
3713 && let Some(value) = cache.as_ref().and_then(|cache| cache.get_intrinsic(&kind))
3714 {
3715 return value;
3716 }
3717 let value = match state.layout_intrinsic(kind) {
3718 Some(value) => value,
3719 None => state
3720 .measure_cached(constraints)
3721 .map_or(0.0, |node| extent(node.size_for_parent())),
3722 };
3723 if let Some(cache) = &cache {
3724 cache.store_intrinsic(kind, value);
3725 }
3726 value
3727 }
3728}
3729
3730impl PlaceTarget for LayoutChildMeasureState {
3731 fn place(&self, x: f32, y: f32) {
3732 let internal_offset = read_cell(&self.measured, |measured| {
3733 measured
3734 .as_ref()
3735 .map(|measured| measured.offset)
3736 .unwrap_or_default()
3737 });
3738 self.place_retained(Point {
3739 x: x + internal_offset.x,
3740 y: y + internal_offset.y,
3741 });
3742 }
3743}
3744
3745impl Measurable for LayoutChildMeasurable {
3746 fn measured_hold(&self) -> Option<(Size, cranpose_ui_layout::ConstraintsHold)> {
3747 read_cell(&self.state.measured, |measured| {
3748 let measured = measured.as_ref()?;
3749 let hold = read_cell(&self.state.cache, |cache| cache.as_ref()?.hold_of(measured))?;
3750 Some((measured.size_for_parent(), hold))
3751 })
3752 }
3753
3754 fn measure(&self, constraints: Constraints) -> Placeable {
3755 let state = &self.state;
3756 let measured = state.measure_cached(constraints);
3757 let (measured_size, size_for_parent) = measured.as_ref().map_or(
3758 (
3759 Size {
3760 width: 0.0,
3761 height: 0.0,
3762 },
3763 Size {
3764 width: 0.0,
3765 height: 0.0,
3766 },
3767 ),
3768 |measured| (measured.size, measured.size_for_parent()),
3769 );
3770 read_cell(&state.layout_state, |layout_state| {
3771 if let Some(layout_state) = layout_state {
3772 layout_state.borrow_mut().set_size(measured_size);
3773 }
3774 });
3775 let alignment_lines = measured
3776 .as_ref()
3777 .map_or_else(AlignmentLines::default, |node| {
3778 node.alignment_lines_for_parent()
3779 });
3780 state.measured.set(measured);
3781
3782 Placeable::with_place_target(
3783 size_for_parent.width,
3784 size_for_parent.height,
3785 state.node_id,
3786 Rc::clone(&self.state) as Rc<dyn PlaceTarget>,
3787 )
3788 .with_alignment_lines(alignment_lines)
3789 }
3790
3791 fn min_intrinsic_width(&self, height: f32) -> f32 {
3792 self.intrinsic(
3793 IntrinsicKind::MinWidth(height),
3794 Constraints {
3795 min_width: 0.0,
3796 max_width: f32::INFINITY,
3797 min_height: height,
3798 max_height: height,
3799 },
3800 |size| size.width,
3801 )
3802 }
3803
3804 fn max_intrinsic_width(&self, height: f32) -> f32 {
3805 self.intrinsic(
3806 IntrinsicKind::MaxWidth(height),
3807 Constraints {
3808 min_width: 0.0,
3809 max_width: f32::INFINITY,
3810 min_height: 0.0,
3811 max_height: height,
3812 },
3813 |size| size.width,
3814 )
3815 }
3816
3817 fn min_intrinsic_height(&self, width: f32) -> f32 {
3818 self.intrinsic(
3819 IntrinsicKind::MinHeight(width),
3820 Constraints {
3821 min_width: width,
3822 max_width: width,
3823 min_height: 0.0,
3824 max_height: f32::INFINITY,
3825 },
3826 |size| size.height,
3827 )
3828 }
3829
3830 fn max_intrinsic_height(&self, width: f32) -> f32 {
3831 self.intrinsic(
3832 IntrinsicKind::MaxHeight(width),
3833 Constraints {
3834 min_width: 0.0,
3835 max_width: width,
3836 min_height: 0.0,
3837 max_height: f32::INFINITY,
3838 },
3839 |size| size.height,
3840 )
3841 }
3842
3843 fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3844 let parent_data = self.resolved_parent_data()?;
3845 if !parent_data.has_weight() {
3846 return None;
3847 }
3848 Some(cranpose_ui_layout::FlexParentData::new(
3849 parent_data.weight,
3850 parent_data.fill,
3851 ))
3852 }
3853
3854 fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3855 self.resolved_parent_data().unwrap_or_default()
3856 }
3857}
3858
3859#[derive(Clone)]
3860struct RuntimeNodeMetadata {
3861 modifier: Modifier,
3862 resolved_modifiers: ResolvedModifiers,
3863 modifier_slices: Rc<ModifierNodeSlices>,
3864 semantics: Option<Rc<SemanticsConfiguration>>,
3865 role: SemanticsRole,
3866 button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3867}
3868
3869impl Default for RuntimeNodeMetadata {
3870 fn default() -> Self {
3871 Self {
3872 modifier: Modifier::empty(),
3873 resolved_modifiers: ResolvedModifiers::default(),
3874 modifier_slices: Rc::default(),
3875 semantics: None,
3876 role: SemanticsRole::Unknown,
3877 button_handler: None,
3878 }
3879 }
3880}
3881
3882fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3883 modifier_slices
3884 .annotated_text()
3885 .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3886 value: SemanticsText(Rc::clone(&text)),
3887 })
3888}
3889
3890fn runtime_metadata_for(
3891 applier: &mut MemoryApplier,
3892 node_id: NodeId,
3893) -> Result<RuntimeNodeMetadata, NodeError> {
3894 if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3895 let modifier = layout.modifier.clone();
3896 let resolved_modifiers = layout.resolved_modifiers();
3897 let modifier_slices = layout.modifier_slices_snapshot();
3898 let role = role_from_modifier_slices(&modifier_slices);
3899
3900 RuntimeNodeMetadata {
3901 modifier,
3902 resolved_modifiers,
3903 modifier_slices,
3904 semantics: layout.semantics_configuration().map(Rc::new),
3905 role,
3906 button_handler: None,
3907 }
3908 }) {
3909 return Ok(meta);
3910 }
3911
3912 if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
3913 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3914 (
3915 node.modifier(),
3916 node.resolved_modifiers(),
3917 node.modifier_slices_snapshot(),
3918 node.semantics_configuration().map(Rc::new),
3919 )
3920 })
3921 {
3922 return Ok(RuntimeNodeMetadata {
3923 modifier,
3924 resolved_modifiers,
3925 modifier_slices,
3926 semantics,
3927 role: SemanticsRole::Subcompose,
3928 button_handler: None,
3929 });
3930 }
3931 Ok(RuntimeNodeMetadata::default())
3932}
3933
3934fn clear_semantics_dirty_flags(
3935 applier: &mut MemoryApplier,
3936 node: &MeasuredNode,
3937) -> Result<(), NodeError> {
3938 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3939 layout.clear_needs_semantics();
3940 }) {
3941 Ok(()) => {}
3942 Err(NodeError::Missing { .. }) => {}
3943 Err(NodeError::TypeMismatch { .. }) => {
3944 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3945 subcompose.clear_needs_semantics();
3946 }) {
3947 Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
3948 Err(err) => return Err(err),
3949 }
3950 }
3951 Err(err) => return Err(err),
3952 }
3953
3954 for child in &node.children {
3955 clear_semantics_dirty_flags(applier, &child.node.borrow())?;
3956 }
3957
3958 Ok(())
3959}
3960
3961fn build_semantics_tree_from_live_nodes(
3962 applier: &mut MemoryApplier,
3963 node: &MeasuredNode,
3964) -> Result<SemanticsTree, NodeError> {
3965 Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3966 applier, node, None,
3967 )?))
3968}
3969
3970fn semantics_node_from_parts(
3971 node_id: NodeId,
3972 node_generation: u32,
3973 mut role: SemanticsRole,
3974 config: Option<SemanticsConfiguration>,
3975 children: Vec<SemanticsNode>,
3976 held_details: Option<Box<SemanticsDetails>>,
3977 bounds: GeometryRect,
3978) -> SemanticsNode {
3979 let focusable = crate::focus_dispatch::has_focus_target(node_id);
3980 let mut node = SemanticsNode {
3981 node_id,
3982 bounds,
3983 node_generation,
3984 children,
3985 focusable,
3986 focused: focusable && crate::focus_dispatch::active_focus_target() == Some(node_id),
3987 details: held_details,
3988 ..SemanticsNode::default()
3989 };
3990 let details = match config {
3991 Some(mut config) => {
3992 if config.role == Some(SemanticsWidgetRole::Button) {
3993 role = SemanticsRole::Button;
3994 }
3995 if config.is_activatable() {
3996 node.actions.push(SemanticsAction::Click {
3997 handler: SemanticsCallback::new(node_id),
3998 });
3999 }
4000 let on_click_label = config.on_click_label.take().or_else(|| {
4001 config
4002 .on_click
4003 .as_ref()
4004 .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
4005 });
4006 node.widget_role = config.role;
4007 node.description = config.content_description.take();
4008 node.on_click = config.on_click.take();
4009 node.selected = config.selected;
4010 node.toggled = config.toggled;
4011 node.enabled = config.enabled;
4012 node.hidden = config.hidden;
4013 node.merge_descendants = config.merge_descendants;
4014 node.traversal_index = config.traversal_index;
4015 node.text = config.text.take();
4016 let is_modal = config.is_modal
4017 && modal_takes_space(Size {
4018 width: bounds.width,
4019 height: bounds.height,
4020 });
4021 SemanticsDetails::from_configuration(config, on_click_label, is_modal)
4022 }
4023 None => SemanticsDetails::NONE,
4024 };
4025 node.set_details(details);
4026 node.role = role;
4027 node
4028}
4029
4030fn build_semantics_node_from_live_nodes(
4031 applier: &mut MemoryApplier,
4032 node: &MeasuredNode,
4033 origin: Option<Point>,
4034) -> Result<SemanticsNode, NodeError> {
4035 let (role, config, (bounds, content), placement) =
4036 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
4037 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
4038 let config = layout.semantics_configuration();
4039 layout.clear_needs_semantics();
4040 let state = layout.layout_state();
4041 (
4042 role,
4043 config,
4044 semantics_placement(&state, origin),
4045 SemanticsPlacement::of(&state),
4046 )
4047 }) {
4048 Ok(data) => data,
4049 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
4050 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
4051 subcompose.clear_needs_semantics();
4052 let state = subcompose.layout_state();
4053 (
4054 SemanticsRole::Subcompose,
4055 subcompose.semantics_configuration(),
4056 semantics_placement(&state, origin),
4057 SemanticsPlacement::of(&state),
4058 )
4059 }) {
4060 Ok(data) => data,
4061 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
4062 let top_left = origin.unwrap_or_default();
4063 (
4064 SemanticsRole::Unknown,
4065 None,
4066 (
4067 GeometryRect::from_origin_size(top_left, node.size),
4068 top_left,
4069 ),
4070 SemanticsPlacement::default(),
4071 )
4072 }
4073 Err(err) => return Err(err),
4074 }
4075 }
4076 Err(err) => return Err(err),
4077 };
4078
4079 let mut children = Vec::with_capacity(node.children.len());
4080 for child in &node.children {
4081 children.push(build_semantics_node_from_live_nodes(
4082 applier,
4083 &child.node.borrow(),
4084 Some(content),
4085 )?);
4086 }
4087
4088 Ok(SemanticsNode {
4089 placement,
4090 ..semantics_node_from_parts(
4091 node.node_id,
4092 applier.node_generation(node.node_id),
4093 role,
4094 config,
4095 children,
4096 None,
4097 bounds,
4098 )
4099 })
4100}
4101
4102fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
4103 stats.semantics_node_count += 1;
4104 stats.semantics_action_count += node.actions.len();
4105 stats.semantics_action_capacity += node.actions.capacity();
4106 stats.semantics_child_count += node.children.len();
4107 stats.semantics_child_capacity += node.children.capacity();
4108 stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
4109 stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
4110 if node.details.is_some() {
4111 stats.semantics_heap_bytes += size_of::<SemanticsDetails>();
4112 }
4113
4114 if let Some(description) = &node.description {
4115 stats.semantics_description_count += 1;
4116 stats.semantics_description_bytes += description.capacity();
4117 stats.semantics_heap_bytes += description.capacity();
4118 }
4119
4120 for child in &node.children {
4121 record_semantics_allocation_stats(child, stats);
4122 }
4123}
4124
4125fn record_layout_box_allocation_stats(
4126 layout_box: &LayoutBox,
4127 stats: &mut LayoutAllocationDebugStats,
4128) {
4129 stats.layout_box_count += 1;
4130 stats.layout_box_child_count += layout_box.children.len();
4131 stats.layout_box_child_capacity += layout_box.children.capacity();
4132 stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
4133 stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
4134
4135 for child in &layout_box.children {
4136 record_layout_box_allocation_stats(child, stats);
4137 }
4138}
4139
4140fn build_layout_tree(
4141 applier: &mut MemoryApplier,
4142 node: &MeasuredNode,
4143) -> Result<LayoutTree, NodeError> {
4144 fn place(
4145 applier: &mut MemoryApplier,
4146 node: &MeasuredNode,
4147 origin: Point,
4148 parent_transform: ProjectiveTransform,
4149 ) -> Result<LayoutBox, NodeError> {
4150 let top_left = Point {
4151 x: origin.x + node.offset.x,
4152 y: origin.y + node.offset.y,
4153 };
4154 let rect = GeometryRect {
4155 x: top_left.x,
4156 y: top_left.y,
4157 width: node.size.width,
4158 height: node.size.height,
4159 };
4160 let (data, window_transform) =
4161 snapshot_node_data(applier, node.node_id, top_left, node.size, parent_transform)?;
4162 let mut children = Vec::with_capacity(node.children.len());
4163 for child in &node.children {
4164 let child_node = child.node.borrow();
4165 if crate::modifier::is_window_root(applier, child_node.node_id) {
4166 continue;
4167 }
4168 let child_origin = Point {
4169 x: top_left.x + child.offset.x,
4170 y: top_left.y + child.offset.y,
4171 };
4172 children.push(place(applier, &child_node, child_origin, window_transform)?);
4173 }
4174 Ok(LayoutBox {
4175 node_generation: applier.node_generation(node.node_id),
4176 ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
4177 })
4178 }
4179
4180 Ok(LayoutTree::new(place(
4181 applier,
4182 node,
4183 Point { x: 0.0, y: 0.0 },
4184 ProjectiveTransform::identity(),
4185 )?))
4186}
4187
4188fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
4189 match &layout_box.node_data.kind {
4190 LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
4191 LayoutNodeKind::Spacer => SemanticsRole::Spacer,
4192 LayoutNodeKind::Unknown => SemanticsRole::Unknown,
4193 LayoutNodeKind::Button { .. } => SemanticsRole::Button,
4194 LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
4195 }
4196}
4197
4198fn build_semantics_node_from_layout_box(layout_box: &LayoutBox, origin: Point) -> SemanticsNode {
4199 let rect = layout_box.rect;
4200 let content = Point {
4201 x: rect.x + layout_box.content_offset.x,
4202 y: rect.y + layout_box.content_offset.y,
4203 };
4204 let children = layout_box
4205 .children
4206 .iter()
4207 .map(|child| build_semantics_node_from_layout_box(child, content))
4208 .collect();
4209
4210 SemanticsNode {
4211 placement: SemanticsPlacement {
4212 position: Point {
4213 x: rect.x - origin.x,
4214 y: rect.y - origin.y,
4215 },
4216 content_offset: layout_box.content_offset,
4217 },
4218 ..semantics_node_from_parts(
4219 layout_box.node_id,
4220 layout_box.node_generation,
4221 semantics_role_from_layout_box(layout_box),
4222 layout_box.node_data.semantics().cloned(),
4223 children,
4224 None,
4225 rect,
4226 )
4227 }
4228}
4229
4230fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
4231 match &info.role {
4232 SemanticsRole::Layout => LayoutNodeKind::Layout,
4233 SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
4234 SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
4235 SemanticsRole::Spacer => LayoutNodeKind::Spacer,
4236 SemanticsRole::Button => {
4237 let handler = info
4238 .button_handler
4239 .as_ref()
4240 .cloned()
4241 .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
4242 LayoutNodeKind::Button { on_click: handler }
4243 }
4244 SemanticsRole::Unknown => LayoutNodeKind::Unknown,
4245 }
4246}
4247
4248fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
4249 let horizontal = padding.horizontal_sum();
4250 let vertical = padding.vertical_sum();
4251 let min_width = (constraints.min_width - horizontal).max(0.0);
4252 let mut max_width = constraints.max_width;
4253 if max_width.is_finite() {
4254 max_width = (max_width - horizontal).max(0.0);
4255 }
4256 let min_height = (constraints.min_height - vertical).max(0.0);
4257 let mut max_height = constraints.max_height;
4258 if max_height.is_finite() {
4259 max_height = (max_height - vertical).max(0.0);
4260 }
4261 normalize_constraints(Constraints {
4262 min_width,
4263 max_width,
4264 min_height,
4265 max_height,
4266 })
4267}
4268
4269fn resolve_dimension(
4270 base: f32,
4271 explicit: DimensionConstraint,
4272 min_override: Option<f32>,
4273 max_override: Option<f32>,
4274 min_limit: f32,
4275 max_limit: f32,
4276) -> f32 {
4277 let mut min_bound = min_limit;
4278 if let Some(min_value) = min_override {
4279 min_bound = min_bound.max(min_value);
4280 }
4281
4282 let mut max_bound = if max_limit.is_finite() {
4283 max_limit
4284 } else {
4285 max_override.unwrap_or(max_limit)
4286 };
4287 if let Some(max_value) = max_override {
4288 if max_bound.is_finite() {
4289 max_bound = max_bound.min(max_value);
4290 } else {
4291 max_bound = max_value;
4292 }
4293 }
4294 if max_bound < min_bound {
4295 max_bound = min_bound;
4296 }
4297
4298 let mut size = match explicit {
4299 DimensionConstraint::Points(points) => points,
4300 DimensionConstraint::Fraction(fraction) => {
4301 if max_limit.is_finite() {
4302 max_limit * fraction.clamp(0.0, 1.0)
4303 } else {
4304 base
4305 }
4306 }
4307 DimensionConstraint::Unspecified => base,
4308 DimensionConstraint::Intrinsic(_) => base,
4309 };
4310
4311 size = clamp_dimension(size, min_bound, max_bound);
4312 size = clamp_dimension(size, min_limit, max_limit);
4313 size.max(0.0)
4314}
4315
4316fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
4317 let mut result = value.max(min);
4318 if max.is_finite() {
4319 result = result.min(max);
4320 }
4321 result
4322}
4323
4324fn normalize_constraints(mut constraints: Constraints) -> Constraints {
4325 if constraints.max_width < constraints.min_width {
4326 constraints.max_width = constraints.min_width;
4327 }
4328 if constraints.max_height < constraints.min_height {
4329 constraints.max_height = constraints.min_height;
4330 }
4331 constraints
4332}
4333
4334#[cfg(test)]
4335#[path = "tests/layout_tests.rs"]
4336mod tests;
4337
4338#[cfg(test)]
4339#[path = "tests/semantics_update_tests.rs"]
4340mod semantics_update_tests;
4341
4342#[cfg(test)]
4343#[path = "tests/coordinator_geometry_tests.rs"]
4344mod coordinator_geometry_tests;