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