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