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