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, PartialEq)]
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)?.layout_dirty())
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() || Node::descendant_needs_measure(node),
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() || node.descendant_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_descendant_needs_layout(false);
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 Keep(Rc<MeasuredNode>),
1483 Measure(LayoutNodeMeasure<'a>),
1484}
1485
1486#[derive(Clone, Copy)]
1488struct ChangedChild {
1489 constraints: Constraints,
1490 placed: bool,
1491}
1492
1493enum ChildChange {
1495 Unchanged,
1496 Changed(ChangedChild),
1497 Refuse,
1499}
1500
1501struct ChildView {
1504 self_dirty: bool,
1505 parent_data: Option<cranpose_ui_layout::ParentData>,
1508 cached_constraints: Option<Constraints>,
1510 read_intrinsics: bool,
1511 placed: bool,
1512}
1513
1514impl ChildView {
1515 fn of(
1517 node: &dyn Node,
1518 cache: &LayoutNodeCacheHandles,
1519 props: &dyn Fn() -> crate::modifier::LayoutProperties,
1520 placed: bool,
1521 ) -> Option<Self> {
1522 let self_dirty = node.needs_measure() || node.needs_layout();
1523 if !self_dirty && !node.descendant_needs_layout() {
1524 return None;
1525 }
1526 Some(Self {
1527 self_dirty,
1528 parent_data: self_dirty.then(|| parent_data_of(props())),
1529 cached_constraints: cache.measured_constraints(),
1530 read_intrinsics: cache.has_intrinsics(),
1531 placed,
1532 })
1533 }
1534}
1535
1536struct LayoutNodeMeasure<'a> {
1537 runtime_state: Rc<RefCell<LayoutRuntimeState>>,
1538 chain: ModifierChainInputs,
1539 pools: VecPools<'a>,
1540}
1541
1542impl LayoutBuilderState {
1543 fn new_with_epoch(
1544 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1545 epoch: u64,
1546 slots: Rc<RefCell<SlotTable>>,
1547 frame_arena: FrameLayoutArena,
1548 ) -> Self {
1549 let runtime_handle = applier.borrow_typed().runtime_handle();
1550
1551 Self {
1552 applier,
1553 runtime_handle: RefCell::new(runtime_handle),
1554 slots,
1555 cache_epoch: epoch,
1556 cache_floor: crate::render_state::layout_cache_floor(),
1557 frame_arena: RefCell::new(frame_arena),
1558 }
1559 }
1560
1561 fn with_applier_result<R>(
1562 &self,
1563 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1564 ) -> Result<R, NodeError> {
1565 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1566 return Err(NodeError::MissingContext {
1567 id: NodeId::default(),
1568 reason: "applier already borrowed",
1569 });
1570 };
1571 f(&mut applier)
1572 }
1573
1574 fn measure_subcompose_node(
1575 self: &Rc<Self>,
1576 node_id: NodeId,
1577 constraints: Constraints,
1578 ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1579 self.clear_subcompose_placed(node_id);
1580 if let Some(cached) = self.subcompose_measurement_to_keep(node_id, constraints)
1581 && let Some(kept) = self.keep_subcompose_measurement(node_id, constraints, &cached)?
1582 {
1583 return Ok(("subcompose kept", kept));
1584 }
1585 Ok(match self.try_measure_subcompose(node_id, constraints)? {
1586 Some(measured) => {
1587 self.store_subcompose_measurement(node_id, constraints, &measured);
1588 ("subcompose", measured)
1589 }
1590 None => (
1591 "fallback",
1592 Rc::new(MeasuredNode::new(
1593 node_id,
1594 Size::default(),
1595 Point { x: 0.0, y: 0.0 },
1596 Point::default(),
1597 Vec::new(),
1598 )),
1599 ),
1600 })
1601 }
1602
1603 fn subcompose_measurement_to_keep(
1607 &self,
1608 node_id: NodeId,
1609 constraints: Constraints,
1610 ) -> Option<Rc<MeasuredNode>> {
1611 let mut applier = self.applier.try_borrow_typed().ok()?;
1612 applier
1613 .with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1614 if node.needs_measure() || Node::needs_layout(node) {
1615 return None;
1616 }
1617 let cache = node.cache_handles();
1618 if cache.epoch() < self.cache_floor {
1619 return None;
1620 }
1621 cache.get_measurement(constraints)
1622 })
1623 .ok()
1624 .flatten()
1625 }
1626
1627 fn keep_subcompose_measurement(
1633 self: &Rc<Self>,
1634 node_id: NodeId,
1635 constraints: Constraints,
1636 cached: &Rc<MeasuredNode>,
1637 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1638 let kept = self.remeasure_changed_children(
1639 cached,
1640 |child_id| self.subcompose_child_change(child_id),
1641 |child_id| self.mark_measured_without_policy(child_id),
1642 )?;
1643 let Some(kept) = kept else {
1644 return Ok(None);
1645 };
1646 self.with_applier_result(|applier| {
1647 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1648 node.cache_handles()
1649 .store_measurement(constraints, Rc::clone(&kept));
1650 node.clear_needs_layout();
1651 })
1652 })?;
1653 Ok(Some(kept))
1654 }
1655
1656 fn subcompose_child_change(&self, child_id: NodeId) -> Result<ChildChange, NodeError> {
1660 self.child_change(child_id, |view| {
1661 match view.cached_constraints.filter(|_| !view.self_dirty) {
1662 Some(constraints) if !view.read_intrinsics => ChildChange::Changed(ChangedChild {
1663 constraints,
1664 placed: view.placed,
1665 }),
1666 _ => ChildChange::Refuse,
1667 }
1668 })
1669 }
1670
1671 fn child_change(
1674 &self,
1675 child_id: NodeId,
1676 decide: impl FnOnce(ChildView) -> ChildChange,
1677 ) -> Result<ChildChange, NodeError> {
1678 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1679 return Ok(ChildChange::Refuse);
1680 };
1681 Ok(
1682 match read_layout_child(&mut applier, child_id, ChildView::of)? {
1683 None => ChildChange::Refuse,
1684 Some(None) => ChildChange::Unchanged,
1685 Some(Some(view)) => decide(view),
1686 },
1687 )
1688 }
1689
1690 fn mark_measured_without_policy(&self, child_id: NodeId) {
1694 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1695 return;
1696 };
1697 if let Ok(node) = applier.get_mut(child_id) {
1698 node.mark_descendant_needs_layout(true);
1699 }
1700 }
1701
1702 fn clear_subcompose_placed(&self, node_id: NodeId) {
1703 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1704 return;
1705 };
1706 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1707 node.clear_placed();
1708 });
1709 }
1710
1711 fn measure_node(
1712 self: &Rc<Self>,
1713 node_id: NodeId,
1714 constraints: Constraints,
1715 ) -> Result<Rc<MeasuredNode>, NodeError> {
1716 let telemetry = layout_measure_telemetry_threshold_ms().map(|ms| (ms, Instant::now()));
1717 let (kind, measured) =
1718 if let Some(measured) = self.measure_layout_node(node_id, constraints)? {
1719 ("layout", measured)
1720 } else {
1721 self.measure_subcompose_node(node_id, constraints)?
1722 };
1723 if let Some(telemetry) = telemetry {
1724 log_node_measure_telemetry(kind, node_id, constraints, &measured, telemetry);
1725 }
1726 Ok(measured)
1727 }
1728
1729 fn cached_measure_node_with_applier(
1732 applier: &mut MemoryApplier,
1733 node_id: NodeId,
1734 constraints: Constraints,
1735 current_epoch: u64,
1736 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1737 let served = |cache: &LayoutNodeCacheHandles, dirty: bool| {
1738 let epoch = cache.epoch();
1739 if dirty || epoch == 0 || epoch != current_epoch {
1740 return None;
1741 }
1742 cache.get_measurement(constraints)
1743 };
1744
1745 match applier.with_node::<LayoutNode, _>(node_id, |node| {
1746 let measured = served(node.cache_handles(), Node::layout_dirty(node))?;
1747 node.set_measured_size(measured.size);
1748 Some(measured)
1749 }) {
1750 Ok(measured) => Ok(measured),
1751 Err(NodeError::TypeMismatch { .. }) => {
1752 match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1753 let measured = served(node.cache_handles(), Node::layout_dirty(node))?;
1754 node.set_measured_size(measured.size);
1755 Some(measured)
1756 }) {
1757 Ok(measured) => Ok(measured),
1758 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
1759 Err(err) => Err(err),
1760 }
1761 }
1762 Err(NodeError::Missing { .. }) => Ok(None),
1763 Err(err) => Err(err),
1764 }
1765 }
1766
1767 fn try_measure_subcompose(
1768 self: &Rc<Self>,
1769 node_id: NodeId,
1770 constraints: Constraints,
1771 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1772 let (node_handle, resolved_modifiers) = {
1773 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1774 return Ok(None);
1775 };
1776 let node = match applier.get_mut(node_id) {
1777 Ok(node) => node,
1778 Err(NodeError::Missing { .. }) => return Ok(None),
1779 Err(err) => return Err(err),
1780 };
1781 let any = node.as_any_mut();
1782 if let Some(subcompose) =
1783 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1784 {
1785 let handle = subcompose.handle();
1786 let resolved_modifiers = handle
1787 .resolved_modifiers()
1788 .on_device_grid(subcompose.density().density());
1789 (handle, resolved_modifiers)
1790 } else {
1791 return Ok(None);
1792 }
1793 };
1794
1795 let runtime_handle = {
1796 let mut runtime_handle = self.runtime_handle.borrow_mut();
1797 if runtime_handle.is_none()
1798 && let Ok(applier) = self.applier.try_borrow_typed()
1799 {
1800 *runtime_handle = applier.runtime_handle();
1801 }
1802 runtime_handle.clone().ok_or(NodeError::MissingContext {
1803 id: node_id,
1804 reason: "runtime handle required for subcomposition",
1805 })?
1806 };
1807
1808 let props = resolved_modifiers.layout_properties();
1809 let padding = resolved_modifiers.padding();
1810 let offset = resolved_modifiers.offset();
1811 let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1812
1813 if let DimensionConstraint::Points(width) = props.width() {
1814 let constrained_width = width - padding.horizontal_sum();
1815 inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1816 inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1817 }
1818 if let DimensionConstraint::Points(height) = props.height() {
1819 let constrained_height = height - padding.vertical_sum();
1820 inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1821 inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1822 }
1823
1824 let mut slots_guard = SlotsGuard::take(&self.slots);
1825 let slots_host = slots_guard.host();
1826 let applier_host_dyn: Rc<dyn ApplierHost> = Rc::clone(&self.applier) as Rc<dyn ApplierHost>;
1827 let composer = Composer::new(
1828 Rc::clone(&slots_host),
1829 applier_host_dyn,
1830 runtime_handle,
1831 Some(node_id),
1832 );
1833 composer.enter_phase(Phase::Measure);
1834
1835 let measure_error = RefCell::new(None);
1836 let measured_children = node_handle.measured_children_scratch();
1837
1838 let measure_result = node_handle.measure_with_cached_batch(
1839 &composer,
1840 node_id,
1841 inner_constraints,
1842 CachedBatchMeasureInputs {
1843 measurer: Box::new(|child_id: NodeId, child_constraints: Constraints| {
1844 match self.measure_node(child_id, child_constraints) {
1845 Ok(measured) => {
1846 measured_children
1847 .borrow_mut()
1848 .insert(child_id, Rc::clone(&measured));
1849 let size = measured.size_for_parent();
1850 Placeable::value(size.width, size.height, child_id)
1851 .with_alignment_lines(measured.alignment_lines_for_parent())
1852 }
1853 Err(err) => {
1854 let mut slot = measure_error.borrow_mut();
1855 if slot.is_none() {
1856 *slot = Some(err);
1857 }
1858 Placeable::value(0.0, 0.0, child_id)
1859 }
1860 }
1861 }),
1862 cached_measure_batch_registrar: Box::new(
1863 |child_ids: &[NodeId],
1864 child_constraints: Constraints,
1865 out: &mut Vec<Option<Size>>| {
1866 out.clear();
1867 out.resize(child_ids.len(), None);
1868
1869 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1870 return;
1871 };
1872
1873 let mut measured_children = measured_children.borrow_mut();
1874 let current_epoch = crate::render_state::current_layout_cache_epoch();
1875 for (index, &child_id) in child_ids.iter().enumerate() {
1876 match Self::cached_measure_node_with_applier(
1877 &mut applier,
1878 child_id,
1879 child_constraints,
1880 current_epoch,
1881 ) {
1882 Ok(Some(measured)) => {
1883 out[index] = Some(measured.size);
1884 measured_children.insert(child_id, Rc::clone(&measured));
1885 }
1886 Ok(None) => {}
1887 Err(err) => {
1888 let mut slot = measure_error.borrow_mut();
1889 if slot.is_none() {
1890 *slot = Some(err);
1891 }
1892 break;
1893 }
1894 }
1895 }
1896 },
1897 ),
1898 retained_measure_lookup: Box::new(|child_id| {
1899 measured_children.borrow().get(&child_id).cloned()
1900 }),
1901 retained_measure_registrar: Box::new(|measurements| {
1902 let mut measured_children = measured_children.borrow_mut();
1903 for measured in measurements {
1904 measured_children.insert(measured.node_id(), Rc::clone(measured));
1905 }
1906 }),
1907 error: &measure_error,
1908 },
1909 )?;
1910 drop(composer);
1911 slots_guard.restore(slots_host.into_table()?);
1912
1913 if let Some(err) = measure_error.borrow_mut().take() {
1914 return Err(err);
1915 }
1916
1917 let cranpose_ui_layout::MeasureResult {
1918 size: measured_size,
1919 alignment_lines: explicit_lines,
1920 placements,
1921 } = measure_result;
1922
1923 let mut width = measured_size.width + padding.horizontal_sum();
1924 let mut height = measured_size.height + padding.vertical_sum();
1925
1926 width = resolve_dimension(
1927 width,
1928 props.width(),
1929 props.min_width(),
1930 props.max_width(),
1931 constraints.min_width,
1932 constraints.max_width,
1933 );
1934 height = resolve_dimension(
1935 height,
1936 props.height(),
1937 props.min_height(),
1938 props.max_height(),
1939 constraints.min_height,
1940 constraints.max_height,
1941 );
1942
1943 let mut children = Vec::with_capacity(placements.len());
1944 let mut alignment_lines = AlignmentLines::default();
1945 let mut measured_children_by_id = measured_children.borrow_mut();
1946
1947 if let Ok(mut applier) = self.applier.try_borrow_typed() {
1948 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1949 parent_node.set_measured_size(Size { width, height });
1950 parent_node.clear_needs_measure();
1951 parent_node.clear_needs_layout();
1952 });
1953 }
1954
1955 for placement in &placements {
1956 let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1957 measured
1958 } else {
1959 self.measure_node(placement.node_id, inner_constraints)?
1960 };
1961 let policy_position = Point {
1962 x: padding.left + placement.x,
1963 y: padding.top + placement.y,
1964 };
1965 let retained_position = Point {
1966 x: policy_position.x + child.offset.x,
1967 y: policy_position.y + child.offset.y,
1968 };
1969 alignment_lines.merge(
1970 child
1971 .alignment_lines_for_parent()
1972 .translated(policy_position.y),
1973 );
1974
1975 if let Ok(mut applier) = self.applier.try_borrow_typed()
1976 && applier
1977 .with_node::<LayoutNode, _>(placement.node_id, |node| {
1978 node.set_position(retained_position);
1979 })
1980 .is_err()
1981 {
1982 let _ = applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1983 node.set_position(retained_position);
1984 });
1985 }
1986
1987 children.push(MeasuredChild {
1988 node: RefCell::new(child),
1989 offset: policy_position,
1990 });
1991 }
1992
1993 node_handle.set_active_children(children.iter().map(|c| c.node.borrow().node_id));
1994 node_handle.recycle_placement_scratch(placements);
1995
1996 Ok(Some(Rc::new(
1997 MeasuredNode::new(
1998 node_id,
1999 Size { width, height },
2000 offset,
2001 Point::default(),
2002 children,
2003 )
2004 .with_alignment_lines(
2005 alignment_lines
2006 .with_overrides(explicit_lines.translated(padding.top))
2007 .translated(offset.y),
2008 ),
2009 )))
2010 }
2011
2012 fn measure_layout_node(
2013 self: &Rc<Self>,
2014 node_id: NodeId,
2015 constraints: Constraints,
2016 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2017 let mut keep = true;
2018 let (mut applier, measure) = loop {
2019 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2020 return Ok(None);
2021 };
2022 match applier.with_node::<LayoutNode, _>(node_id, |node| {
2023 self.visit_layout_node(node, constraints, keep)
2024 }) {
2025 Ok(LayoutNodeVisit::Measure(measure)) => break (applier, measure),
2026 Ok(LayoutNodeVisit::Cached(measured)) => return Ok(Some(measured)),
2027 Ok(LayoutNodeVisit::Keep(cached)) => {
2028 drop(applier);
2029 if let Some(kept) = self.keep_measurement(node_id, constraints, &cached)? {
2030 return Ok(Some(kept));
2031 }
2032 keep = false;
2033 }
2034 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
2035 Err(err) => return Err(err),
2036 }
2037 };
2038 let LayoutNodeMeasure {
2039 runtime_state,
2040 chain,
2041 mut pools,
2042 } = measure;
2043
2044 let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
2045 state: &runtime_state,
2046 };
2047 self.bind_layout_children(
2048 &mut applier,
2049 &runtime_state,
2050 &pools.child_ids,
2051 ChildPass::Measure,
2052 )?;
2053 drop(applier);
2054 pools.child_ids.clear();
2055
2056 let runtime_state = runtime_state.borrow();
2057 let measurement = self.measure_through_modifier_chain(
2058 node_id,
2059 &runtime_state,
2060 chain,
2061 constraints,
2062 &mut pools.placements,
2063 &mut pools.child_ids,
2064 );
2065
2066 if let Some(err) = runtime_state.frame.error.borrow_mut().take() {
2067 for child_state in &runtime_state.child_states {
2068 child_state.measured.borrow_mut().take();
2069 }
2070 self.with_applier_result(|applier| {
2071 applier.with_node::<LayoutNode, _>(node_id, |node| {
2072 runtime_state.write_node_geometry(node_id, node, &measurement);
2073 })
2074 })
2075 .ok();
2076 return Err(err);
2077 }
2078
2079 let measured = Rc::new(
2080 MeasuredNode::new(
2081 node_id,
2082 measurement.size,
2083 measurement.offset,
2084 measurement.content_offset,
2085 runtime_state.measured_children(
2086 &pools.placements,
2087 &pools.child_ids,
2088 measurement.content_offset,
2089 ),
2090 )
2091 .with_alignment_lines(measurement.alignment_lines)
2092 .with_window_root(measurement.window_root),
2093 );
2094
2095 self.with_applier_result(|applier| {
2096 applier.with_node::<LayoutNode, _>(node_id, |node| {
2097 node.cache_handles()
2098 .store_measurement(constraints, Rc::clone(&measured));
2099 runtime_state.write_node_geometry(node_id, node, &measurement);
2100 node.clear_needs_measure();
2101 node.clear_needs_layout();
2102 node.set_measured_size(measurement.size);
2103 node.set_content_offset(measurement.content_offset);
2104 })
2105 })
2106 .ok();
2107
2108 Ok(Some(measured))
2109 }
2110
2111 fn layout_node_intrinsic(
2116 self: &Rc<Self>,
2117 node_id: NodeId,
2118 kind: IntrinsicKind,
2119 ) -> Result<Option<f32>, NodeError> {
2120 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2121 return Ok(None);
2122 };
2123 let mut pools = VecPools::acquire(&self.frame_arena);
2124 let bound = applier.with_node::<LayoutNode, _>(node_id, |node| {
2125 let runtime_state = node.layout_runtime_state_handle();
2126 let chain = runtime_state.borrow_mut().bind_node(node);
2127 pools.child_ids.extend_from_slice(&node.children);
2128 (runtime_state, chain)
2129 });
2130 let (runtime_state, chain) = match bound {
2131 Ok(bound) => bound,
2132 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
2133 Err(err) => return Err(err),
2134 };
2135 let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
2136 state: &runtime_state,
2137 };
2138 self.bind_layout_children(
2139 &mut applier,
2140 &runtime_state,
2141 &pools.child_ids,
2142 ChildPass::Intrinsics,
2143 )?;
2144 drop(applier);
2145 pools.child_ids.clear();
2146
2147 let runtime_state = runtime_state.borrow();
2148 let value = if chain.uses_chain {
2149 let scope = crate::density::DensityMeasureScope::new(chain.density);
2150 let frame = CoordinatorFrame::new(
2151 &runtime_state.measure_policy,
2152 &scope,
2153 runtime_state.child_measurables.as_slice(),
2154 &runtime_state.child_states,
2155 &mut pools.placements,
2156 &mut pools.child_ids,
2157 );
2158 runtime_state
2159 .coordinator_chain
2160 .intrinsic_from(0, &frame, kind)
2161 } else {
2162 policy_intrinsic(
2163 runtime_state.measure_policy.as_ref(),
2164 runtime_state.child_measurables.as_slice(),
2165 kind,
2166 )
2167 };
2168 match runtime_state.frame.error.borrow_mut().take() {
2169 Some(err) => Err(err),
2170 None => Ok(Some(value)),
2171 }
2172 }
2173
2174 fn measurement_to_keep(
2177 &self,
2178 node: &LayoutNode,
2179 constraints: Constraints,
2180 ) -> Option<Rc<MeasuredNode>> {
2181 if node.needs_measure() || node.needs_layout() || node.is_virtual() {
2182 return None;
2183 }
2184 let cache = node.cache_handles();
2185 if cache.epoch() < self.cache_floor {
2186 return None;
2187 }
2188 cache.get_measurement(constraints)
2189 }
2190
2191 fn keep_measurement(
2197 self: &Rc<Self>,
2198 node_id: NodeId,
2199 constraints: Constraints,
2200 cached: &Rc<MeasuredNode>,
2201 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2202 let runtime_state = self.with_applier_result(|applier| {
2203 applier.with_node::<LayoutNode, _>(node_id, |node| node.layout_runtime_state_handle())
2204 })?;
2205 let mut next_state = 0;
2206 let kept = self.remeasure_changed_children(
2207 cached,
2208 |child_id| self.layout_child_change(&runtime_state, &mut next_state, child_id),
2209 |_| {},
2210 )?;
2211 let Some(kept) = kept else {
2212 return Ok(None);
2213 };
2214 self.with_applier_result(|applier| {
2215 applier.with_node::<LayoutNode, _>(node_id, |node| {
2216 node.cache_handles()
2217 .store_measurement(constraints, Rc::clone(&kept));
2218 node.clear_needs_layout();
2219 })
2220 })?;
2221 Ok(Some(kept))
2222 }
2223
2224 fn remeasure_changed_children(
2230 self: &Rc<Self>,
2231 cached: &Rc<MeasuredNode>,
2232 mut change: impl FnMut(NodeId) -> Result<ChildChange, NodeError>,
2233 mut remeasured: impl FnMut(NodeId),
2234 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
2235 let mut replaced = smallvec::SmallVec::<[(usize, Rc<MeasuredNode>); 4]>::new();
2236 for (index, previous) in cached.children.iter().enumerate() {
2237 let previous = Rc::clone(&previous.node.borrow());
2238 let child_id = previous.node_id();
2239 let child = match change(child_id)? {
2240 ChildChange::Unchanged => continue,
2241 ChildChange::Refuse => return Ok(None),
2242 ChildChange::Changed(child) => child,
2243 };
2244 let measured = self.measure_node(child_id, child.constraints)?;
2245 remeasured(child_id);
2246 if !measures_the_same_for_parent(&previous, &measured) {
2247 return Ok(None);
2248 }
2249 if child.placed {
2250 self.place_where_it_was(child_id);
2251 }
2252 if !Rc::ptr_eq(&previous, &measured) {
2253 replaced.push((index, measured));
2254 }
2255 }
2256 for (index, measured) in replaced {
2257 if let Some(child) = cached.children.get(index) {
2258 *child.node.borrow_mut() = measured;
2259 }
2260 }
2261 Ok(Some(Rc::clone(cached)))
2262 }
2263
2264 fn layout_child_change(
2269 &self,
2270 runtime_state: &RefCell<LayoutRuntimeState>,
2271 next_state: &mut usize,
2272 child_id: NodeId,
2273 ) -> Result<ChildChange, NodeError> {
2274 let runtime_state = runtime_state.borrow();
2275 let Some((offset, state)) = runtime_state
2276 .child_states
2277 .get(*next_state..)
2278 .unwrap_or_default()
2279 .iter()
2280 .enumerate()
2281 .find(|(_, state)| state.node_id == child_id)
2282 else {
2283 return Ok(ChildChange::Refuse);
2284 };
2285 *next_state += offset + 1;
2286 self.child_change(child_id, |view| {
2287 let parent_data_changed = view
2288 .parent_data
2289 .is_some_and(|data| Some(data) != state.parent_data.get());
2290 match state.measured_constraints.get() {
2291 Some(constraints) if !state.read_intrinsics.get() && !parent_data_changed => {
2292 ChildChange::Changed(ChangedChild {
2293 constraints,
2294 placed: view.placed,
2295 })
2296 }
2297 _ => ChildChange::Refuse,
2298 }
2299 })
2300 }
2301
2302 fn store_subcompose_measurement(
2305 &self,
2306 node_id: NodeId,
2307 constraints: Constraints,
2308 measured: &Rc<MeasuredNode>,
2309 ) {
2310 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2311 return;
2312 };
2313 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
2314 node.cache_handles()
2315 .store_measurement(constraints, Rc::clone(measured));
2316 });
2317 }
2318
2319 fn place_where_it_was(&self, child_id: NodeId) {
2322 let Ok(mut applier) = self.applier.try_borrow_typed() else {
2323 return;
2324 };
2325 if applier
2326 .with_node::<LayoutNode, _>(child_id, |child| child.set_position(child.position()))
2327 .is_err()
2328 {
2329 let _ = applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2330 child.set_position(child.layout_state().position());
2331 });
2332 }
2333 }
2334
2335 fn visit_layout_node(
2336 &self,
2337 node: &mut LayoutNode,
2338 constraints: Constraints,
2339 keep: bool,
2340 ) -> LayoutNodeVisit<'_> {
2341 node.clear_placed();
2342 if keep && let Some(cached) = self.measurement_to_keep(node, constraints) {
2343 return LayoutNodeVisit::Keep(cached);
2344 }
2345 let cache = node.cache_handles();
2346 cache.activate(self.cache_epoch);
2347 if !Node::layout_dirty(node)
2348 && let Some(cached) = cache.get_measurement(constraints)
2349 {
2350 node.clear_needs_measure();
2351 node.clear_needs_layout();
2352 return LayoutNodeVisit::Cached(cached);
2353 }
2354
2355 let runtime_state = node.layout_runtime_state_handle();
2356 let chain = runtime_state.borrow_mut().bind_node(node);
2357 let mut pools = VecPools::acquire(&self.frame_arena);
2358 pools.child_ids.extend_from_slice(&node.children);
2359 LayoutNodeVisit::Measure(LayoutNodeMeasure {
2360 runtime_state,
2361 chain,
2362 pools,
2363 })
2364 }
2365
2366 fn bind_layout_children(
2367 self: &Rc<Self>,
2368 applier: &mut MemoryApplier,
2369 runtime_state: &RefCell<LayoutRuntimeState>,
2370 child_ids: &[NodeId],
2371 pass: ChildPass,
2372 ) -> Result<(), NodeError> {
2373 let mut runtime_state = runtime_state.borrow_mut();
2374 runtime_state.frame.bind(self, pass);
2375 let mut bound = 0;
2376 for &child_id in child_ids {
2377 if self.bind_layout_child(applier, &mut runtime_state, bound, child_id)? {
2378 bound += 1;
2379 }
2380 }
2381 runtime_state.truncate_children(bound);
2382 Ok(())
2383 }
2384
2385 fn bind_layout_child(
2386 &self,
2387 applier: &mut MemoryApplier,
2388 runtime_state: &mut LayoutRuntimeState,
2389 position: usize,
2390 child_id: NodeId,
2391 ) -> Result<bool, NodeError> {
2392 let bound = applier.with_node::<LayoutNode, _>(child_id, |child| {
2393 runtime_state.child_state_at(position, child_id).bind(
2394 LayoutChildBinding {
2395 cache: child.cache_handles(),
2396 layout_state: Some(child.layout_state_handle()),
2397 parent_data: Some(parent_data_of(
2398 child.resolved_modifiers().layout_properties(),
2399 )),
2400 dirty: child.needs_layout() || child.needs_measure(),
2401 descendant_dirty: Node::descendant_needs_layout(child),
2402 },
2403 self,
2404 );
2405 });
2406 match bound {
2407 Ok(()) => Ok(true),
2408 Err(NodeError::TypeMismatch { .. }) => {
2409 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
2410 runtime_state.child_state_at(position, child_id).bind(
2411 LayoutChildBinding {
2412 cache: child.cache_handles(),
2413 layout_state: None,
2414 parent_data: Some(parent_data_of(
2415 child.resolved_modifiers().layout_properties(),
2416 )),
2417 dirty: child.needs_layout() || child.needs_measure(),
2418 descendant_dirty: Node::descendant_needs_layout(child),
2419 },
2420 self,
2421 );
2422 }) {
2423 Ok(()) => Ok(true),
2424 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(false),
2425 Err(err) => Err(err),
2426 }
2427 }
2428 Err(NodeError::Missing { .. }) => Ok(false),
2429 Err(err) => Err(err),
2430 }
2431 }
2432
2433 fn measure_through_modifier_chain(
2434 &self,
2435 node_id: NodeId,
2436 runtime_state: &LayoutRuntimeState,
2437 chain: ModifierChainInputs,
2438 constraints: Constraints,
2439 placements: &mut Vec<Placement>,
2440 placement_indices: &mut Vec<usize>,
2441 ) -> ModifierChainMeasurement {
2442 let scope = crate::density::DensityMeasureScope::new(chain.density);
2443
2444 if !chain.uses_chain {
2445 let measurement = runtime_state.measure_policy.measure_into(
2446 &scope,
2447 runtime_state.child_measurables.as_slice(),
2448 constraints,
2449 placements,
2450 );
2451 return ModifierChainMeasurement {
2452 size: measurement.size,
2453 alignment_lines: inherited_alignment_lines(
2454 &runtime_state.child_states,
2455 placements,
2456 placement_indices,
2457 )
2458 .with_overrides(measurement.alignment_lines),
2459 content_offset: Point::default(),
2460 offset: chain.offset,
2461 window_root: chain.window_root,
2462 uses_chain: false,
2463 };
2464 }
2465
2466 let frame = CoordinatorFrame::new(
2467 &runtime_state.measure_policy,
2468 &scope,
2469 runtime_state.child_measurables.as_slice(),
2470 &runtime_state.child_states,
2471 placements,
2472 placement_indices,
2473 );
2474 let placeable = runtime_state
2475 .coordinator_chain
2476 .measure_from(0, &frame, constraints);
2477 let (content_x, content_y) = placeable.content_offset();
2478
2479 let invalidations = frame.take_invalidations();
2480 if !invalidations.is_empty() {
2481 self.with_applier_result(|applier| {
2482 applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
2483 for kind in invalidations {
2484 match kind {
2485 InvalidationKind::Layout => layout_node.mark_needs_measure(),
2486 InvalidationKind::Draw => layout_node.mark_needs_redraw(),
2487 InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
2488 InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
2489 InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
2490 }
2491 }
2492 })
2493 })
2494 .ok();
2495 }
2496
2497 ModifierChainMeasurement {
2498 alignment_lines: placeable.alignment_lines(),
2499 size: Size {
2500 width: placeable.width(),
2501 height: placeable.height(),
2502 },
2503 content_offset: Point {
2504 x: content_x - chain.offset.x,
2505 y: content_y - chain.offset.y,
2506 },
2507 offset: chain.offset,
2508 window_root: chain.window_root,
2509 uses_chain: true,
2510 }
2511 }
2512}
2513
2514struct VecPools<'a> {
2515 arena: &'a RefCell<FrameLayoutArena>,
2516 child_ids: Vec<NodeId>,
2517 placements: Vec<Placement>,
2518}
2519
2520impl<'a> VecPools<'a> {
2521 fn acquire(arena: &'a RefCell<FrameLayoutArena>) -> Self {
2522 let mut pools = arena.borrow_mut();
2523 let child_ids = pools.tmp_child_ids.acquire();
2524 let placements = pools.tmp_placements.acquire();
2525 Self {
2526 arena,
2527 child_ids,
2528 placements,
2529 }
2530 }
2531}
2532
2533impl Drop for VecPools<'_> {
2534 fn drop(&mut self) {
2535 let mut pools = self.arena.borrow_mut();
2536 pools
2537 .tmp_child_ids
2538 .release(std::mem::take(&mut self.child_ids));
2539 pools
2540 .tmp_placements
2541 .release(std::mem::take(&mut self.placements));
2542 }
2543}
2544
2545struct SlotsGuard<'a> {
2546 table: &'a RefCell<SlotTable>,
2547 slots: Option<SlotTable>,
2548}
2549
2550impl<'a> SlotsGuard<'a> {
2551 fn take(table: &'a RefCell<SlotTable>) -> Self {
2552 let slots = std::mem::take(&mut *table.borrow_mut());
2553 Self {
2554 table,
2555 slots: Some(slots),
2556 }
2557 }
2558
2559 fn host(&mut self) -> Rc<SlotsHost> {
2560 let slots = self.slots.take().unwrap_or_default();
2561 Rc::new(SlotsHost::new(slots))
2562 }
2563
2564 fn restore(&mut self, slots: SlotTable) {
2565 debug_assert!(self.slots.is_none());
2566 self.slots = Some(slots);
2567 }
2568}
2569
2570impl Drop for SlotsGuard<'_> {
2571 fn drop(&mut self) {
2572 if let Some(slots) = self.slots.take() {
2573 *self.table.borrow_mut() = slots;
2574 }
2575 }
2576}
2577
2578#[derive(Debug, Clone)]
2579pub(crate) struct MeasuredNode {
2580 node_id: NodeId,
2581 size: Size,
2582 offset: Point,
2583 content_offset: Point,
2584 alignment_lines: AlignmentLines,
2585 children: Vec<MeasuredChild>,
2586 window_root: bool,
2587}
2588
2589impl MeasuredNode {
2590 fn new(
2591 node_id: NodeId,
2592 size: Size,
2593 offset: Point,
2594 content_offset: Point,
2595 children: Vec<MeasuredChild>,
2596 ) -> Self {
2597 Self {
2598 node_id,
2599 size,
2600 offset,
2601 content_offset,
2602 alignment_lines: AlignmentLines::default(),
2603 children,
2604 window_root: false,
2605 }
2606 }
2607
2608 fn with_window_root(mut self, window_root: bool) -> Self {
2609 self.window_root = window_root;
2610 self
2611 }
2612
2613 fn with_alignment_lines(mut self, alignment_lines: AlignmentLines) -> Self {
2614 self.alignment_lines = alignment_lines;
2615 self
2616 }
2617
2618 pub(crate) fn alignment_lines_for_parent(&self) -> AlignmentLines {
2619 if self.window_root {
2620 AlignmentLines::default()
2621 } else {
2622 self.alignment_lines
2623 }
2624 }
2625
2626 pub(crate) fn size_for_parent(&self) -> Size {
2627 if self.window_root {
2628 Size::new(0.0, 0.0)
2629 } else {
2630 self.size
2631 }
2632 }
2633
2634 #[cfg(test)]
2635 pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2636 Self::new(
2637 node_id,
2638 size,
2639 Point::default(),
2640 Point::default(),
2641 Vec::new(),
2642 )
2643 }
2644
2645 pub(crate) fn node_id(&self) -> NodeId {
2646 self.node_id
2647 }
2648
2649 pub(crate) fn size(&self) -> Size {
2650 self.size
2651 }
2652}
2653
2654#[derive(Debug, Clone)]
2655struct MeasuredChild {
2656 node: RefCell<Rc<MeasuredNode>>,
2659 offset: Point,
2660}
2661
2662fn measurable_intrinsic(measurable: &dyn Measurable, kind: IntrinsicKind) -> f32 {
2664 match kind {
2665 IntrinsicKind::MinWidth(height) => measurable.min_intrinsic_width(height),
2666 IntrinsicKind::MaxWidth(height) => measurable.max_intrinsic_width(height),
2667 IntrinsicKind::MinHeight(width) => measurable.min_intrinsic_height(width),
2668 IntrinsicKind::MaxHeight(width) => measurable.max_intrinsic_height(width),
2669 }
2670}
2671
2672fn policy_intrinsic(
2674 policy: &dyn MeasurePolicy,
2675 measurables: &[Box<dyn Measurable>],
2676 kind: IntrinsicKind,
2677) -> f32 {
2678 match kind {
2679 IntrinsicKind::MinWidth(height) => policy.min_intrinsic_width(measurables, height),
2680 IntrinsicKind::MaxWidth(height) => policy.max_intrinsic_width(measurables, height),
2681 IntrinsicKind::MinHeight(width) => policy.min_intrinsic_height(measurables, width),
2682 IntrinsicKind::MaxHeight(width) => policy.max_intrinsic_height(measurables, width),
2683 }
2684}
2685
2686fn layout_modifier_intrinsic(
2688 node: &dyn cranpose_foundation::LayoutModifierNode,
2689 wrapped: &dyn Measurable,
2690 kind: IntrinsicKind,
2691 density: f32,
2692) -> f32 {
2693 match kind {
2694 IntrinsicKind::MinWidth(height) => node.min_intrinsic_width(wrapped, height, density),
2695 IntrinsicKind::MaxWidth(height) => node.max_intrinsic_width(wrapped, height, density),
2696 IntrinsicKind::MinHeight(width) => node.min_intrinsic_height(wrapped, width, density),
2697 IntrinsicKind::MaxHeight(width) => node.max_intrinsic_height(wrapped, width, density),
2698 }
2699}
2700
2701struct CoordinatorFrame<'a> {
2702 measure_policy: &'a Rc<dyn MeasurePolicy>,
2703 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2704 measurables: &'a [Box<dyn Measurable>],
2705 child_states: &'a [Rc<LayoutChildMeasureState>],
2706 placements: RefCell<&'a mut Vec<Placement>>,
2707 placement_indices: RefCell<&'a mut Vec<usize>>,
2708 context: RefCell<LayoutNodeContext>,
2709}
2710
2711impl<'a> CoordinatorFrame<'a> {
2712 fn new(
2713 measure_policy: &'a Rc<dyn MeasurePolicy>,
2714 scope: &'a dyn cranpose_ui_layout::MeasureScope,
2715 measurables: &'a [Box<dyn Measurable>],
2716 child_states: &'a [Rc<LayoutChildMeasureState>],
2717 placements: &'a mut Vec<Placement>,
2718 placement_indices: &'a mut Vec<usize>,
2719 ) -> Self {
2720 Self {
2721 measure_policy,
2722 scope,
2723 measurables,
2724 child_states,
2725 placements: RefCell::new(placements),
2726 placement_indices: RefCell::new(placement_indices),
2727 context: RefCell::new(LayoutNodeContext::new(scope.density())),
2728 }
2729 }
2730
2731 fn take_invalidations(&self) -> Vec<InvalidationKind> {
2732 self.context.borrow_mut().take_invalidations()
2733 }
2734}
2735
2736struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2737 chain: &'chain CoordinatorChain,
2738 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2739 index: usize,
2740 alignment_lines: Cell<AlignmentLines>,
2741}
2742
2743impl<'chain, 'frame_ref, 'frame_data> CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2744 fn new(
2745 chain: &'chain CoordinatorChain,
2746 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2747 index: usize,
2748 ) -> Self {
2749 Self {
2750 chain,
2751 frame,
2752 index,
2753 alignment_lines: Cell::default(),
2754 }
2755 }
2756}
2757
2758impl Measurable for CoordinatorLink<'_, '_, '_> {
2759 fn measure(&self, constraints: Constraints) -> Placeable {
2760 let placeable = self.chain.measure_from(self.index, self.frame, constraints);
2761 self.alignment_lines.set(placeable.alignment_lines());
2762 placeable
2763 }
2764
2765 fn min_intrinsic_width(&self, height: f32) -> f32 {
2766 self.chain
2767 .intrinsic_from(self.index, self.frame, IntrinsicKind::MinWidth(height))
2768 }
2769
2770 fn max_intrinsic_width(&self, height: f32) -> f32 {
2771 self.chain
2772 .intrinsic_from(self.index, self.frame, IntrinsicKind::MaxWidth(height))
2773 }
2774
2775 fn min_intrinsic_height(&self, width: f32) -> f32 {
2776 self.chain
2777 .intrinsic_from(self.index, self.frame, IntrinsicKind::MinHeight(width))
2778 }
2779
2780 fn max_intrinsic_height(&self, width: f32) -> f32 {
2781 self.chain
2782 .intrinsic_from(self.index, self.frame, IntrinsicKind::MaxHeight(width))
2783 }
2784}
2785
2786struct CoordinatorNode {
2787 modifier_index: usize,
2788 node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2789 measured_size: Cell<Size>,
2790 accumulated_offset: Cell<Point>,
2791}
2792
2793impl CoordinatorNode {
2794 fn new(
2795 modifier_index: usize,
2796 node: Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2797 ) -> Self {
2798 Self {
2799 modifier_index,
2800 node,
2801 measured_size: Cell::new(Size::default()),
2802 accumulated_offset: Cell::new(Point::default()),
2803 }
2804 }
2805
2806 fn matches(
2807 &self,
2808 modifier_index: usize,
2809 node: &Rc<RefCell<dyn cranpose_foundation::ModifierNode>>,
2810 ) -> bool {
2811 self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2812 }
2813
2814 #[cfg(test)]
2815 fn ptr(&self) -> usize {
2816 Rc::as_ptr(&self.node) as *const () as usize
2817 }
2818}
2819
2820#[derive(Default)]
2821struct CoordinatorChain {
2822 nodes: Vec<CoordinatorNode>,
2823 inner_size: Cell<Size>,
2825 synced: Option<(u64, ModifierChainInputs)>,
2829}
2830
2831impl CoordinatorChain {
2832 fn sync(&mut self, node: &LayoutNode) -> ModifierChainInputs {
2833 let density = node.density();
2834 let revision = node.modifier_chain().revision();
2835 if let Some((synced, inputs)) = self.synced
2836 && synced == revision
2837 && inputs.density == density
2838 {
2839 return inputs;
2840 }
2841 let inputs = self.walk(node, density);
2842 self.synced = Some((revision, inputs));
2843 inputs
2844 }
2845
2846 fn walk(&mut self, node: &LayoutNode, density: crate::density::Density) -> ModifierChainInputs {
2849 let mut inputs = ModifierChainInputs {
2850 density,
2851 window_root: node.is_window_root(),
2852 offset: Point::default(),
2853 uses_chain: false,
2854 };
2855 let chain_handle = node.modifier_chain();
2856 if !chain_handle.has_layout_nodes() {
2857 return inputs;
2858 }
2859
2860 let chain = chain_handle.chain();
2861 let mut len = 0;
2862 let mut matches = true;
2863 chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2864 let Some(index) = node_ref.entry_index() else {
2865 return;
2866 };
2867 if let Some(node) = chain.get_node_rc(index) {
2868 matches = matches
2869 && self
2870 .nodes
2871 .get(len)
2872 .is_some_and(|candidate| candidate.matches(index, node));
2873 len += 1;
2874 }
2875 node_ref.with_node(|node| {
2876 if let Some(offset_node) = node
2877 .as_any()
2878 .downcast_ref::<crate::modifier_nodes::OffsetNode>()
2879 {
2880 let delta = offset_node.device_offset(density.density());
2881 inputs.offset.x += delta.x;
2882 inputs.offset.y += delta.y;
2883 }
2884 });
2885 });
2886
2887 inputs.uses_chain = len > 0;
2888 if inputs.uses_chain && !(matches && len == self.nodes.len()) {
2889 self.rebuild(chain, len);
2890 }
2891 inputs
2892 }
2893
2894 fn rebuild(&mut self, chain: &cranpose_foundation::ModifierNodeChain, len: usize) {
2895 let mut previous_nodes = std::mem::take(&mut self.nodes);
2896 self.nodes.reserve(len);
2897 chain.for_each_forward_matching(NodeCapabilities::LAYOUT, |node_ref| {
2898 let Some((index, node)) = node_ref
2899 .entry_index()
2900 .and_then(|index| chain.get_node_rc(index).map(|node| (index, node)))
2901 else {
2902 return;
2903 };
2904 match previous_nodes
2905 .iter()
2906 .position(|candidate| candidate.matches(index, node))
2907 {
2908 Some(position) => self.nodes.push(previous_nodes.swap_remove(position)),
2909 None => self
2910 .nodes
2911 .push(CoordinatorNode::new(index, Rc::clone(node))),
2912 }
2913 });
2914 }
2915
2916 fn measure_from(
2917 &self,
2918 index: usize,
2919 frame: &CoordinatorFrame<'_>,
2920 constraints: Constraints,
2921 ) -> Placeable {
2922 let Some(node) = self.nodes.get(index) else {
2923 let mut placements = frame.placements.borrow_mut();
2924 let measurement = frame.measure_policy.measure_into(
2925 frame.scope,
2926 frame.measurables,
2927 constraints,
2928 &mut placements,
2929 );
2930 self.inner_size.set(measurement.size);
2931 return Placeable::value(
2932 measurement.size.width,
2933 measurement.size.height,
2934 NodeId::default(),
2935 )
2936 .with_alignment_lines(
2937 inherited_alignment_lines(
2938 frame.child_states,
2939 &placements,
2940 &mut frame.placement_indices.borrow_mut(),
2941 )
2942 .with_overrides(measurement.alignment_lines),
2943 );
2944 };
2945
2946 let wrapped = CoordinatorLink::new(self, frame, index + 1);
2947 let node_borrow = node.node.borrow();
2948
2949 let Some(layout_node) = node_borrow.as_layout_node() else {
2950 let placeable = wrapped.measure(constraints);
2951 node.measured_size.set(Size {
2952 width: placeable.width(),
2953 height: placeable.height(),
2954 });
2955 let child_accumulated = self.total_content_offset_from(index + 1);
2956 node.accumulated_offset.set(child_accumulated);
2957 return placeable;
2958 };
2959
2960 let result = match frame.context.try_borrow_mut() {
2961 Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
2962 Err(_) => {
2963 let mut temp = LayoutNodeContext::new(frame.scope.density());
2964 let result = layout_node.measure(&mut temp, &wrapped, constraints);
2965 if let Ok(mut context) = frame.context.try_borrow_mut() {
2966 for kind in temp.take_invalidations() {
2967 context.invalidate(kind);
2968 }
2969 }
2970 result
2971 }
2972 };
2973
2974 node.measured_size.set(result.size);
2975 let local_offset = Point {
2976 x: result.placement_offset_x,
2977 y: result.placement_offset_y,
2978 };
2979 let child_accumulated = self.total_content_offset_from(index + 1);
2980 let accumulated = Point {
2981 x: local_offset.x + child_accumulated.x,
2982 y: local_offset.y + child_accumulated.y,
2983 };
2984 node.accumulated_offset.set(accumulated);
2985
2986 Placeable::value_with_offset(
2987 result.size.width,
2988 result.size.height,
2989 NodeId::default(),
2990 (accumulated.x, accumulated.y),
2991 )
2992 .with_alignment_lines(
2993 wrapped
2994 .alignment_lines
2995 .get()
2996 .translated(local_offset.y)
2997 .with_overrides(result.alignment_lines),
2998 )
2999 }
3000
3001 fn intrinsic_from(
3002 &self,
3003 index: usize,
3004 frame: &CoordinatorFrame<'_>,
3005 kind: IntrinsicKind,
3006 ) -> f32 {
3007 let Some(node) = self.nodes.get(index) else {
3008 return policy_intrinsic(frame.measure_policy.as_ref(), frame.measurables, kind);
3009 };
3010 let wrapped = CoordinatorLink::new(self, frame, index + 1);
3011 let node_borrow = node.node.borrow();
3012 node_borrow.as_layout_node().map_or_else(
3013 || measurable_intrinsic(&wrapped, kind),
3014 |layout_node| {
3015 layout_modifier_intrinsic(layout_node, &wrapped, kind, frame.scope.density())
3016 },
3017 )
3018 }
3019
3020 fn write_geometry(
3024 &self,
3025 geometry: &crate::modifier::CoordinatorGeometry,
3026 node_offset: Point,
3027 ) -> bool {
3028 let content = self.total_content_offset_from(0);
3029 let placed = |inner_offset: Point, size: Size| GeometryRect {
3030 x: content.x - inner_offset.x - node_offset.x,
3031 y: content.y - inner_offset.y - node_offset.y,
3032 width: size.width,
3033 height: size.height,
3034 };
3035 geometry.replace(
3036 self.nodes
3037 .iter()
3038 .map(|node| placed(node.accumulated_offset.get(), node.measured_size.get()))
3039 .chain(std::iter::once(placed(
3040 Point::default(),
3041 self.inner_size.get(),
3042 ))),
3043 )
3044 }
3045
3046 fn total_content_offset_from(&self, index: usize) -> Point {
3047 self.nodes
3048 .get(index)
3049 .map(|node| node.accumulated_offset.get())
3050 .unwrap_or_default()
3051 }
3052
3053 #[cfg(test)]
3054 fn debug_ptrs(&self) -> Vec<usize> {
3055 self.nodes.iter().map(CoordinatorNode::ptr).collect()
3056 }
3057}
3058
3059fn inherited_alignment_lines(
3060 child_states: &[Rc<LayoutChildMeasureState>],
3061 placements: &[Placement],
3062 placement_indices: &mut Vec<usize>,
3063) -> AlignmentLines {
3064 placement_indices.clear();
3065 let in_child_order = placements.len() == child_states.len()
3066 && placements
3067 .iter()
3068 .zip(child_states)
3069 .all(|(placement, child)| placement.node_id == child.node_id);
3070 if !in_child_order {
3071 placement_indices.extend(0..placements.len());
3072 placement_indices.sort_unstable_by_key(|&index| (placements[index].node_id, index));
3073 }
3074 let mut lines = AlignmentLines::default();
3075 for (index, child) in child_states.iter().enumerate() {
3076 let placement = placement_for_child(placements, placement_indices, index, child.node_id);
3077 if let Some(measured) = child.measured.borrow().as_ref() {
3078 lines.merge(
3079 measured
3080 .alignment_lines_for_parent()
3081 .translated(child.placement_position(placement).y),
3082 );
3083 }
3084 }
3085 lines
3086}
3087
3088fn placement_for_child<'a>(
3089 placements: &'a [Placement],
3090 indices: &[usize],
3091 child_index: usize,
3092 node_id: NodeId,
3093) -> Option<&'a Placement> {
3094 let index = if indices.is_empty() {
3095 child_index
3096 } else {
3097 let first = indices.partition_point(|&index| placements[index].node_id < node_id);
3098 *indices.get(first)?
3099 };
3100 placements
3101 .get(index)
3102 .filter(|placement| placement.node_id == node_id)
3103}
3104
3105pub(crate) struct LayoutRuntimeState {
3106 child_ids: Vec<NodeId>,
3107 child_states: Vec<Rc<LayoutChildMeasureState>>,
3108 child_measurables: Vec<Box<dyn Measurable>>,
3109 coordinator_chain: CoordinatorChain,
3110 measure_policy: Rc<dyn MeasurePolicy>,
3111 frame: Rc<LayoutChildFrame>,
3112}
3113
3114impl LayoutRuntimeState {
3115 pub(crate) fn new(measure_policy: Rc<dyn MeasurePolicy>) -> Self {
3116 Self {
3117 child_ids: Vec::new(),
3118 child_states: Vec::new(),
3119 child_measurables: Vec::new(),
3120 coordinator_chain: CoordinatorChain::default(),
3121 measure_policy,
3122 frame: Rc::default(),
3123 }
3124 }
3125
3126 fn bind_node(&mut self, node: &LayoutNode) -> ModifierChainInputs {
3127 if !Rc::ptr_eq(&self.measure_policy, &node.measure_policy) {
3128 self.measure_policy = Rc::clone(&node.measure_policy);
3129 }
3130 self.coordinator_chain.sync(node)
3131 }
3132
3133 fn child_state_at(&mut self, position: usize, child_id: NodeId) -> &LayoutChildMeasureState {
3134 if self.child_ids.get(position) != Some(&child_id) {
3135 let from = match self.child_ids[position..]
3136 .iter()
3137 .position(|&id| id == child_id)
3138 {
3139 Some(offset) => position + offset,
3140 None => {
3141 let state = LayoutChildMeasureState::new(child_id, Rc::clone(&self.frame));
3142 self.child_ids.push(child_id);
3143 self.child_states.push(Rc::clone(&state));
3144 self.child_measurables
3145 .push(Box::new(LayoutChildMeasurable::new(state)));
3146 self.child_ids.len() - 1
3147 }
3148 };
3149 self.child_ids.swap(position, from);
3150 self.child_states.swap(position, from);
3151 self.child_measurables.swap(position, from);
3152 }
3153 &self.child_states[position]
3154 }
3155
3156 fn truncate_children(&mut self, len: usize) {
3157 self.child_ids.truncate(len);
3158 self.child_states.truncate(len);
3159 self.child_measurables.truncate(len);
3160 }
3161
3162 fn measured_children(
3163 &self,
3164 placements: &[Placement],
3165 placement_indices: &[usize],
3166 content_offset: Point,
3167 ) -> Vec<MeasuredChild> {
3168 let mut measured_children = Vec::with_capacity(self.child_states.len());
3169 for (index, child_state) in self.child_states.iter().enumerate() {
3170 let Some(measured) = child_state.measured.borrow_mut().take() else {
3171 continue;
3172 };
3173 let placement =
3174 placement_for_child(placements, placement_indices, index, child_state.node_id);
3175 let base_position = child_state.placement_position(placement);
3176 if placement.is_some() {
3177 child_state.place_retained(Point {
3178 x: base_position.x + measured.offset.x,
3179 y: base_position.y + measured.offset.y,
3180 });
3181 }
3182 measured_children.push(MeasuredChild {
3183 node: RefCell::new(measured),
3184 offset: Point {
3185 x: content_offset.x + base_position.x,
3186 y: content_offset.y + base_position.y,
3187 },
3188 });
3189 }
3190 measured_children
3191 }
3192
3193 fn write_node_geometry(
3194 &self,
3195 node_id: NodeId,
3196 node: &LayoutNode,
3197 measurement: &ModifierChainMeasurement,
3198 ) {
3199 let geometry = node.coordinator_geometry();
3200 let moved = if measurement.uses_chain {
3201 self.coordinator_chain
3202 .write_geometry(geometry, measurement.offset)
3203 } else {
3204 geometry.replace([GeometryRect {
3205 x: 0.0,
3206 y: 0.0,
3207 width: measurement.size.width,
3208 height: measurement.size.height,
3209 }])
3210 };
3211 if moved {
3212 crate::render_state::record_geometry_scene_node(node_id);
3213 }
3214 }
3215
3216 #[cfg(test)]
3217 pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
3218 LayoutRuntimeDebugStats {
3219 child_ids: self.child_ids.clone(),
3220 child_state_ptrs: self
3221 .child_states
3222 .iter()
3223 .map(|state| Rc::as_ptr(state) as *const () as usize)
3224 .collect(),
3225 child_measurable_ptrs: self
3226 .child_measurables
3227 .iter()
3228 .map(|measurable| {
3229 measurable.as_ref() as *const dyn Measurable as *const () as usize
3230 })
3231 .collect(),
3232 child_measurable_count: self.child_measurables.len(),
3233 coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
3234 coordinator_node_count: self.coordinator_chain.nodes.len(),
3235 }
3236 }
3237}
3238
3239#[cfg(test)]
3240#[derive(Debug, Clone, PartialEq, Eq)]
3241pub(crate) struct LayoutRuntimeDebugStats {
3242 pub(crate) child_ids: Vec<NodeId>,
3243 pub(crate) child_state_ptrs: Vec<usize>,
3244 pub(crate) child_measurable_ptrs: Vec<usize>,
3245 pub(crate) child_measurable_count: usize,
3246 pub(crate) coordinator_node_ptrs: Vec<usize>,
3247 pub(crate) coordinator_node_count: usize,
3248}
3249
3250#[derive(Default)]
3251struct LayoutChildFrame {
3252 builder: RefCell<Option<Rc<LayoutBuilderState>>>,
3253 error: RefCell<Option<NodeError>>,
3254 pass: Cell<ChildPass>,
3255}
3256
3257#[derive(Clone, Copy, Default, PartialEq, Eq)]
3259enum ChildPass {
3260 #[default]
3263 Measure,
3264 Intrinsics,
3267}
3268
3269impl LayoutChildFrame {
3270 fn bind(&self, builder: &Rc<LayoutBuilderState>, pass: ChildPass) {
3271 self.error.borrow_mut().take();
3272 *self.builder.borrow_mut() = Some(Rc::clone(builder));
3273 self.pass.set(pass);
3274 }
3275
3276 fn measures(&self) -> bool {
3277 self.pass.get() == ChildPass::Measure
3278 }
3279
3280 fn unbind(&self) {
3281 self.builder.borrow_mut().take();
3282 }
3283
3284 fn record_error(&self, err: NodeError) {
3285 if self.builder.borrow().is_none() {
3286 return;
3287 }
3288 let mut slot = self.error.borrow_mut();
3289 if slot.is_none() {
3290 *slot = Some(err);
3291 }
3292 }
3293}
3294
3295struct LayoutChildBinding<'a> {
3296 cache: &'a LayoutNodeCacheHandles,
3297 layout_state: Option<&'a Rc<RefCell<LayoutState>>>,
3298 parent_data: Option<cranpose_ui_layout::ParentData>,
3299 dirty: bool,
3301 descendant_dirty: bool,
3303}
3304
3305fn read_layout_child<R>(
3308 applier: &mut MemoryApplier,
3309 child_id: NodeId,
3310 read: impl Fn(
3311 &dyn Node,
3312 &LayoutNodeCacheHandles,
3313 &dyn Fn() -> crate::modifier::LayoutProperties,
3314 bool,
3315 ) -> R,
3316) -> Result<Option<R>, NodeError> {
3317 match applier.with_node::<LayoutNode, _>(child_id, |child| {
3318 read(
3319 &*child,
3320 child.cache_handles(),
3321 &|| child.resolved_modifiers().layout_properties(),
3322 child.is_placed(),
3323 )
3324 }) {
3325 Ok(value) => Ok(Some(value)),
3326 Err(NodeError::TypeMismatch { .. }) => {
3327 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |child| {
3328 read(
3329 &*child,
3330 child.cache_handles(),
3331 &|| child.resolved_modifiers().layout_properties(),
3332 child.layout_state().is_placed(),
3333 )
3334 }) {
3335 Ok(value) => Ok(Some(value)),
3336 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => Ok(None),
3337 Err(err) => Err(err),
3338 }
3339 }
3340 Err(NodeError::Missing { .. }) => Ok(None),
3341 Err(err) => Err(err),
3342 }
3343}
3344
3345fn measures_the_same_for_parent(previous: &MeasuredNode, measured: &MeasuredNode) -> bool {
3349 let (before, after) = (
3350 previous.alignment_lines_for_parent(),
3351 measured.alignment_lines_for_parent(),
3352 );
3353 previous.size_for_parent() == measured.size_for_parent()
3354 && previous.offset == measured.offset
3355 && before.first_baseline() == after.first_baseline()
3356 && before.last_baseline() == after.last_baseline()
3357}
3358
3359fn parent_data_of(props: crate::modifier::LayoutProperties) -> cranpose_ui_layout::ParentData {
3360 let weight = props.weight().unwrap_or_default();
3361 cranpose_ui_layout::ParentData {
3362 weight: weight.weight,
3363 fill: weight.fill,
3364 box_alignment: props.box_alignment(),
3365 row_alignment: props.row_alignment(),
3366 row_baseline: props.row_baseline(),
3367 column_alignment: props.column_alignment(),
3368 }
3369}
3370
3371struct LayoutChildMeasureState {
3372 node_id: NodeId,
3373 frame: Rc<LayoutChildFrame>,
3374 cache: RefCell<LayoutNodeCacheHandles>,
3375 cache_epoch: Cell<u64>,
3376 force_remeasure: Cell<bool>,
3377 parent_data: Cell<Option<cranpose_ui_layout::ParentData>>,
3378 measured_constraints: Cell<Option<Constraints>>,
3380 read_intrinsics: Cell<bool>,
3382 measured: RefCell<Option<Rc<MeasuredNode>>>,
3383 last_position: Cell<Option<Point>>,
3384 layout_state: RefCell<Option<Rc<RefCell<LayoutState>>>>,
3385}
3386
3387impl LayoutChildMeasureState {
3388 fn placement_position(&self, placement: Option<&Placement>) -> Point {
3389 placement
3390 .map(|placement| Point {
3391 x: placement.x,
3392 y: placement.y,
3393 })
3394 .or_else(|| self.last_position.get())
3395 .unwrap_or_default()
3396 }
3397
3398 fn new(node_id: NodeId, frame: Rc<LayoutChildFrame>) -> Rc<Self> {
3399 Rc::new(Self {
3400 node_id,
3401 frame,
3402 cache: RefCell::new(LayoutNodeCacheHandles::default()),
3403 cache_epoch: Cell::new(0),
3404 force_remeasure: Cell::new(true),
3405 parent_data: Cell::new(None),
3406 measured_constraints: Cell::new(None),
3407 read_intrinsics: Cell::new(false),
3408 measured: RefCell::new(None),
3409 last_position: Cell::new(None),
3410 layout_state: RefCell::new(None),
3411 })
3412 }
3413
3414 fn bind(&self, binding: LayoutChildBinding<'_>, pass: &LayoutBuilderState) {
3418 let child_epoch = binding.cache.epoch();
3419 let stale = binding.dirty || child_epoch < pass.cache_floor;
3420 let cache_epoch = if stale { pass.cache_epoch } else { child_epoch };
3421 binding.cache.activate(cache_epoch);
3422 self.cache.borrow_mut().clone_from(binding.cache);
3423 self.cache_epoch.set(cache_epoch);
3424 self.force_remeasure.set(stale || binding.descendant_dirty);
3425 self.parent_data.set(binding.parent_data);
3426 if self.frame.measures() {
3427 self.measured.borrow_mut().take();
3428 self.last_position.set(None);
3429 self.measured_constraints.set(None);
3430 self.read_intrinsics.set(false);
3431 }
3432 let mut layout_state = self.layout_state.borrow_mut();
3433 let shared = layout_state
3434 .as_ref()
3435 .zip(binding.layout_state)
3436 .is_some_and(|(current, bound)| Rc::ptr_eq(current, bound));
3437 if !shared {
3438 *layout_state = binding.layout_state.cloned();
3439 }
3440 }
3441
3442 fn place_retained(&self, position: Point) {
3443 self.last_position.set(Some(position));
3444 let builder = self.frame.builder.borrow();
3445 let Some(builder) = builder.as_ref() else {
3446 return;
3447 };
3448 if let Some(layout_state) = self.layout_state.borrow().as_ref() {
3449 layout_state.borrow_mut().place(position);
3450 return;
3451 }
3452 let Ok(mut applier) = builder.applier.try_borrow_typed() else {
3453 return;
3454 };
3455 let _ = applier.with_node::<SubcomposeLayoutNode, _>(self.node_id, |node| {
3456 node.set_position(position);
3457 });
3458 }
3459
3460 fn layout_intrinsic(&self, kind: IntrinsicKind) -> Option<f32> {
3464 let builder = self.frame.builder.borrow();
3465 let builder = builder.as_ref()?;
3466 match builder.layout_node_intrinsic(self.node_id, kind) {
3467 Ok(value) => value,
3468 Err(err) => {
3469 self.frame.record_error(err);
3470 Some(0.0)
3471 }
3472 }
3473 }
3474
3475 fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
3476 let builder = self.frame.builder.borrow();
3477 let builder = builder.as_ref().ok_or(NodeError::MissingContext {
3478 id: self.node_id,
3479 reason: "layout child applier not configured",
3480 })?;
3481 builder.measure_node(self.node_id, constraints)
3482 }
3483
3484 fn measure_cached(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
3485 if self.frame.measures() {
3486 self.measured_constraints.set(Some(constraints));
3487 }
3488 let cache = self.cache.borrow();
3489 cache.activate(self.cache_epoch.get());
3490 if !self.force_remeasure.get()
3491 && let Some(cached) = cache.get_measurement(constraints)
3492 {
3493 return Some(cached);
3494 }
3495
3496 match self.perform_measure(constraints) {
3497 Ok(measured) => {
3498 self.force_remeasure.set(false);
3499 cache.store_measurement(constraints, Rc::clone(&measured));
3500 Some(measured)
3501 }
3502 Err(err) => {
3503 self.frame.record_error(err);
3504 None
3505 }
3506 }
3507 }
3508}
3509
3510struct LayoutChildMeasurable {
3511 state: Rc<LayoutChildMeasureState>,
3512}
3513
3514impl LayoutChildMeasurable {
3515 fn new(state: Rc<LayoutChildMeasureState>) -> Self {
3516 Self { state }
3517 }
3518
3519 fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
3520 if self.state.frame.builder.borrow().is_none() {
3521 return None;
3522 }
3523 self.state.parent_data.get()
3524 }
3525
3526 fn intrinsic(
3527 &self,
3528 kind: IntrinsicKind,
3529 constraints: Constraints,
3530 extent: fn(Size) -> f32,
3531 ) -> f32 {
3532 let state = &self.state;
3533 if state.frame.measures() {
3534 state.read_intrinsics.set(true);
3535 }
3536 let cache = state.cache.borrow();
3537 cache.activate(state.cache_epoch.get());
3538 if !state.force_remeasure.get()
3539 && let Some(value) = cache.get_intrinsic(&kind)
3540 {
3541 return value;
3542 }
3543 let value = match state.layout_intrinsic(kind) {
3544 Some(value) => value,
3545 None => state
3546 .measure_cached(constraints)
3547 .map_or(0.0, |node| extent(node.size_for_parent())),
3548 };
3549 cache.store_intrinsic(kind, value);
3550 value
3551 }
3552}
3553
3554impl PlaceTarget for LayoutChildMeasureState {
3555 fn place(&self, x: f32, y: f32) {
3556 let internal_offset = self
3557 .measured
3558 .borrow()
3559 .as_ref()
3560 .map(|measured| measured.offset)
3561 .unwrap_or_default();
3562 self.place_retained(Point {
3563 x: x + internal_offset.x,
3564 y: y + internal_offset.y,
3565 });
3566 }
3567}
3568
3569impl Measurable for LayoutChildMeasurable {
3570 fn measure(&self, constraints: Constraints) -> Placeable {
3571 let state = &self.state;
3572 let measured = state.measure_cached(constraints);
3573 let (measured_size, size_for_parent) = measured.as_ref().map_or(
3574 (
3575 Size {
3576 width: 0.0,
3577 height: 0.0,
3578 },
3579 Size {
3580 width: 0.0,
3581 height: 0.0,
3582 },
3583 ),
3584 |measured| (measured.size, measured.size_for_parent()),
3585 );
3586 if let Some(layout_state) = state.layout_state.borrow().as_ref() {
3587 layout_state.borrow_mut().set_size(measured_size);
3588 }
3589 let alignment_lines = measured
3590 .as_ref()
3591 .map_or_else(AlignmentLines::default, |node| {
3592 node.alignment_lines_for_parent()
3593 });
3594 *state.measured.borrow_mut() = measured;
3595
3596 Placeable::with_place_target(
3597 size_for_parent.width,
3598 size_for_parent.height,
3599 state.node_id,
3600 Rc::clone(&self.state) as Rc<dyn PlaceTarget>,
3601 )
3602 .with_alignment_lines(alignment_lines)
3603 }
3604
3605 fn min_intrinsic_width(&self, height: f32) -> f32 {
3606 self.intrinsic(
3607 IntrinsicKind::MinWidth(height),
3608 Constraints {
3609 min_width: 0.0,
3610 max_width: f32::INFINITY,
3611 min_height: height,
3612 max_height: height,
3613 },
3614 |size| size.width,
3615 )
3616 }
3617
3618 fn max_intrinsic_width(&self, height: f32) -> f32 {
3619 self.intrinsic(
3620 IntrinsicKind::MaxWidth(height),
3621 Constraints {
3622 min_width: 0.0,
3623 max_width: f32::INFINITY,
3624 min_height: 0.0,
3625 max_height: height,
3626 },
3627 |size| size.width,
3628 )
3629 }
3630
3631 fn min_intrinsic_height(&self, width: f32) -> f32 {
3632 self.intrinsic(
3633 IntrinsicKind::MinHeight(width),
3634 Constraints {
3635 min_width: width,
3636 max_width: width,
3637 min_height: 0.0,
3638 max_height: f32::INFINITY,
3639 },
3640 |size| size.height,
3641 )
3642 }
3643
3644 fn max_intrinsic_height(&self, width: f32) -> f32 {
3645 self.intrinsic(
3646 IntrinsicKind::MaxHeight(width),
3647 Constraints {
3648 min_width: 0.0,
3649 max_width: width,
3650 min_height: 0.0,
3651 max_height: f32::INFINITY,
3652 },
3653 |size| size.height,
3654 )
3655 }
3656
3657 fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3658 let parent_data = self.resolved_parent_data()?;
3659 if !parent_data.has_weight() {
3660 return None;
3661 }
3662 Some(cranpose_ui_layout::FlexParentData::new(
3663 parent_data.weight,
3664 parent_data.fill,
3665 ))
3666 }
3667
3668 fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3669 self.resolved_parent_data().unwrap_or_default()
3670 }
3671}
3672
3673#[derive(Clone)]
3674struct RuntimeNodeMetadata {
3675 modifier: Modifier,
3676 resolved_modifiers: ResolvedModifiers,
3677 modifier_slices: Rc<ModifierNodeSlices>,
3678 semantics: Option<Rc<SemanticsConfiguration>>,
3679 role: SemanticsRole,
3680 button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3681}
3682
3683impl Default for RuntimeNodeMetadata {
3684 fn default() -> Self {
3685 Self {
3686 modifier: Modifier::empty(),
3687 resolved_modifiers: ResolvedModifiers::default(),
3688 modifier_slices: Rc::default(),
3689 semantics: None,
3690 role: SemanticsRole::Unknown,
3691 button_handler: None,
3692 }
3693 }
3694}
3695
3696fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3697 modifier_slices
3698 .annotated_text()
3699 .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
3700 value: SemanticsText(Rc::clone(text)),
3701 })
3702}
3703
3704fn runtime_metadata_for(
3705 applier: &mut MemoryApplier,
3706 node_id: NodeId,
3707) -> Result<RuntimeNodeMetadata, NodeError> {
3708 if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3709 let modifier = layout.modifier.clone();
3710 let resolved_modifiers = layout.resolved_modifiers();
3711 let modifier_slices = layout.modifier_slices_snapshot();
3712 let role = role_from_modifier_slices(&modifier_slices);
3713
3714 RuntimeNodeMetadata {
3715 modifier,
3716 resolved_modifiers,
3717 modifier_slices,
3718 semantics: layout.semantics_configuration().map(Rc::new),
3719 role,
3720 button_handler: None,
3721 }
3722 }) {
3723 return Ok(meta);
3724 }
3725
3726 if let Ok((modifier, resolved_modifiers, modifier_slices, semantics)) =
3727 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3728 (
3729 node.modifier(),
3730 node.resolved_modifiers(),
3731 node.modifier_slices_snapshot(),
3732 node.semantics_configuration().map(Rc::new),
3733 )
3734 })
3735 {
3736 return Ok(RuntimeNodeMetadata {
3737 modifier,
3738 resolved_modifiers,
3739 modifier_slices,
3740 semantics,
3741 role: SemanticsRole::Subcompose,
3742 button_handler: None,
3743 });
3744 }
3745 Ok(RuntimeNodeMetadata::default())
3746}
3747
3748fn clear_semantics_dirty_flags(
3749 applier: &mut MemoryApplier,
3750 node: &MeasuredNode,
3751) -> Result<(), NodeError> {
3752 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3753 layout.clear_needs_semantics();
3754 }) {
3755 Ok(()) => {}
3756 Err(NodeError::Missing { .. }) => {}
3757 Err(NodeError::TypeMismatch { .. }) => {
3758 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3759 subcompose.clear_needs_semantics();
3760 }) {
3761 Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
3762 Err(err) => return Err(err),
3763 }
3764 }
3765 Err(err) => return Err(err),
3766 }
3767
3768 for child in &node.children {
3769 clear_semantics_dirty_flags(applier, &child.node.borrow())?;
3770 }
3771
3772 Ok(())
3773}
3774
3775fn build_semantics_tree_from_live_nodes(
3776 applier: &mut MemoryApplier,
3777 node: &MeasuredNode,
3778) -> Result<SemanticsTree, NodeError> {
3779 Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3780 applier, node, None,
3781 )?))
3782}
3783
3784fn semantics_node_from_parts(
3785 node_id: NodeId,
3786 node_generation: u32,
3787 mut role: SemanticsRole,
3788 config: Option<SemanticsConfiguration>,
3789 children: Vec<SemanticsNode>,
3790 held_details: Option<Box<SemanticsDetails>>,
3791 bounds: GeometryRect,
3792) -> SemanticsNode {
3793 let focusable = crate::focus_dispatch::has_focus_target(node_id);
3794 let mut node = SemanticsNode {
3795 node_id,
3796 bounds,
3797 node_generation,
3798 children,
3799 focusable,
3800 focused: focusable && crate::focus_dispatch::active_focus_target() == Some(node_id),
3801 details: held_details,
3802 ..SemanticsNode::default()
3803 };
3804 let details = match config {
3805 Some(mut config) => {
3806 if config.role == Some(SemanticsWidgetRole::Button) {
3807 role = SemanticsRole::Button;
3808 }
3809 if config.is_activatable() {
3810 node.actions.push(SemanticsAction::Click {
3811 handler: SemanticsCallback::new(node_id),
3812 });
3813 }
3814 let on_click_label = config.on_click_label.take().or_else(|| {
3815 config
3816 .on_click
3817 .as_ref()
3818 .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
3819 });
3820 node.widget_role = config.role;
3821 node.description = config.content_description.take();
3822 node.on_click = config.on_click.take();
3823 node.selected = config.selected;
3824 node.toggled = config.toggled;
3825 node.enabled = config.enabled;
3826 node.hidden = config.hidden;
3827 node.merge_descendants = config.merge_descendants;
3828 node.traversal_index = config.traversal_index;
3829 node.text = config.text.take();
3830 let is_modal = config.is_modal
3831 && modal_takes_space(Size {
3832 width: bounds.width,
3833 height: bounds.height,
3834 });
3835 SemanticsDetails::from_configuration(config, on_click_label, is_modal)
3836 }
3837 None => SemanticsDetails::NONE,
3838 };
3839 node.set_details(details);
3840 node.role = role;
3841 node
3842}
3843
3844fn build_semantics_node_from_live_nodes(
3845 applier: &mut MemoryApplier,
3846 node: &MeasuredNode,
3847 origin: Option<Point>,
3848) -> Result<SemanticsNode, NodeError> {
3849 let (role, config, (bounds, content), placement) =
3850 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3851 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
3852 let config = layout.semantics_configuration();
3853 layout.clear_needs_semantics();
3854 let state = layout.layout_state();
3855 (
3856 role,
3857 config,
3858 semantics_placement(&state, origin),
3859 SemanticsPlacement::of(&state),
3860 )
3861 }) {
3862 Ok(data) => data,
3863 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3864 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3865 subcompose.clear_needs_semantics();
3866 let state = subcompose.layout_state();
3867 (
3868 SemanticsRole::Subcompose,
3869 subcompose.semantics_configuration(),
3870 semantics_placement(&state, origin),
3871 SemanticsPlacement::of(&state),
3872 )
3873 }) {
3874 Ok(data) => data,
3875 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
3876 let top_left = origin.unwrap_or_default();
3877 (
3878 SemanticsRole::Unknown,
3879 None,
3880 (
3881 GeometryRect::from_origin_size(top_left, node.size),
3882 top_left,
3883 ),
3884 SemanticsPlacement::default(),
3885 )
3886 }
3887 Err(err) => return Err(err),
3888 }
3889 }
3890 Err(err) => return Err(err),
3891 };
3892
3893 let mut children = Vec::with_capacity(node.children.len());
3894 for child in &node.children {
3895 children.push(build_semantics_node_from_live_nodes(
3896 applier,
3897 &child.node.borrow(),
3898 Some(content),
3899 )?);
3900 }
3901
3902 Ok(SemanticsNode {
3903 placement,
3904 ..semantics_node_from_parts(
3905 node.node_id,
3906 applier.node_generation(node.node_id),
3907 role,
3908 config,
3909 children,
3910 None,
3911 bounds,
3912 )
3913 })
3914}
3915
3916fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
3917 stats.semantics_node_count += 1;
3918 stats.semantics_action_count += node.actions.len();
3919 stats.semantics_action_capacity += node.actions.capacity();
3920 stats.semantics_child_count += node.children.len();
3921 stats.semantics_child_capacity += node.children.capacity();
3922 stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
3923 stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
3924 if node.details.is_some() {
3925 stats.semantics_heap_bytes += size_of::<SemanticsDetails>();
3926 }
3927
3928 if let Some(description) = &node.description {
3929 stats.semantics_description_count += 1;
3930 stats.semantics_description_bytes += description.capacity();
3931 stats.semantics_heap_bytes += description.capacity();
3932 }
3933
3934 for child in &node.children {
3935 record_semantics_allocation_stats(child, stats);
3936 }
3937}
3938
3939fn record_layout_box_allocation_stats(
3940 layout_box: &LayoutBox,
3941 stats: &mut LayoutAllocationDebugStats,
3942) {
3943 stats.layout_box_count += 1;
3944 stats.layout_box_child_count += layout_box.children.len();
3945 stats.layout_box_child_capacity += layout_box.children.capacity();
3946 stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
3947 stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
3948
3949 for child in &layout_box.children {
3950 record_layout_box_allocation_stats(child, stats);
3951 }
3952}
3953
3954fn build_layout_tree(
3955 applier: &mut MemoryApplier,
3956 node: &MeasuredNode,
3957) -> Result<LayoutTree, NodeError> {
3958 fn place(
3959 applier: &mut MemoryApplier,
3960 node: &MeasuredNode,
3961 origin: Point,
3962 parent_transform: ProjectiveTransform,
3963 ) -> Result<LayoutBox, NodeError> {
3964 let top_left = Point {
3965 x: origin.x + node.offset.x,
3966 y: origin.y + node.offset.y,
3967 };
3968 let rect = GeometryRect {
3969 x: top_left.x,
3970 y: top_left.y,
3971 width: node.size.width,
3972 height: node.size.height,
3973 };
3974 let (data, window_transform) =
3975 snapshot_node_data(applier, node.node_id, top_left, node.size, parent_transform)?;
3976 let mut children = Vec::with_capacity(node.children.len());
3977 for child in &node.children {
3978 let child_node = child.node.borrow();
3979 if crate::modifier::is_window_root(applier, child_node.node_id) {
3980 continue;
3981 }
3982 let child_origin = Point {
3983 x: top_left.x + child.offset.x,
3984 y: top_left.y + child.offset.y,
3985 };
3986 children.push(place(applier, &child_node, child_origin, window_transform)?);
3987 }
3988 Ok(LayoutBox {
3989 node_generation: applier.node_generation(node.node_id),
3990 ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
3991 })
3992 }
3993
3994 Ok(LayoutTree::new(place(
3995 applier,
3996 node,
3997 Point { x: 0.0, y: 0.0 },
3998 ProjectiveTransform::identity(),
3999 )?))
4000}
4001
4002fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
4003 match &layout_box.node_data.kind {
4004 LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
4005 LayoutNodeKind::Spacer => SemanticsRole::Spacer,
4006 LayoutNodeKind::Unknown => SemanticsRole::Unknown,
4007 LayoutNodeKind::Button { .. } => SemanticsRole::Button,
4008 LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
4009 }
4010}
4011
4012fn build_semantics_node_from_layout_box(layout_box: &LayoutBox, origin: Point) -> SemanticsNode {
4013 let rect = layout_box.rect;
4014 let content = Point {
4015 x: rect.x + layout_box.content_offset.x,
4016 y: rect.y + layout_box.content_offset.y,
4017 };
4018 let children = layout_box
4019 .children
4020 .iter()
4021 .map(|child| build_semantics_node_from_layout_box(child, content))
4022 .collect();
4023
4024 SemanticsNode {
4025 placement: SemanticsPlacement {
4026 position: Point {
4027 x: rect.x - origin.x,
4028 y: rect.y - origin.y,
4029 },
4030 content_offset: layout_box.content_offset,
4031 },
4032 ..semantics_node_from_parts(
4033 layout_box.node_id,
4034 layout_box.node_generation,
4035 semantics_role_from_layout_box(layout_box),
4036 layout_box.node_data.semantics().cloned(),
4037 children,
4038 None,
4039 rect,
4040 )
4041 }
4042}
4043
4044fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
4045 match &info.role {
4046 SemanticsRole::Layout => LayoutNodeKind::Layout,
4047 SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
4048 SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
4049 SemanticsRole::Spacer => LayoutNodeKind::Spacer,
4050 SemanticsRole::Button => {
4051 let handler = info
4052 .button_handler
4053 .as_ref()
4054 .cloned()
4055 .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
4056 LayoutNodeKind::Button { on_click: handler }
4057 }
4058 SemanticsRole::Unknown => LayoutNodeKind::Unknown,
4059 }
4060}
4061
4062fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
4063 let horizontal = padding.horizontal_sum();
4064 let vertical = padding.vertical_sum();
4065 let min_width = (constraints.min_width - horizontal).max(0.0);
4066 let mut max_width = constraints.max_width;
4067 if max_width.is_finite() {
4068 max_width = (max_width - horizontal).max(0.0);
4069 }
4070 let min_height = (constraints.min_height - vertical).max(0.0);
4071 let mut max_height = constraints.max_height;
4072 if max_height.is_finite() {
4073 max_height = (max_height - vertical).max(0.0);
4074 }
4075 normalize_constraints(Constraints {
4076 min_width,
4077 max_width,
4078 min_height,
4079 max_height,
4080 })
4081}
4082
4083fn resolve_dimension(
4084 base: f32,
4085 explicit: DimensionConstraint,
4086 min_override: Option<f32>,
4087 max_override: Option<f32>,
4088 min_limit: f32,
4089 max_limit: f32,
4090) -> f32 {
4091 let mut min_bound = min_limit;
4092 if let Some(min_value) = min_override {
4093 min_bound = min_bound.max(min_value);
4094 }
4095
4096 let mut max_bound = if max_limit.is_finite() {
4097 max_limit
4098 } else {
4099 max_override.unwrap_or(max_limit)
4100 };
4101 if let Some(max_value) = max_override {
4102 if max_bound.is_finite() {
4103 max_bound = max_bound.min(max_value);
4104 } else {
4105 max_bound = max_value;
4106 }
4107 }
4108 if max_bound < min_bound {
4109 max_bound = min_bound;
4110 }
4111
4112 let mut size = match explicit {
4113 DimensionConstraint::Points(points) => points,
4114 DimensionConstraint::Fraction(fraction) => {
4115 if max_limit.is_finite() {
4116 max_limit * fraction.clamp(0.0, 1.0)
4117 } else {
4118 base
4119 }
4120 }
4121 DimensionConstraint::Unspecified => base,
4122 DimensionConstraint::Intrinsic(_) => base,
4123 };
4124
4125 size = clamp_dimension(size, min_bound, max_bound);
4126 size = clamp_dimension(size, min_limit, max_limit);
4127 size.max(0.0)
4128}
4129
4130fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
4131 let mut result = value.max(min);
4132 if max.is_finite() {
4133 result = result.min(max);
4134 }
4135 result
4136}
4137
4138fn normalize_constraints(mut constraints: Constraints) -> Constraints {
4139 if constraints.max_width < constraints.min_width {
4140 constraints.max_width = constraints.min_width;
4141 }
4142 if constraints.max_height < constraints.min_height {
4143 constraints.max_height = constraints.min_height;
4144 }
4145 constraints
4146}
4147
4148#[cfg(test)]
4149#[path = "tests/layout_tests.rs"]
4150mod tests;
4151
4152#[cfg(test)]
4153#[path = "tests/semantics_update_tests.rs"]
4154mod semantics_update_tests;
4155
4156#[cfg(test)]
4157#[path = "tests/coordinator_geometry_tests.rs"]
4158mod coordinator_geometry_tests;