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