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