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