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