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