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