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