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