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