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 composer = Composer::new(
1669 Rc::clone(&slots_host),
1670 applier_host_dyn,
1671 runtime_handle,
1672 Some(node_id),
1673 );
1674 composer.enter_phase(Phase::Measure);
1675
1676 let measure_error = RefCell::new(None);
1677 let measured_children = node_handle.measured_children_scratch();
1678
1679 let measure_result = node_handle.measure_with_cached_batch(
1680 &composer,
1681 node_id,
1682 inner_constraints,
1683 CachedBatchMeasureInputs {
1684 measurer: Box::new(|child_id: NodeId, child_constraints: Constraints| {
1685 match self.measure_node(child_id, child_constraints) {
1686 Ok(measured) => {
1687 measured_children
1688 .borrow_mut()
1689 .insert(child_id, Rc::clone(&measured));
1690 let size = measured.size_for_parent();
1691 Placeable::value(size.width, size.height, child_id)
1692 .with_alignment_lines(measured.alignment_lines_for_parent())
1693 }
1694 Err(err) => {
1695 let mut slot = measure_error.borrow_mut();
1696 if slot.is_none() {
1697 *slot = Some(err);
1698 }
1699 Placeable::value(0.0, 0.0, child_id)
1700 }
1701 }
1702 }),
1703 cached_measure_batch_registrar: Box::new(
1704 |child_ids: &[NodeId],
1705 child_constraints: Constraints,
1706 out: &mut Vec<Option<Size>>| {
1707 out.clear();
1708 out.resize(child_ids.len(), None);
1709
1710 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1711 return;
1712 };
1713
1714 let mut measured_children = measured_children.borrow_mut();
1715 for (index, &child_id) in child_ids.iter().enumerate() {
1716 match Self::cached_measure_node_with_applier(
1717 &mut applier,
1718 child_id,
1719 child_constraints,
1720 ) {
1721 Ok(Some(measured)) => {
1722 out[index] = Some(measured.size);
1723 measured_children.insert(child_id, Rc::clone(&measured));
1724 }
1725 Ok(None) => {}
1726 Err(err) => {
1727 let mut slot = measure_error.borrow_mut();
1728 if slot.is_none() {
1729 *slot = Some(err);
1730 }
1731 break;
1732 }
1733 }
1734 }
1735 },
1736 ),
1737 retained_measure_lookup: Box::new(|child_id| {
1738 measured_children.borrow().get(&child_id).cloned()
1739 }),
1740 retained_measure_registrar: Box::new(|measurements| {
1741 let mut measured_children = measured_children.borrow_mut();
1742 for measured in measurements {
1743 measured_children.insert(measured.node_id(), Rc::clone(measured));
1744 }
1745 }),
1746 error: &measure_error,
1747 },
1748 )?;
1749 drop(composer);
1750 slots_guard.restore(slots_host.into_table()?);
1751
1752 if let Some(err) = measure_error.borrow_mut().take() {
1753 return Err(err);
1754 }
1755
1756 let cranpose_ui_layout::MeasureResult {
1757 size: measured_size,
1758 alignment_lines: explicit_lines,
1759 placements,
1760 } = measure_result;
1761
1762 let mut width = measured_size.width + padding.horizontal_sum();
1763 let mut height = measured_size.height + padding.vertical_sum();
1764
1765 width = resolve_dimension(
1766 width,
1767 props.width(),
1768 props.min_width(),
1769 props.max_width(),
1770 constraints.min_width,
1771 constraints.max_width,
1772 );
1773 height = resolve_dimension(
1774 height,
1775 props.height(),
1776 props.min_height(),
1777 props.max_height(),
1778 constraints.min_height,
1779 constraints.max_height,
1780 );
1781
1782 let mut children = Vec::with_capacity(placements.len());
1783 let mut alignment_lines = AlignmentLines::default();
1784 let mut measured_children_by_id = measured_children.borrow_mut();
1785
1786 if let Ok(mut applier) = self.applier.try_borrow_typed() {
1787 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1788 parent_node.set_measured_size(Size { width, height });
1789 parent_node.clear_needs_measure();
1790 parent_node.clear_needs_layout();
1791 });
1792 }
1793
1794 for placement in &placements {
1795 let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1796 measured
1797 } else {
1798 self.measure_node(placement.node_id, inner_constraints)?
1799 };
1800 let policy_position = Point {
1801 x: padding.left + placement.x,
1802 y: padding.top + placement.y,
1803 };
1804 let retained_position = Point {
1805 x: policy_position.x + child.offset.x,
1806 y: policy_position.y + child.offset.y,
1807 };
1808 alignment_lines.merge(
1809 child
1810 .alignment_lines_for_parent()
1811 .translated(policy_position.y),
1812 );
1813
1814 if let Ok(mut applier) = self.applier.try_borrow_typed()
1815 && applier
1816 .with_node::<LayoutNode, _>(placement.node_id, |node| {
1817 node.set_position(retained_position);
1818 })
1819 .is_err()
1820 {
1821 let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1822 node.set_position(retained_position);
1823 });
1824 }
1825
1826 children.push(MeasuredChild {
1827 node: child,
1828 offset: policy_position,
1829 });
1830 }
1831
1832 node_handle.set_active_children(children.iter().map(|c| c.node.node_id));
1833 node_handle.recycle_placement_scratch(placements);
1834
1835 Ok(Some(Rc::new(
1836 MeasuredNode::new(
1837 node_id,
1838 Size { width, height },
1839 offset,
1840 Point::default(),
1841 children,
1842 )
1843 .with_alignment_lines(
1844 alignment_lines
1845 .with_overrides(explicit_lines.translated(padding.top))
1846 .translated(offset.y),
1847 ),
1848 )))
1849 }
1850
1851 fn measure_layout_node(
1852 self: &Rc<Self>,
1853 node_id: NodeId,
1854 constraints: Constraints,
1855 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1856 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1857 return Ok(None);
1858 };
1859 let LayoutNodeMeasure {
1860 runtime_state,
1861 chain,
1862 mut pools,
1863 } = match applier
1864 .with_node::<LayoutNode, _>(node_id, |node| self.visit_layout_node(node, constraints))
1865 {
1866 Ok(LayoutNodeVisit::Measure(measure)) => measure,
1867 Ok(LayoutNodeVisit::Cached(measured)) => return Ok(Some(measured)),
1868 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
1869 Err(err) => return Err(err),
1870 };
1871
1872 let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
1873 state: &runtime_state,
1874 };
1875 self.bind_layout_children(&mut applier, &runtime_state, &pools.child_ids)?;
1876 drop(applier);
1877 pools.child_ids.clear();
1878
1879 let runtime_state = runtime_state.borrow();
1880 let measurement = self.measure_through_modifier_chain(
1881 node_id,
1882 &runtime_state,
1883 chain,
1884 constraints,
1885 &mut pools.placements,
1886 &mut pools.child_ids,
1887 );
1888
1889 if let Some(err) = runtime_state.frame.error.borrow_mut().take() {
1890 for child_state in &runtime_state.child_states {
1891 child_state.measured.borrow_mut().take();
1892 }
1893 self.with_applier_result(|applier| {
1894 applier.with_node::<LayoutNode, _>(node_id, |node| {
1895 runtime_state.write_node_geometry(node_id, node, &measurement);
1896 })
1897 })
1898 .ok();
1899 return Err(err);
1900 }
1901
1902 let measured = Rc::new(
1903 MeasuredNode::new(
1904 node_id,
1905 measurement.size,
1906 measurement.offset,
1907 measurement.content_offset,
1908 runtime_state.measured_children(
1909 &pools.placements,
1910 &pools.child_ids,
1911 measurement.content_offset,
1912 ),
1913 )
1914 .with_alignment_lines(measurement.alignment_lines)
1915 .with_window_root(measurement.window_root),
1916 );
1917
1918 self.with_applier_result(|applier| {
1919 applier.with_node::<LayoutNode, _>(node_id, |node| {
1920 node.cache_handles()
1921 .store_measurement(constraints, Rc::clone(&measured));
1922 runtime_state.write_node_geometry(node_id, node, &measurement);
1923 node.clear_needs_measure();
1924 node.clear_needs_layout();
1925 node.set_measured_size(measurement.size);
1926 node.set_content_offset(measurement.content_offset);
1927 })
1928 })
1929 .ok();
1930
1931 Ok(Some(measured))
1932 }
1933
1934 fn visit_layout_node(
1935 &self,
1936 node: &mut LayoutNode,
1937 constraints: Constraints,
1938 ) -> LayoutNodeVisit<'_> {
1939 node.clear_placed();
1940 let cache = node.cache_handles();
1941 cache.activate(self.cache_epoch);
1942 if !node.needs_measure()
1943 && !node.needs_layout()
1944 && let Some(cached) = cache.get_measurement(constraints)
1945 {
1946 node.clear_needs_measure();
1947 node.clear_needs_layout();
1948 return LayoutNodeVisit::Cached(cached);
1949 }
1950
1951 let runtime_state = node.layout_runtime_state_handle();
1952 let chain = runtime_state.borrow_mut().bind_node(node);
1953 let mut pools = VecPools::acquire(&self.frame_arena);
1954 pools.child_ids.extend_from_slice(&node.children);
1955 LayoutNodeVisit::Measure(LayoutNodeMeasure {
1956 runtime_state,
1957 chain,
1958 pools,
1959 })
1960 }
1961
1962 fn bind_layout_children(
1963 self: &Rc<Self>,
1964 applier: &mut MemoryApplier,
1965 runtime_state: &RefCell<LayoutRuntimeState>,
1966 child_ids: &[NodeId],
1967 ) -> Result<(), NodeError> {
1968 let mut runtime_state = runtime_state.borrow_mut();
1969 runtime_state.frame.bind(self);
1970 let mut bound = 0;
1971 for &child_id in child_ids {
1972 if self.bind_layout_child(applier, &mut runtime_state, bound, child_id)? {
1973 bound += 1;
1974 }
1975 }
1976 runtime_state.truncate_children(bound);
1977 Ok(())
1978 }
1979
1980 fn bind_layout_child(
1981 &self,
1982 applier: &mut MemoryApplier,
1983 runtime_state: &mut LayoutRuntimeState,
1984 position: usize,
1985 child_id: NodeId,
1986 ) -> Result<bool, NodeError> {
1987 let bound = applier.with_node::<LayoutNode, _>(child_id, |child| {
1988 runtime_state.child_state_at(position, child_id).bind(
1989 LayoutChildBinding {
1990 cache: child.cache_handles(),
1991 layout_state: Some(child.layout_state_handle()),
1992 parent_data: Some(parent_data_of(
1993 child.resolved_modifiers().layout_properties(),
1994 )),
1995 dirty: child.needs_layout() || child.needs_measure(),
1996 },
1997 self,
1998 );
1999 });
2000 match bound {
2001 Ok(()) => Ok(true),
2002 Err(NodeError::TypeMismatch { .. }) => {
2003 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2004 runtime_state.child_state_at(position, child_id).bind(
2005 LayoutChildBinding {
2006 cache: child.cache_handles(),
2007 layout_state: None,
2008 parent_data: Some(parent_data_of(
2009 child.resolved_modifiers().layout_properties(),
2010 )),
2011 dirty: child.needs_layout() || child.needs_measure(),
2012 },
2013 self,
2014 );
2015 }) {
2016 Ok(()) => Ok(true),
2017 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(false),
2018 Err(err) => Err(err),
2019 }
2020 }
2021 Err(NodeError::Missing { .. }) => Ok(false),
2022 Err(err) => Err(err),
2023 }
2024 }
2025
2026 fn measure_through_modifier_chain(
2027 &self,
2028 node_id: NodeId,
2029 runtime_state: &LayoutRuntimeState,
2030 chain: ModifierChainInputs,
2031 constraints: Constraints,
2032 placements: &mut Vec<Placement>,
2033 placement_indices: &mut Vec<usize>,
2034 ) -> ModifierChainMeasurement {
2035 let scope = crate::density::DensityMeasureScope::new(chain.density);
2036
2037 if !chain.uses_chain {
2038 let measurement = runtime_state.measure_policy.measure_into(
2039 &scope,
2040 runtime_state.child_measurables.as_slice(),
2041 constraints,
2042 placements,
2043 );
2044 return ModifierChainMeasurement {
2045 size: measurement.size,
2046 alignment_lines: inherited_alignment_lines(
2047 &runtime_state.child_states,
2048 placements,
2049 placement_indices,
2050 )
2051 .with_overrides(measurement.alignment_lines),
2052 content_offset: Point::default(),
2053 offset: chain.offset,
2054 window_root: chain.window_root,
2055 uses_chain: false,
2056 };
2057 }
2058
2059 let frame = CoordinatorFrame::new(
2060 &runtime_state.measure_policy,
2061 &scope,
2062 runtime_state.child_measurables.as_slice(),
2063 &runtime_state.child_states,
2064 placements,
2065 placement_indices,
2066 );
2067 let placeable = runtime_state
2068 .coordinator_chain
2069 .measure_from(0, &frame, constraints);
2070 let (content_x, content_y) = placeable.content_offset();
2071
2072 let invalidations = frame.take_invalidations();
2073 if !invalidations.is_empty() {
2074 self.with_applier_result(|applier| {
2075 applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2076 for kind in invalidations {
2077 match kind {
2078 InvalidationKind::Layout => layout_node.mark_needs_measure(),
2079 InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2080 InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2081 InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2082 InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2083 }
2084 }
2085 })
2086 })
2087 .ok();
2088 }
2089
2090 ModifierChainMeasurement {
2091 alignment_lines: placeable.alignment_lines(),
2092 size: Size {
2093 width: placeable.width(),
2094 height: placeable.height(),
2095 },
2096 content_offset: Point {
2097 x: content_x - chain.offset.x,
2098 y: content_y - chain.offset.y,
2099 },
2100 offset: chain.offset,
2101 window_root: chain.window_root,
2102 uses_chain: true,
2103 }
2104 }
2105}
2106
2107struct VecPools<'a> {
2108 arena: &'a RefCell<FrameLayoutArena>,
2109 child_ids: Vec<NodeId>,
2110 placements: Vec<Placement>,
2111}
2112
2113impl<'a> VecPools<'a> {
2114 fn acquire(arena: &'a RefCell<FrameLayoutArena>) -> Self {
2115 let mut pools = arena.borrow_mut();
2116 let child_ids = pools.tmp_child_ids.acquire();
2117 let placements = pools.tmp_placements.acquire();
2118 Self {
2119 arena,
2120 child_ids,
2121 placements,
2122 }
2123 }
2124}
2125
2126impl Drop for VecPools<'_> {
2127 fn drop(&mut self) {
2128 let mut pools = self.arena.borrow_mut();
2129 pools
2130 .tmp_child_ids
2131 .release(std::mem::take(&mut self.child_ids));
2132 pools
2133 .tmp_placements
2134 .release(std::mem::take(&mut self.placements));
2135 }
2136}
2137
2138struct SlotsGuard<'a> {
2139 table: &'a RefCell<SlotTable>,
2140 slots: Option<SlotTable>,
2141}
2142
2143impl<'a> SlotsGuard<'a> {
2144 fn take(table: &'a RefCell<SlotTable>) -> Self {
2145 let slots = std::mem::take(&mut *table.borrow_mut());
2146 Self {
2147 table,
2148 slots: Some(slots),
2149 }
2150 }
2151
2152 fn host(&mut self) -> Rc<SlotsHost> {
2153 let slots = self.slots.take().unwrap_or_default();
2154 Rc::new(SlotsHost::new(slots))
2155 }
2156
2157 fn restore(&mut self, slots: SlotTable) {
2158 debug_assert!(self.slots.is_none());
2159 self.slots = Some(slots);
2160 }
2161}
2162
2163impl Drop for SlotsGuard<'_> {
2164 fn drop(&mut self) {
2165 if let Some(slots) = self.slots.take() {
2166 *self.table.borrow_mut() = slots;
2167 }
2168 }
2169}
2170
2171#[derive(Debug, Clone)]
2172pub(crate) struct MeasuredNode {
2173 node_id: NodeId,
2174 size: Size,
2175 offset: Point,
2176 content_offset: Point,
2177 alignment_lines: AlignmentLines,
2178 children: Vec<MeasuredChild>,
2179 window_root: bool,
2180}
2181
2182impl MeasuredNode {
2183 fn new(
2184 node_id: NodeId,
2185 size: Size,
2186 offset: Point,
2187 content_offset: Point,
2188 children: Vec<MeasuredChild>,
2189 ) -> Self {
2190 Self {
2191 node_id,
2192 size,
2193 offset,
2194 content_offset,
2195 alignment_lines: AlignmentLines::default(),
2196 children,
2197 window_root: false,
2198 }
2199 }
2200
2201 fn with_window_root(mut self, window_root: bool) -> Self {
2202 self.window_root = window_root;
2203 self
2204 }
2205
2206 fn with_alignment_lines(mut self, alignment_lines: AlignmentLines) -> Self {
2207 self.alignment_lines = alignment_lines;
2208 self
2209 }
2210
2211 pub(crate) fn alignment_lines_for_parent(&self) -> AlignmentLines {
2212 if self.window_root {
2213 AlignmentLines::default()
2214 } else {
2215 self.alignment_lines
2216 }
2217 }
2218
2219 pub(crate) fn size_for_parent(&self) -> Size {
2220 if self.window_root {
2221 Size::new(0.0, 0.0)
2222 } else {
2223 self.size
2224 }
2225 }
2226
2227 #[cfg(test)]
2228 pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2229 Self::new(
2230 node_id,
2231 size,
2232 Point::default(),
2233 Point::default(),
2234 Vec::new(),
2235 )
2236 }
2237
2238 pub(crate) fn node_id(&self) -> NodeId {
2239 self.node_id
2240 }
2241
2242 pub(crate) fn size(&self) -> Size {
2243 self.size
2244 }
2245}
2246
2247#[derive(Debug, Clone)]
2248struct MeasuredChild {
2249 node: Rc<MeasuredNode>,
2250 offset: Point,
2251}
2252
2253struct CoordinatorFrame<'a> {
2254 measure_policy: &'a Rc<dyn MeasurePolicy>,
2255 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2256 measurables: &'a [Box<dyn Measurable>],
2257 child_states: &'a [Rc<LayoutChildMeasureState>],
2258 placements: RefCell<&'a mut Vec<Placement>>,
2259 placement_indices: RefCell<&'a mut Vec<usize>>,
2260 context: RefCell<LayoutNodeContext>,
2261}
2262
2263impl<'a> CoordinatorFrame<'a> {
2264 fn new(
2265 measure_policy: &'a Rc<dyn MeasurePolicy>,
2266 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2267 measurables: &'a [Box<dyn Measurable>],
2268 child_states: &'a [Rc<LayoutChildMeasureState>],
2269 placements: &'a mut Vec<Placement>,
2270 placement_indices: &'a mut Vec<usize>,
2271 ) -> Self {
2272 Self {
2273 measure_policy,
2274 scope,
2275 measurables,
2276 child_states,
2277 placements: RefCell::new(placements),
2278 placement_indices: RefCell::new(placement_indices),
2279 context: RefCell::new(LayoutNodeContext::new(scope.density())),
2280 }
2281 }
2282
2283 fn take_invalidations(&self) -> Vec<InvalidationKind> {
2284 self.context.borrow_mut().take_invalidations()
2285 }
2286}
2287
2288struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2289 chain: &'chain CoordinatorChain,
2290 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2291 index: usize,
2292 alignment_lines: Cell<AlignmentLines>,
2293}
2294
2295impl<'chain, 'frame_ref, 'frame_data> CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2296 fn new(
2297 chain: &'chain CoordinatorChain,
2298 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2299 index: usize,
2300 ) -> Self {
2301 Self {
2302 chain,
2303 frame,
2304 index,
2305 alignment_lines: Cell::default(),
2306 }
2307 }
2308}
2309
2310impl Measurable for CoordinatorLink<'_, '_, '_> {
2311 fn measure(&self, constraints: Constraints) -> Placeable {
2312 let placeable = self.chain.measure_from(self.index, self.frame, constraints);
2313 self.alignment_lines.set(placeable.alignment_lines());
2314 placeable
2315 }
2316
2317 fn min_intrinsic_width(&self, height: f32) -> f32 {
2318 self.chain
2319 .min_intrinsic_width_from(self.index, self.frame, height)
2320 }
2321
2322 fn max_intrinsic_width(&self, height: f32) -> f32 {
2323 self.chain
2324 .max_intrinsic_width_from(self.index, self.frame, height)
2325 }
2326
2327 fn min_intrinsic_height(&self, width: f32) -> f32 {
2328 self.chain
2329 .min_intrinsic_height_from(self.index, self.frame, width)
2330 }
2331
2332 fn max_intrinsic_height(&self, width: f32) -> f32 {
2333 self.chain
2334 .max_intrinsic_height_from(self.index, self.frame, width)
2335 }
2336}
2337
2338struct CoordinatorNode {
2339 modifier_index: usize,
2340 node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2341 measured_size: Cell<Size>,
2342 accumulated_offset: Cell<Point>,
2343}
2344
2345impl CoordinatorNode {
2346 fn new(
2347 modifier_index: usize,
2348 node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2349 ) -> Self {
2350 Self {
2351 modifier_index,
2352 node,
2353 measured_size: Cell::new(Size::default()),
2354 accumulated_offset: Cell::new(Point::default()),
2355 }
2356 }
2357
2358 fn matches(
2359 &self,
2360 modifier_index: usize,
2361 node: &Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2362 ) -> bool {
2363 self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2364 }
2365
2366 #[cfg(test)]
2367 fn ptr(&self) -> usize {
2368 Rc::as_ptr(&self.node) as *const () as usize
2369 }
2370}
2371
2372#[derive(Default)]
2373struct CoordinatorChain {
2374 nodes: Vec<CoordinatorNode>,
2375 inner_size: Cell<Size>,
2377}
2378
2379impl CoordinatorChain {
2380 fn sync(&mut self, node: &LayoutNode) -> ModifierChainInputs {
2381 let density = node.density();
2382 let mut inputs = ModifierChainInputs {
2383 density,
2384 window_root: node.is_window_root(),
2385 offset: Point::default(),
2386 uses_chain: false,
2387 };
2388 let chain_handle = node.modifier_chain();
2389 if !chain_handle.has_layout_nodes() {
2390 return inputs;
2391 }
2392
2393 let chain = chain_handle.chain();
2394 let mut len = 0;
2395 let mut matches = true;
2396 chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2397 let Some(index) = node_ref.entry_index() else {
2398 return;
2399 };
2400 if let Some(node) = chain.get_node_rc(index) {
2401 matches = matches
2402 && self
2403 .nodes
2404 .get(len)
2405 .is_some_and(|candidate| candidate.matches(index, node));
2406 len += 1;
2407 }
2408 node_ref.with_node(|node| {
2409 if let Some(offset_node) = node
2410 .as_any()
2411 .downcast_ref::<crate::modifier_nodes::OffsetNode>()
2412 {
2413 let delta = offset_node.device_offset(density.density());
2414 inputs.offset.x += delta.x;
2415 inputs.offset.y += delta.y;
2416 }
2417 });
2418 });
2419
2420 inputs.uses_chain = len > 0;
2421 if inputs.uses_chain && !(matches && len == self.nodes.len()) {
2422 self.rebuild(chain, len);
2423 }
2424 inputs
2425 }
2426
2427 fn rebuild(&mut self, chain: &cranpose_foundation::ModifierNodeChain, len: usize) {
2428 let mut previous_nodes = std::mem::take(&mut self.nodes);
2429 self.nodes.reserve(len);
2430 chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2431 let Some((index, node)) = node_ref
2432 .entry_index()
2433 .and_then(|index| chain.get_node_rc(index).map(|node| (index, node)))
2434 else {
2435 return;
2436 };
2437 match previous_nodes
2438 .iter()
2439 .position(|candidate| candidate.matches(index, node))
2440 {
2441 Some(position) => self.nodes.push(previous_nodes.swap_remove(position)),
2442 None => self
2443 .nodes
2444 .push(CoordinatorNode::new(index, Rc::clone(node))),
2445 }
2446 });
2447 }
2448
2449 fn measure_from(
2450 &self,
2451 index: usize,
2452 frame: &CoordinatorFrame<'_>,
2453 constraints: Constraints,
2454 ) -> Placeable {
2455 let Some(node) = self.nodes.get(index) else {
2456 let mut placements = frame.placements.borrow_mut();
2457 let measurement = frame.measure_policy.measure_into(
2458 frame.scope,
2459 frame.measurables,
2460 constraints,
2461 &mut placements,
2462 );
2463 self.inner_size.set(measurement.size);
2464 return Placeable::value(
2465 measurement.size.width,
2466 measurement.size.height,
2467 NodeId::default(),
2468 )
2469 .with_alignment_lines(
2470 inherited_alignment_lines(
2471 frame.child_states,
2472 &placements,
2473 &mut frame.placement_indices.borrow_mut(),
2474 )
2475 .with_overrides(measurement.alignment_lines),
2476 );
2477 };
2478
2479 let wrapped = CoordinatorLink::new(self, frame, index + 1);
2480 let node_borrow = node.node.borrow();
2481
2482 let Some(layout_node) = node_borrow.as_layout_node() else {
2483 let placeable = wrapped.measure(constraints);
2484 node.measured_size.set(Size {
2485 width: placeable.width(),
2486 height: placeable.height(),
2487 });
2488 let child_accumulated = self.total_content_offset_from(index + 1);
2489 node.accumulated_offset.set(child_accumulated);
2490 return placeable;
2491 };
2492
2493 let result = match frame.context.try_borrow_mut() {
2494 Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
2495 Err(_) => {
2496 let mut temp = LayoutNodeContext::new(frame.scope.density());
2497 let result = layout_node.measure(&mut temp, &wrapped, constraints);
2498 if let Ok(mut context) = frame.context.try_borrow_mut() {
2499 for kind in temp.take_invalidations() {
2500 context.invalidate(kind);
2501 }
2502 }
2503 result
2504 }
2505 };
2506
2507 node.measured_size.set(result.size);
2508 let local_offset = Point {
2509 x: result.placement_offset_x,
2510 y: result.placement_offset_y,
2511 };
2512 let child_accumulated = self.total_content_offset_from(index + 1);
2513 let accumulated = Point {
2514 x: local_offset.x + child_accumulated.x,
2515 y: local_offset.y + child_accumulated.y,
2516 };
2517 node.accumulated_offset.set(accumulated);
2518
2519 Placeable::value_with_offset(
2520 result.size.width,
2521 result.size.height,
2522 NodeId::default(),
2523 (accumulated.x, accumulated.y),
2524 )
2525 .with_alignment_lines(
2526 wrapped
2527 .alignment_lines
2528 .get()
2529 .translated(local_offset.y)
2530 .with_overrides(result.alignment_lines),
2531 )
2532 }
2533
2534 fn min_intrinsic_width_from(
2535 &self,
2536 index: usize,
2537 frame: &CoordinatorFrame<'_>,
2538 height: f32,
2539 ) -> f32 {
2540 let Some(node) = self.nodes.get(index) else {
2541 return frame
2542 .measure_policy
2543 .min_intrinsic_width(frame.measurables, height);
2544 };
2545 let wrapped = CoordinatorLink::new(self, frame, index + 1);
2546 let node_borrow = node.node.borrow();
2547 node_borrow.as_layout_node().map_or_else(
2548 || wrapped.min_intrinsic_width(height),
2549 |layout_node| layout_node.min_intrinsic_width(&wrapped, height, frame.scope.density()),
2550 )
2551 }
2552
2553 fn max_intrinsic_width_from(
2554 &self,
2555 index: usize,
2556 frame: &CoordinatorFrame<'_>,
2557 height: f32,
2558 ) -> f32 {
2559 let Some(node) = self.nodes.get(index) else {
2560 return frame
2561 .measure_policy
2562 .max_intrinsic_width(frame.measurables, height);
2563 };
2564 let wrapped = CoordinatorLink::new(self, frame, index + 1);
2565 let node_borrow = node.node.borrow();
2566 node_borrow.as_layout_node().map_or_else(
2567 || wrapped.max_intrinsic_width(height),
2568 |layout_node| layout_node.max_intrinsic_width(&wrapped, height, frame.scope.density()),
2569 )
2570 }
2571
2572 fn min_intrinsic_height_from(
2573 &self,
2574 index: usize,
2575 frame: &CoordinatorFrame<'_>,
2576 width: f32,
2577 ) -> f32 {
2578 let Some(node) = self.nodes.get(index) else {
2579 return frame
2580 .measure_policy
2581 .min_intrinsic_height(frame.measurables, width);
2582 };
2583 let wrapped = CoordinatorLink::new(self, frame, index + 1);
2584 let node_borrow = node.node.borrow();
2585 node_borrow.as_layout_node().map_or_else(
2586 || wrapped.min_intrinsic_height(width),
2587 |layout_node| layout_node.min_intrinsic_height(&wrapped, width, frame.scope.density()),
2588 )
2589 }
2590
2591 fn max_intrinsic_height_from(
2592 &self,
2593 index: usize,
2594 frame: &CoordinatorFrame<'_>,
2595 width: f32,
2596 ) -> f32 {
2597 let Some(node) = self.nodes.get(index) else {
2598 return frame
2599 .measure_policy
2600 .max_intrinsic_height(frame.measurables, width);
2601 };
2602 let wrapped = CoordinatorLink::new(self, frame, index + 1);
2603 let node_borrow = node.node.borrow();
2604 node_borrow.as_layout_node().map_or_else(
2605 || wrapped.max_intrinsic_height(width),
2606 |layout_node| layout_node.max_intrinsic_height(&wrapped, width, frame.scope.density()),
2607 )
2608 }
2609
2610 fn write_geometry(
2614 &self,
2615 geometry: &crate::modifier::CoordinatorGeometry,
2616 node_offset: Point,
2617 ) -> bool {
2618 let content = self.total_content_offset_from(0);
2619 let placed = |inner_offset: Point, size: Size| GeometryRect {
2620 x: content.x - inner_offset.x - node_offset.x,
2621 y: content.y - inner_offset.y - node_offset.y,
2622 width: size.width,
2623 height: size.height,
2624 };
2625 geometry.replace(
2626 self.nodes
2627 .iter()
2628 .map(|node| placed(node.accumulated_offset.get(), node.measured_size.get()))
2629 .chain(std::iter::once(placed(
2630 Point::default(),
2631 self.inner_size.get(),
2632 ))),
2633 )
2634 }
2635
2636 fn total_content_offset_from(&self, index: usize) -> Point {
2637 self.nodes
2638 .get(index)
2639 .map(|node| node.accumulated_offset.get())
2640 .unwrap_or_default()
2641 }
2642
2643 #[cfg(test)]
2644 fn debug_ptrs(&self) -> Vec<usize> {
2645 self.nodes.iter().map(CoordinatorNode::ptr).collect()
2646 }
2647}
2648
2649fn inherited_alignment_lines(
2650 child_states: &[Rc<LayoutChildMeasureState>],
2651 placements: &[Placement],
2652 placement_indices: &mut Vec<usize>,
2653) -> AlignmentLines {
2654 placement_indices.clear();
2655 let in_child_order = placements.len() == child_states.len()
2656 && placements
2657 .iter()
2658 .zip(child_states)
2659 .all(|(placement, child)| placement.node_id == child.node_id);
2660 if !in_child_order {
2661 placement_indices.extend(0..placements.len());
2662 placement_indices.sort_unstable_by_key(|&index| (placements[index].node_id, index));
2663 }
2664 let mut lines = AlignmentLines::default();
2665 for (index, child) in child_states.iter().enumerate() {
2666 let placement = placement_for_child(placements, placement_indices, index, child.node_id);
2667 if let Some(measured) = child.measured.borrow().as_ref() {
2668 lines.merge(
2669 measured
2670 .alignment_lines_for_parent()
2671 .translated(child.placement_position(placement).y),
2672 );
2673 }
2674 }
2675 lines
2676}
2677
2678fn placement_for_child<'a>(
2679 placements: &'a [Placement],
2680 indices: &[usize],
2681 child_index: usize,
2682 node_id: NodeId,
2683) -> Option<&'a Placement> {
2684 let index = if indices.is_empty() {
2685 child_index
2686 } else {
2687 let first = indices.partition_point(|&index| placements[index].node_id < node_id);
2688 *indices.get(first)?
2689 };
2690 placements
2691 .get(index)
2692 .filter(|placement| placement.node_id == node_id)
2693}
2694
2695pub(crate) struct LayoutRuntimeState {
2696 child_ids: Vec<NodeId>,
2697 child_states: Vec<Rc<LayoutChildMeasureState>>,
2698 child_measurables: Vec<Box<dyn Measurable>>,
2699 coordinator_chain: CoordinatorChain,
2700 measure_policy: Rc<dyn MeasurePolicy>,
2701 frame: Rc<LayoutChildFrame>,
2702}
2703
2704impl LayoutRuntimeState {
2705 pub(crate) fn new(measure_policy: Rc<dyn MeasurePolicy>) -> Self {
2706 Self {
2707 child_ids: Vec::new(),
2708 child_states: Vec::new(),
2709 child_measurables: Vec::new(),
2710 coordinator_chain: CoordinatorChain::default(),
2711 measure_policy,
2712 frame: Rc::default(),
2713 }
2714 }
2715
2716 fn bind_node(&mut self, node: &LayoutNode) -> ModifierChainInputs {
2717 if !Rc::ptr_eq(&self.measure_policy, &node.measure_policy) {
2718 self.measure_policy = Rc::clone(&node.measure_policy);
2719 }
2720 self.coordinator_chain.sync(node)
2721 }
2722
2723 fn child_state_at(&mut self, position: usize, child_id: NodeId) -> &LayoutChildMeasureState {
2724 if self.child_ids.get(position) != Some(&child_id) {
2725 let from = match self.child_ids[position..]
2726 .iter()
2727 .position(|&id| id == child_id)
2728 {
2729 Some(offset) => position + offset,
2730 None => {
2731 let state = LayoutChildMeasureState::new(child_id, Rc::clone(&self.frame));
2732 self.child_ids.push(child_id);
2733 self.child_states.push(Rc::clone(&state));
2734 self.child_measurables
2735 .push(Box::new(LayoutChildMeasurable::new(state)));
2736 self.child_ids.len() - 1
2737 }
2738 };
2739 self.child_ids.swap(position, from);
2740 self.child_states.swap(position, from);
2741 self.child_measurables.swap(position, from);
2742 }
2743 &self.child_states[position]
2744 }
2745
2746 fn truncate_children(&mut self, len: usize) {
2747 self.child_ids.truncate(len);
2748 self.child_states.truncate(len);
2749 self.child_measurables.truncate(len);
2750 }
2751
2752 fn measured_children(
2753 &self,
2754 placements: &[Placement],
2755 placement_indices: &[usize],
2756 content_offset: Point,
2757 ) -> Vec<MeasuredChild> {
2758 let mut measured_children = Vec::with_capacity(self.child_states.len());
2759 for (index, child_state) in self.child_states.iter().enumerate() {
2760 let Some(measured) = child_state.measured.borrow_mut().take() else {
2761 continue;
2762 };
2763 let placement =
2764 placement_for_child(placements, placement_indices, index, child_state.node_id);
2765 let base_position = child_state.placement_position(placement);
2766 if placement.is_some() {
2767 child_state.place_retained(Point {
2768 x: base_position.x + measured.offset.x,
2769 y: base_position.y + measured.offset.y,
2770 });
2771 }
2772 measured_children.push(MeasuredChild {
2773 node: measured,
2774 offset: Point {
2775 x: content_offset.x + base_position.x,
2776 y: content_offset.y + base_position.y,
2777 },
2778 });
2779 }
2780 measured_children
2781 }
2782
2783 fn write_node_geometry(
2784 &self,
2785 node_id: NodeId,
2786 node: &LayoutNode,
2787 measurement: &ModifierChainMeasurement,
2788 ) {
2789 let geometry = node.coordinator_geometry();
2790 let moved = if measurement.uses_chain {
2791 self.coordinator_chain
2792 .write_geometry(geometry, measurement.offset)
2793 } else {
2794 geometry.replace([GeometryRect {
2795 x: 0.0,
2796 y: 0.0,
2797 width: measurement.size.width,
2798 height: measurement.size.height,
2799 }])
2800 };
2801 if moved {
2802 crate::render_state::record_geometry_scene_node(node_id);
2803 }
2804 }
2805
2806 #[cfg(test)]
2807 pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
2808 LayoutRuntimeDebugStats {
2809 child_ids: self.child_ids.clone(),
2810 child_state_ptrs: self
2811 .child_states
2812 .iter()
2813 .map(|state| Rc::as_ptr(state) as *const () as usize)
2814 .collect(),
2815 child_measurable_ptrs: self
2816 .child_measurables
2817 .iter()
2818 .map(|measurable| {
2819 measurable.as_ref() as *const dyn Measurable as *const () as usize
2820 })
2821 .collect(),
2822 child_measurable_count: self.child_measurables.len(),
2823 coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
2824 coordinator_node_count: self.coordinator_chain.nodes.len(),
2825 }
2826 }
2827}
2828
2829#[cfg(test)]
2830#[derive(Debug, Clone, PartialEq, Eq)]
2831pub(crate) struct LayoutRuntimeDebugStats {
2832 pub(crate) child_ids: Vec<NodeId>,
2833 pub(crate) child_state_ptrs: Vec<usize>,
2834 pub(crate) child_measurable_ptrs: Vec<usize>,
2835 pub(crate) child_measurable_count: usize,
2836 pub(crate) coordinator_node_ptrs: Vec<usize>,
2837 pub(crate) coordinator_node_count: usize,
2838}
2839
2840#[derive(Default)]
2841struct LayoutChildFrame {
2842 builder: RefCell<Option<Rc<LayoutBuilderState>>>,
2843 error: RefCell<Option<NodeError>>,
2844}
2845
2846impl LayoutChildFrame {
2847 fn bind(&self, builder: &Rc<LayoutBuilderState>) {
2848 self.error.borrow_mut().take();
2849 *self.builder.borrow_mut() = Some(Rc::clone(builder));
2850 }
2851
2852 fn unbind(&self) {
2853 self.builder.borrow_mut().take();
2854 }
2855
2856 fn record_error(&self, err: NodeError) {
2857 if self.builder.borrow().is_none() {
2858 return;
2859 }
2860 let mut slot = self.error.borrow_mut();
2861 if slot.is_none() {
2862 *slot = Some(err);
2863 }
2864 }
2865}
2866
2867struct LayoutChildBinding<'a> {
2868 cache: &'a LayoutNodeCacheHandles,
2869 layout_state: Option<&'a Rc<RefCell<LayoutState>>>,
2870 parent_data: Option<cranpose_ui_layout::ParentData>,
2871 dirty: bool,
2872}
2873
2874fn parent_data_of(props: crate::modifier::LayoutProperties) -> cranpose_ui_layout::ParentData {
2875 let weight = props.weight().unwrap_or_default();
2876 cranpose_ui_layout::ParentData {
2877 weight: weight.weight,
2878 fill: weight.fill,
2879 box_alignment: props.box_alignment(),
2880 row_alignment: props.row_alignment(),
2881 row_baseline: props.row_baseline(),
2882 column_alignment: props.column_alignment(),
2883 }
2884}
2885
2886struct LayoutChildMeasureState {
2887 node_id: NodeId,
2888 frame: Rc<LayoutChildFrame>,
2889 cache: RefCell<LayoutNodeCacheHandles>,
2890 cache_epoch: Cell<u64>,
2891 force_remeasure: Cell<bool>,
2892 parent_data: Cell<Option<cranpose_ui_layout::ParentData>>,
2893 measured: RefCell<Option<Rc<MeasuredNode>>>,
2894 last_position: Cell<Option<Point>>,
2895 layout_state: RefCell<Option<Rc<RefCell<LayoutState>>>>,
2896}
2897
2898impl LayoutChildMeasureState {
2899 fn placement_position(&self, placement: Option<&Placement>) -> Point {
2900 placement
2901 .map(|placement| Point {
2902 x: placement.x,
2903 y: placement.y,
2904 })
2905 .or_else(|| self.last_position.get())
2906 .unwrap_or_default()
2907 }
2908
2909 fn new(node_id: NodeId, frame: Rc<LayoutChildFrame>) -> Rc<Self> {
2910 Rc::new(Self {
2911 node_id,
2912 frame,
2913 cache: RefCell::new(LayoutNodeCacheHandles::default()),
2914 cache_epoch: Cell::new(0),
2915 force_remeasure: Cell::new(true),
2916 parent_data: Cell::new(None),
2917 measured: RefCell::new(None),
2918 last_position: Cell::new(None),
2919 layout_state: RefCell::new(None),
2920 })
2921 }
2922
2923 fn bind(&self, binding: LayoutChildBinding<'_>, pass: &LayoutBuilderState) {
2924 let child_epoch = binding.cache.epoch();
2925 let stale = binding.dirty || child_epoch < pass.cache_floor;
2926 let cache_epoch = if stale { pass.cache_epoch } else { child_epoch };
2927 binding.cache.activate(cache_epoch);
2928 self.measured.borrow_mut().take();
2929 self.last_position.set(None);
2930 self.cache.borrow_mut().clone_from(binding.cache);
2931 self.cache_epoch.set(cache_epoch);
2932 self.force_remeasure.set(stale);
2933 self.parent_data.set(binding.parent_data);
2934 let mut layout_state = self.layout_state.borrow_mut();
2935 let shared = layout_state
2936 .as_ref()
2937 .zip(binding.layout_state)
2938 .is_some_and(|(current, bound)| Rc::ptr_eq(current, bound));
2939 if !shared {
2940 *layout_state = binding.layout_state.cloned();
2941 }
2942 }
2943
2944 fn place_retained(&self, position: Point) {
2945 self.last_position.set(Some(position));
2946 let builder = self.frame.builder.borrow();
2947 let Some(builder) = builder.as_ref() else {
2948 return;
2949 };
2950 if let Some(layout_state) = self.layout_state.borrow().as_ref() {
2951 layout_state.borrow_mut().place(position);
2952 return;
2953 }
2954 let Ok(mut applier) = builder.applier.try_borrow_typed() else {
2955 return;
2956 };
2957 let _ = applier.with_node::<SubcomposeLayoutNode, _>(self.node_id, |node| {
2958 node.set_position(position);
2959 });
2960 }
2961
2962 fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
2963 let builder = self.frame.builder.borrow();
2964 let builder = builder.as_ref().ok_or(NodeError::MissingContext {
2965 id: self.node_id,
2966 reason: "layout child applier not configured",
2967 })?;
2968 builder.measure_node(self.node_id, constraints)
2969 }
2970
2971 fn measure_cached(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
2972 let cache = self.cache.borrow();
2973 cache.activate(self.cache_epoch.get());
2974 if !self.force_remeasure.get()
2975 && let Some(cached) = cache.get_measurement(constraints)
2976 {
2977 return Some(cached);
2978 }
2979
2980 match self.perform_measure(constraints) {
2981 Ok(measured) => {
2982 self.force_remeasure.set(false);
2983 cache.store_measurement(constraints, Rc::clone(&measured));
2984 Some(measured)
2985 }
2986 Err(err) => {
2987 self.frame.record_error(err);
2988 None
2989 }
2990 }
2991 }
2992}
2993
2994struct LayoutChildMeasurable {
2995 state: Rc<LayoutChildMeasureState>,
2996}
2997
2998impl LayoutChildMeasurable {
2999 fn new(state: Rc<LayoutChildMeasureState>) -> Self {
3000 Self { state }
3001 }
3002
3003 fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
3004 if self.state.frame.builder.borrow().is_none() {
3005 return None;
3006 }
3007 self.state.parent_data.get()
3008 }
3009
3010 fn intrinsic(
3011 &self,
3012 kind: IntrinsicKind,
3013 constraints: Constraints,
3014 extent: fn(Size) -> f32,
3015 ) -> f32 {
3016 let state = &self.state;
3017 let cache = state.cache.borrow();
3018 cache.activate(state.cache_epoch.get());
3019 if !state.force_remeasure.get()
3020 && let Some(value) = cache.get_intrinsic(&kind)
3021 {
3022 return value;
3023 }
3024 let Some(node) = state.measure_cached(constraints) else {
3025 return 0.0;
3026 };
3027 let value = extent(node.size_for_parent());
3028 cache.store_intrinsic(kind, value);
3029 value
3030 }
3031}
3032
3033impl PlaceTarget for LayoutChildMeasureState {
3034 fn place(&self, x: f32, y: f32) {
3035 let internal_offset = self
3036 .measured
3037 .borrow()
3038 .as_ref()
3039 .map(|measured| measured.offset)
3040 .unwrap_or_default();
3041 self.place_retained(Point {
3042 x: x + internal_offset.x,
3043 y: y + internal_offset.y,
3044 });
3045 }
3046}
3047
3048impl Measurable for LayoutChildMeasurable {
3049 fn measure(&self, constraints: Constraints) -> Placeable {
3050 let state = &self.state;
3051 let measured = state.measure_cached(constraints);
3052 let (measured_size, size_for_parent) = measured.as_ref().map_or(
3053 (
3054 Size {
3055 width: 0.0,
3056 height: 0.0,
3057 },
3058 Size {
3059 width: 0.0,
3060 height: 0.0,
3061 },
3062 ),
3063 |measured| (measured.size, measured.size_for_parent()),
3064 );
3065 if let Some(layout_state) = state.layout_state.borrow().as_ref() {
3066 layout_state.borrow_mut().set_size(measured_size);
3067 }
3068 let alignment_lines = measured
3069 .as_ref()
3070 .map_or_else(AlignmentLines::default, |node| {
3071 node.alignment_lines_for_parent()
3072 });
3073 *state.measured.borrow_mut() = measured;
3074
3075 Placeable::with_place_target(
3076 size_for_parent.width,
3077 size_for_parent.height,
3078 state.node_id,
3079 Rc::clone(&self.state) as Rc<dyn PlaceTarget>,
3080 )
3081 .with_alignment_lines(alignment_lines)
3082 }
3083
3084 fn min_intrinsic_width(&self, height: f32) -> f32 {
3085 self.intrinsic(
3086 IntrinsicKind::MinWidth(height),
3087 Constraints {
3088 min_width: 0.0,
3089 max_width: f32::INFINITY,
3090 min_height: height,
3091 max_height: height,
3092 },
3093 |size| size.width,
3094 )
3095 }
3096
3097 fn max_intrinsic_width(&self, height: f32) -> f32 {
3098 self.intrinsic(
3099 IntrinsicKind::MaxWidth(height),
3100 Constraints {
3101 min_width: 0.0,
3102 max_width: f32::INFINITY,
3103 min_height: 0.0,
3104 max_height: height,
3105 },
3106 |size| size.width,
3107 )
3108 }
3109
3110 fn min_intrinsic_height(&self, width: f32) -> f32 {
3111 self.intrinsic(
3112 IntrinsicKind::MinHeight(width),
3113 Constraints {
3114 min_width: width,
3115 max_width: width,
3116 min_height: 0.0,
3117 max_height: f32::INFINITY,
3118 },
3119 |size| size.height,
3120 )
3121 }
3122
3123 fn max_intrinsic_height(&self, width: f32) -> f32 {
3124 self.intrinsic(
3125 IntrinsicKind::MaxHeight(width),
3126 Constraints {
3127 min_width: 0.0,
3128 max_width: width,
3129 min_height: 0.0,
3130 max_height: f32::INFINITY,
3131 },
3132 |size| size.height,
3133 )
3134 }
3135
3136 fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3137 let parent_data = self.resolved_parent_data()?;
3138 if !parent_data.has_weight() {
3139 return None;
3140 }
3141 Some(cranpose_ui_layout::FlexParentData::new(
3142 parent_data.weight,
3143 parent_data.fill,
3144 ))
3145 }
3146
3147 fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3148 self.resolved_parent_data().unwrap_or_default()
3149 }
3150}
3151
3152#[derive(Clone)]
3153struct RuntimeNodeMetadata {
3154 modifier: Modifier,
3155 resolved_modifiers: ResolvedModifiers,
3156 modifier_slices: Rc<ModifierNodeSlices>,
3157 semantics: Option<Rc<SemanticsConfiguration>>,
3158 role: SemanticsRole,
3159 button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3160}
3161
3162impl Default for RuntimeNodeMetadata {
3163 fn default() -> Self {
3164 Self {
3165 modifier: Modifier::empty(),
3166 resolved_modifiers: ResolvedModifiers::default(),
3167 modifier_slices: Rc::default(),
3168 semantics: None,
3169 role: SemanticsRole::Unknown,
3170 button_handler: None,
3171 }
3172 }
3173}
3174
3175fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3176 modifier_slices
3177 .annotated_text()
3178 .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3179 value: SemanticsText(Rc::clone(text)),
3180 })
3181}
3182
3183fn runtime_metadata_for(
3184 applier: &mut MemoryApplier,
3185 node_id: NodeId,
3186) -> Result<RuntimeNodeMetadata, NodeError> {
3187 if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3188 let modifier = layout.modifier.clone();
3189 let resolved_modifiers = layout.resolved_modifiers();
3190 let modifier_slices = layout.modifier_slices_snapshot();
3191 let role = role_from_modifier_slices(&modifier_slices);
3192
3193 RuntimeNodeMetadata {
3194 modifier,
3195 resolved_modifiers,
3196 modifier_slices,
3197 semantics: layout.semantics_configuration().map(Rc::new),
3198 role,
3199 button_handler: None,
3200 }
3201 }) {
3202 return Ok(meta);
3203 }
3204
3205 if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
3206 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3207 (
3208 node.modifier(),
3209 node.resolved_modifiers(),
3210 node.modifier_slices_snapshot(),
3211 node.semantics_configuration().map(Rc::new),
3212 )
3213 })
3214 {
3215 return Ok(RuntimeNodeMetadata {
3216 modifier,
3217 resolved_modifiers,
3218 modifier_slices,
3219 semantics,
3220 role: SemanticsRole::Subcompose,
3221 button_handler: None,
3222 });
3223 }
3224 Ok(RuntimeNodeMetadata::default())
3225}
3226
3227fn clear_semantics_dirty_flags(
3228 applier: &mut MemoryApplier,
3229 node: &MeasuredNode,
3230) -> Result<(), NodeError> {
3231 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3232 layout.clear_needs_semantics();
3233 }) {
3234 Ok(()) => {}
3235 Err(NodeError::Missing { .. }) => {}
3236 Err(NodeError::TypeMismatch { .. }) => {
3237 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3238 subcompose.clear_needs_semantics();
3239 }) {
3240 Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
3241 Err(err) => return Err(err),
3242 }
3243 }
3244 Err(err) => return Err(err),
3245 }
3246
3247 for child in &node.children {
3248 clear_semantics_dirty_flags(applier, &child.node)?;
3249 }
3250
3251 Ok(())
3252}
3253
3254fn build_semantics_tree_from_live_nodes(
3255 applier: &mut MemoryApplier,
3256 node: &MeasuredNode,
3257) -> Result<SemanticsTree, NodeError> {
3258 Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3259 applier, node, None,
3260 )?))
3261}
3262
3263fn semantics_node_from_parts(
3264 node_id: NodeId,
3265 node_generation: u32,
3266 mut role: SemanticsRole,
3267 config: Option<SemanticsConfiguration>,
3268 children: Vec<SemanticsNode>,
3269 held_details: Option<Box<SemanticsDetails>>,
3270 bounds: GeometryRect,
3271) -> SemanticsNode {
3272 let focusable = crate::focus_dispatch::has_focus_target(node_id);
3273 let mut node = SemanticsNode {
3274 node_id,
3275 bounds,
3276 node_generation,
3277 children,
3278 focusable,
3279 focused: focusable && crate::focus_dispatch::active_focus_target() == Some(node_id),
3280 details: held_details,
3281 ..SemanticsNode::default()
3282 };
3283 let details = match config {
3284 Some(mut config) => {
3285 if config.role == Some(SemanticsWidgetRole::Button) {
3286 role = SemanticsRole::Button;
3287 }
3288 if config.is_activatable() {
3289 node.actions.push(SemanticsAction::Click {
3290 handler: SemanticsCallback::new(node_id),
3291 });
3292 }
3293 let on_click_label = config.on_click_label.take().or_else(|| {
3294 config
3295 .on_click
3296 .as_ref()
3297 .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
3298 });
3299 node.widget_role = config.role;
3300 node.description = config.content_description.take();
3301 node.on_click = config.on_click.take();
3302 node.selected = config.selected;
3303 node.toggled = config.toggled;
3304 node.enabled = config.enabled;
3305 node.hidden = config.hidden;
3306 node.merge_descendants = config.merge_descendants;
3307 node.traversal_index = config.traversal_index;
3308 node.text = config.text.take();
3309 let is_modal = config.is_modal
3310 && modal_takes_space(Size {
3311 width: bounds.width,
3312 height: bounds.height,
3313 });
3314 SemanticsDetails::from_configuration(config, on_click_label, is_modal)
3315 }
3316 None => SemanticsDetails::NONE,
3317 };
3318 node.set_details(details);
3319 node.role = role;
3320 node
3321}
3322
3323fn build_semantics_node_from_live_nodes(
3324 applier: &mut MemoryApplier,
3325 node: &MeasuredNode,
3326 origin: Option<Point>,
3327) -> Result<SemanticsNode, NodeError> {
3328 let (role, config, (bounds, content), placement) =
3329 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3330 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
3331 let config = layout.semantics_configuration();
3332 layout.clear_needs_semantics();
3333 let state = layout.layout_state();
3334 (
3335 role,
3336 config,
3337 semantics_placement(&state, origin),
3338 SemanticsPlacement::of(&state),
3339 )
3340 }) {
3341 Ok(data) => data,
3342 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3343 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3344 subcompose.clear_needs_semantics();
3345 let state = subcompose.layout_state();
3346 (
3347 SemanticsRole::Subcompose,
3348 subcompose.semantics_configuration(),
3349 semantics_placement(&state, origin),
3350 SemanticsPlacement::of(&state),
3351 )
3352 }) {
3353 Ok(data) => data,
3354 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3355 let top_left = origin.unwrap_or_default();
3356 (
3357 SemanticsRole::Unknown,
3358 None,
3359 (
3360 GeometryRect::from_origin_size(top_left, node.size),
3361 top_left,
3362 ),
3363 SemanticsPlacement::default(),
3364 )
3365 }
3366 Err(err) => return Err(err),
3367 }
3368 }
3369 Err(err) => return Err(err),
3370 };
3371
3372 let mut children = Vec::with_capacity(node.children.len());
3373 for child in &node.children {
3374 children.push(build_semantics_node_from_live_nodes(
3375 applier,
3376 &child.node,
3377 Some(content),
3378 )?);
3379 }
3380
3381 Ok(SemanticsNode {
3382 placement,
3383 ..semantics_node_from_parts(
3384 node.node_id,
3385 applier.node_generation(node.node_id),
3386 role,
3387 config,
3388 children,
3389 None,
3390 bounds,
3391 )
3392 })
3393}
3394
3395fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
3396 stats.semantics_node_count += 1;
3397 stats.semantics_action_count += node.actions.len();
3398 stats.semantics_action_capacity += node.actions.capacity();
3399 stats.semantics_child_count += node.children.len();
3400 stats.semantics_child_capacity += node.children.capacity();
3401 stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
3402 stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
3403 if node.details.is_some() {
3404 stats.semantics_heap_bytes += size_of::<SemanticsDetails>();
3405 }
3406
3407 if let Some(description) = &node.description {
3408 stats.semantics_description_count += 1;
3409 stats.semantics_description_bytes += description.capacity();
3410 stats.semantics_heap_bytes += description.capacity();
3411 }
3412
3413 for child in &node.children {
3414 record_semantics_allocation_stats(child, stats);
3415 }
3416}
3417
3418fn record_layout_box_allocation_stats(
3419 layout_box: &LayoutBox,
3420 stats: &mut LayoutAllocationDebugStats,
3421) {
3422 stats.layout_box_count += 1;
3423 stats.layout_box_child_count += layout_box.children.len();
3424 stats.layout_box_child_capacity += layout_box.children.capacity();
3425 stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
3426 stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
3427
3428 for child in &layout_box.children {
3429 record_layout_box_allocation_stats(child, stats);
3430 }
3431}
3432
3433fn build_layout_tree(
3434 applier: &mut MemoryApplier,
3435 node: &MeasuredNode,
3436) -> Result<LayoutTree, NodeError> {
3437 fn place(
3438 applier: &mut MemoryApplier,
3439 node: &MeasuredNode,
3440 origin: Point,
3441 parent_transform: ProjectiveTransform,
3442 ) -> Result<LayoutBox, NodeError> {
3443 let top_left = Point {
3444 x: origin.x + node.offset.x,
3445 y: origin.y + node.offset.y,
3446 };
3447 let rect = GeometryRect {
3448 x: top_left.x,
3449 y: top_left.y,
3450 width: node.size.width,
3451 height: node.size.height,
3452 };
3453 let (data, window_transform) =
3454 snapshot_node_data(applier, node.node_id, top_left, node.size, parent_transform)?;
3455 let mut children = Vec::with_capacity(node.children.len());
3456 for child in &node.children {
3457 if crate::modifier::is_window_root(applier, child.node.node_id) {
3458 continue;
3459 }
3460 let child_origin = Point {
3461 x: top_left.x + child.offset.x,
3462 y: top_left.y + child.offset.y,
3463 };
3464 children.push(place(applier, &child.node, child_origin, window_transform)?);
3465 }
3466 Ok(LayoutBox {
3467 node_generation: applier.node_generation(node.node_id),
3468 ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
3469 })
3470 }
3471
3472 Ok(LayoutTree::new(place(
3473 applier,
3474 node,
3475 Point { x: 0.0, y: 0.0 },
3476 ProjectiveTransform::identity(),
3477 )?))
3478}
3479
3480fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
3481 match &layout_box.node_data.kind {
3482 LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
3483 LayoutNodeKind::Spacer => SemanticsRole::Spacer,
3484 LayoutNodeKind::Unknown => SemanticsRole::Unknown,
3485 LayoutNodeKind::Button { .. } => SemanticsRole::Button,
3486 LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
3487 }
3488}
3489
3490fn build_semantics_node_from_layout_box(layout_box: &LayoutBox, origin: Point) -> SemanticsNode {
3491 let rect = layout_box.rect;
3492 let content = Point {
3493 x: rect.x + layout_box.content_offset.x,
3494 y: rect.y + layout_box.content_offset.y,
3495 };
3496 let children = layout_box
3497 .children
3498 .iter()
3499 .map(|child| build_semantics_node_from_layout_box(child, content))
3500 .collect();
3501
3502 SemanticsNode {
3503 placement: SemanticsPlacement {
3504 position: Point {
3505 x: rect.x - origin.x,
3506 y: rect.y - origin.y,
3507 },
3508 content_offset: layout_box.content_offset,
3509 },
3510 ..semantics_node_from_parts(
3511 layout_box.node_id,
3512 layout_box.node_generation,
3513 semantics_role_from_layout_box(layout_box),
3514 layout_box.node_data.semantics().cloned(),
3515 children,
3516 None,
3517 rect,
3518 )
3519 }
3520}
3521
3522fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
3523 match &info.role {
3524 SemanticsRole::Layout => LayoutNodeKind::Layout,
3525 SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
3526 SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
3527 SemanticsRole::Spacer => LayoutNodeKind::Spacer,
3528 SemanticsRole::Button => {
3529 let handler = info
3530 .button_handler
3531 .as_ref()
3532 .cloned()
3533 .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
3534 LayoutNodeKind::Button { on_click: handler }
3535 }
3536 SemanticsRole::Unknown => LayoutNodeKind::Unknown,
3537 }
3538}
3539
3540fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
3541 let horizontal = padding.horizontal_sum();
3542 let vertical = padding.vertical_sum();
3543 let min_width = (constraints.min_width - horizontal).max(0.0);
3544 let mut max_width = constraints.max_width;
3545 if max_width.is_finite() {
3546 max_width = (max_width - horizontal).max(0.0);
3547 }
3548 let min_height = (constraints.min_height - vertical).max(0.0);
3549 let mut max_height = constraints.max_height;
3550 if max_height.is_finite() {
3551 max_height = (max_height - vertical).max(0.0);
3552 }
3553 normalize_constraints(Constraints {
3554 min_width,
3555 max_width,
3556 min_height,
3557 max_height,
3558 })
3559}
3560
3561fn resolve_dimension(
3562 base: f32,
3563 explicit: DimensionConstraint,
3564 min_override: Option<f32>,
3565 max_override: Option<f32>,
3566 min_limit: f32,
3567 max_limit: f32,
3568) -> f32 {
3569 let mut min_bound = min_limit;
3570 if let Some(min_value) = min_override {
3571 min_bound = min_bound.max(min_value);
3572 }
3573
3574 let mut max_bound = if max_limit.is_finite() {
3575 max_limit
3576 } else {
3577 max_override.unwrap_or(max_limit)
3578 };
3579 if let Some(max_value) = max_override {
3580 if max_bound.is_finite() {
3581 max_bound = max_bound.min(max_value);
3582 } else {
3583 max_bound = max_value;
3584 }
3585 }
3586 if max_bound < min_bound {
3587 max_bound = min_bound;
3588 }
3589
3590 let mut size = match explicit {
3591 DimensionConstraint::Points(points) => points,
3592 DimensionConstraint::Fraction(fraction) => {
3593 if max_limit.is_finite() {
3594 max_limit * fraction.clamp(0.0, 1.0)
3595 } else {
3596 base
3597 }
3598 }
3599 DimensionConstraint::Unspecified => base,
3600 DimensionConstraint::Intrinsic(_) => base,
3601 };
3602
3603 size = clamp_dimension(size, min_bound, max_bound);
3604 size = clamp_dimension(size, min_limit, max_limit);
3605 size.max(0.0)
3606}
3607
3608fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
3609 let mut result = value.max(min);
3610 if max.is_finite() {
3611 result = result.min(max);
3612 }
3613 result
3614}
3615
3616fn normalize_constraints(mut constraints: Constraints) -> Constraints {
3617 if constraints.max_width < constraints.min_width {
3618 constraints.max_width = constraints.min_width;
3619 }
3620 if constraints.max_height < constraints.min_height {
3621 constraints.max_height = constraints.min_height;
3622 }
3623 constraints
3624}
3625
3626#[cfg(test)]
3627#[path = "tests/layout_tests.rs"]
3628mod tests;
3629
3630#[cfg(test)]
3631#[path = "tests/semantics_update_tests.rs"]
3632mod semantics_update_tests;
3633
3634#[cfg(test)]
3635#[path = "tests/coordinator_geometry_tests.rs"]
3636mod coordinator_geometry_tests;