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