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 parent_layer_translation: Point,
735 ) -> Result<Option<LayoutBox>, NodeError> {
736 let Some((state, child_ids)) = snapshot(applier, node_id)? else {
737 return Ok(None);
738 };
739 if !state.is_placed {
740 return Ok(None);
741 }
742
743 let top_left = Point {
744 x: parent_content_origin.x + state.position.x,
745 y: parent_content_origin.y + state.position.y,
746 };
747 let rect = GeometryRect {
748 x: top_left.x,
749 y: top_left.y,
750 width: state.size.width,
751 height: state.size.height,
752 };
753 let info = runtime_metadata_for(applier, node_id)?;
754 let kind = layout_kind_from_metadata(node_id, &info);
755 let RuntimeNodeMetadata {
756 modifier,
757 resolved_modifiers,
758 modifier_slices,
759 ..
760 } = info;
761
762 let layer_translation = match modifier_slices.graphics_layer() {
767 Some(layer) => Point {
768 x: parent_layer_translation.x + layer.translation_x,
769 y: parent_layer_translation.y + layer.translation_y,
770 },
771 None => parent_layer_translation,
772 };
773
774 if let Some(sink) = modifier_slices.text_field_window_origin() {
778 sink.set(Point {
779 x: top_left.x + layer_translation.x,
780 y: top_left.y + layer_translation.y,
781 });
782 }
783
784 let data = LayoutNodeData::new(modifier, resolved_modifiers, modifier_slices, kind);
785 let child_origin = Point {
786 x: top_left.x + state.content_offset.x,
787 y: top_left.y + state.content_offset.y,
788 };
789 let mut children = Vec::with_capacity(child_ids.len());
790 for child_id in child_ids {
791 if let Some(child) = place(applier, child_id, child_origin, layer_translation)? {
792 children.push(child);
793 }
794 }
795
796 Ok(Some(LayoutBox::new(
797 node_id,
798 rect,
799 state.content_offset,
800 data,
801 children,
802 )))
803 }
804
805 place(applier, root, Point::default(), Point::default()).map(|root| root.map(LayoutTree::new))
806}
807
808pub fn build_semantics_tree_from_applier(
814 applier: &mut MemoryApplier,
815 root: NodeId,
816) -> Result<Option<SemanticsTree>, NodeError> {
817 fn node(
818 applier: &mut MemoryApplier,
819 node_id: NodeId,
820 ) -> Result<Option<SemanticsNode>, NodeError> {
821 match applier.with_node::<LayoutNode, _>(node_id, |layout| {
822 let state = layout.layout_state();
823 if !state.is_placed {
824 return None;
825 }
826 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
827 let config = layout.semantics_configuration();
828 let children = layout.children.clone();
829 layout.clear_needs_semantics();
830 Some((role, config, children))
831 }) {
832 Ok(Some((role, config, child_ids))) => {
833 let mut children = Vec::with_capacity(child_ids.len());
834 for child_id in child_ids {
835 if let Some(child) = node(applier, child_id)? {
836 children.push(child);
837 }
838 }
839 return Ok(Some(semantics_node_from_parts(
840 node_id, role, config, children,
841 )));
842 }
843 Ok(None) => return Ok(None),
844 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {}
845 Err(err) => return Err(err),
846 }
847
848 match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |subcompose| {
849 let state = subcompose.layout_state();
850 if !state.is_placed {
851 return None;
852 }
853 let config = collect_semantics_from_modifier(&subcompose.modifier());
854 let children = subcompose.active_children();
855 subcompose.clear_needs_semantics();
856 Some((config, children))
857 }) {
858 Ok(Some((config, child_ids))) => {
859 let mut children = Vec::with_capacity(child_ids.len());
860 for child_id in child_ids {
861 if let Some(child) = node(applier, child_id)? {
862 children.push(child);
863 }
864 }
865 Ok(Some(semantics_node_from_parts(
866 node_id,
867 SemanticsRole::Subcompose,
868 config,
869 children,
870 )))
871 }
872 Ok(None) | Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {
873 Ok(None)
874 }
875 Err(err) => Err(err),
876 }
877 }
878
879 node(applier, root).map(|root| root.map(SemanticsTree::new))
880}
881
882#[derive(Clone, Copy, Debug, PartialEq, Eq)]
883pub struct MeasureLayoutOptions {
884 pub collect_semantics: bool,
885 pub build_layout_tree: bool,
886}
887
888impl Default for MeasureLayoutOptions {
889 fn default() -> Self {
890 Self {
891 collect_semantics: true,
892 build_layout_tree: true,
893 }
894 }
895}
896
897pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
905 Ok(applier.get_mut(root)?.needs_layout())
906}
907
908pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
914 Ok(applier.get_mut(root)?.needs_semantics())
915}
916
917#[cfg(test)]
919pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
920 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
921}
922
923pub fn measure_layout(
925 applier: &mut MemoryApplier,
926 root: NodeId,
927 max_size: Size,
928) -> Result<LayoutMeasurements, NodeError> {
929 measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
930}
931
932pub fn measure_layout_with_options(
933 applier: &mut MemoryApplier,
934 root: NodeId,
935 max_size: Size,
936 options: MeasureLayoutOptions,
937) -> Result<LayoutMeasurements, NodeError> {
938 let telemetry_start = Instant::now();
939 process_pending_layout_repasses(applier, root)?;
940 let after_repasses = Instant::now();
941
942 let constraints = Constraints {
943 min_width: 0.0,
944 max_width: max_size.width,
945 min_height: 0.0,
946 max_height: max_size.height,
947 };
948
949 let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
960 .with_node::<LayoutNode, _>(root, |node| {
961 (
962 node.needs_measure(), node.needs_semantics(),
964 node.cache_handles().epoch(),
965 )
966 }) {
967 Ok(tuple) => tuple,
968 Err(NodeError::TypeMismatch { .. }) => {
969 let node = applier.get_mut(root)?;
970 let measure_dirty = node.needs_measure();
974 let semantics_dirty = node.needs_semantics();
975 (measure_dirty, semantics_dirty, 0)
976 }
977 Err(err) => return Err(err),
978 };
979
980 let epoch = if needs_remeasure {
981 crate::render_state::next_layout_cache_epoch()
982 } else if cached_epoch != 0 {
983 cached_epoch
984 } else {
985 crate::render_state::current_layout_cache_epoch()
987 };
988
989 let guard = ApplierSlotGuard::new(applier);
997 let applier_host = guard.host();
998 let slots_handle = guard.slots_handle();
999 let after_guard = Instant::now();
1000
1001 let frame_arena = crate::render_state::take_layout_frame_arena();
1004 let mut builder = LayoutBuilder::new_with_epoch(
1005 Rc::clone(&applier_host),
1006 epoch,
1007 Rc::clone(&slots_handle),
1008 frame_arena,
1009 );
1010 let after_builder = Instant::now();
1011
1012 let measured = builder.measure_node(root, normalize_constraints(constraints))?;
1017 let after_measure = Instant::now();
1018
1019 if let Ok(mut applier) = applier_host.try_borrow_typed() {
1023 if applier
1024 .with_node::<LayoutNode, _>(root, |node| {
1025 node.set_position(Point::default());
1026 })
1027 .is_err()
1028 {
1029 let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
1030 node.set_position(Point::default());
1031 });
1032 }
1033 }
1034 let after_root_place = Instant::now();
1035
1036 let (layout_tree, semantics) = {
1037 let mut applier_ref = applier_host.borrow_typed();
1038 let layout_tree = if options.build_layout_tree {
1039 Some(build_layout_tree(&mut applier_ref, &measured)?)
1040 } else {
1041 None
1042 };
1043 let semantics = if options.collect_semantics {
1044 let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
1045 clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
1046 build_semantics_tree_from_layout_tree(layout_tree)
1047 } else {
1048 build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
1049 };
1050 Some(semantics_tree)
1051 } else {
1052 None
1053 };
1054 (layout_tree, semantics)
1055 };
1056 let after_aux = Instant::now();
1057
1058 drop(builder);
1061 let after_builder_drop = Instant::now();
1062
1063 drop(guard);
1066 let after_guard_drop = Instant::now();
1067
1068 log_layout_measure_telemetry(LayoutMeasureTelemetry {
1069 root,
1070 start: telemetry_start,
1071 after_repasses,
1072 after_guard,
1073 after_builder,
1074 after_measure,
1075 after_root_place,
1076 after_aux,
1077 after_builder_drop,
1078 after_guard_drop,
1079 });
1080
1081 Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
1082}
1083
1084fn process_pending_layout_repasses(
1085 applier: &mut MemoryApplier,
1086 root: NodeId,
1087) -> Result<(), NodeError> {
1088 for node_id in crate::render_state::take_modifier_slice_repass_nodes() {
1089 if let Ok(node) = applier.get_mut(node_id) {
1090 let any = node.as_any_mut();
1091 if let Some(layout) = any.downcast_mut::<crate::widgets::nodes::LayoutNode>() {
1092 layout.mark_modifier_slices_dirty();
1093 } else if let Some(subcompose) =
1094 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1095 {
1096 subcompose.mark_modifier_slices_dirty();
1097 }
1098 }
1099 }
1100 let measure_repass_nodes = crate::take_measure_repass_nodes();
1103 let repass_nodes = crate::take_layout_repass_nodes();
1104 if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
1105 return Ok(());
1106 }
1107 for node_id in measure_repass_nodes {
1108 cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
1109 }
1110 for node_id in repass_nodes {
1111 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1112 }
1113 applier.get_mut(root)?.mark_needs_layout();
1114 Ok(())
1115}
1116
1117struct LayoutBuilder {
1118 state: Rc<RefCell<LayoutBuilderState>>,
1119}
1120
1121impl LayoutBuilder {
1122 fn new_with_epoch(
1123 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1124 epoch: u64,
1125 slots: Rc<RefCell<SlotTable>>,
1126 frame_arena: FrameLayoutArena,
1127 ) -> Self {
1128 Self {
1129 state: Rc::new(RefCell::new(LayoutBuilderState::new_with_epoch(
1130 applier,
1131 epoch,
1132 slots,
1133 frame_arena,
1134 ))),
1135 }
1136 }
1137
1138 fn measure_node(
1139 &mut self,
1140 node_id: NodeId,
1141 constraints: Constraints,
1142 ) -> Result<Rc<MeasuredNode>, NodeError> {
1143 LayoutBuilderState::measure_node(Rc::clone(&self.state), node_id, constraints)
1144 }
1145
1146 fn set_runtime_handle(&mut self, handle: Option<RuntimeHandle>) {
1147 self.state.borrow_mut().runtime_handle = handle;
1148 }
1149}
1150
1151impl Drop for LayoutBuilder {
1152 fn drop(&mut self) {
1153 if Rc::strong_count(&self.state) != 1 {
1154 return;
1155 }
1156 let Ok(mut state) = self.state.try_borrow_mut() else {
1157 return;
1158 };
1159 crate::render_state::replace_layout_frame_arena(std::mem::take(&mut state.frame_arena));
1160 }
1161}
1162
1163struct LayoutBuilderState {
1164 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1165 runtime_handle: Option<RuntimeHandle>,
1166 slots: Rc<RefCell<SlotTable>>,
1169 cache_epoch: u64,
1170 frame_arena: FrameLayoutArena,
1171}
1172
1173struct LayoutRuntimeFrameBindingCleanup {
1174 state: Rc<RefCell<LayoutRuntimeState>>,
1175}
1176
1177impl LayoutRuntimeFrameBindingCleanup {
1178 fn new(state: Rc<RefCell<LayoutRuntimeState>>) -> Self {
1179 Self { state }
1180 }
1181}
1182
1183impl Drop for LayoutRuntimeFrameBindingCleanup {
1184 fn drop(&mut self) {
1185 self.state.borrow().clear_frame_bindings();
1186 }
1187}
1188
1189impl LayoutBuilderState {
1190 fn new_with_epoch(
1191 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1192 epoch: u64,
1193 slots: Rc<RefCell<SlotTable>>,
1194 frame_arena: FrameLayoutArena,
1195 ) -> Self {
1196 let runtime_handle = applier.borrow_typed().runtime_handle();
1197
1198 Self {
1199 applier,
1200 runtime_handle,
1201 slots,
1202 cache_epoch: epoch,
1203 frame_arena,
1204 }
1205 }
1206
1207 fn try_with_applier_result<R>(
1208 state_rc: &Rc<RefCell<Self>>,
1209 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1210 ) -> Option<Result<R, NodeError>> {
1211 let host = {
1212 let state = state_rc.borrow();
1213 Rc::clone(&state.applier)
1214 };
1215
1216 let Ok(mut applier) = host.try_borrow_typed() else {
1218 return None;
1219 };
1220
1221 Some(f(&mut applier))
1222 }
1223
1224 fn with_applier_result<R>(
1225 state_rc: &Rc<RefCell<Self>>,
1226 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1227 ) -> Result<R, NodeError> {
1228 Self::try_with_applier_result(state_rc, f).unwrap_or_else(|| {
1229 Err(NodeError::MissingContext {
1230 id: NodeId::default(),
1231 reason: "applier already borrowed",
1232 })
1233 })
1234 }
1235
1236 fn clear_node_placed(state_rc: &Rc<RefCell<Self>>, node_id: NodeId) {
1239 let host = {
1240 let state = state_rc.borrow();
1241 Rc::clone(&state.applier)
1242 };
1243 let Ok(mut applier) = host.try_borrow_typed() else {
1244 return;
1245 };
1246 if applier
1248 .with_node::<LayoutNode, _>(node_id, |node| {
1249 node.clear_placed();
1250 })
1251 .is_err()
1252 {
1253 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1254 node.clear_placed();
1255 });
1256 }
1257 }
1258
1259 fn measure_node(
1260 state_rc: Rc<RefCell<Self>>,
1261 node_id: NodeId,
1262 constraints: Constraints,
1263 ) -> Result<Rc<MeasuredNode>, NodeError> {
1264 let telemetry_start = Instant::now();
1265 Self::clear_node_placed(&state_rc, node_id);
1269
1270 if let Some(subcompose) =
1272 Self::try_measure_subcompose(Rc::clone(&state_rc), node_id, constraints)?
1273 {
1274 log_node_measure_telemetry(
1275 "subcompose",
1276 node_id,
1277 constraints,
1278 subcompose.size,
1279 subcompose.children.len(),
1280 telemetry_start,
1281 );
1282 return Ok(subcompose);
1283 }
1284
1285 if let Some(result) = Self::try_with_applier_result(&state_rc, |applier| {
1287 match applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1288 LayoutNodeSnapshot::from_layout_node(layout_node)
1289 }) {
1290 Ok(snapshot) => Ok(Some(snapshot)),
1291 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => Ok(None),
1292 Err(err) => Err(err),
1293 }
1294 }) {
1295 if let Some(snapshot) = result? {
1297 let measured = Self::measure_layout_node(
1298 Rc::clone(&state_rc),
1299 node_id,
1300 snapshot,
1301 constraints,
1302 )?;
1303 log_node_measure_telemetry(
1304 "layout",
1305 node_id,
1306 constraints,
1307 measured.size,
1308 measured.children.len(),
1309 telemetry_start,
1310 );
1311 return Ok(measured);
1312 }
1313 }
1314 let measured = Rc::new(MeasuredNode::new(
1319 node_id,
1320 Size::default(),
1321 Point { x: 0.0, y: 0.0 },
1322 Point::default(), Vec::new(),
1324 ));
1325 log_node_measure_telemetry(
1326 "fallback",
1327 node_id,
1328 constraints,
1329 measured.size,
1330 measured.children.len(),
1331 telemetry_start,
1332 );
1333 Ok(measured)
1334 }
1335
1336 fn cached_measure_node_with_applier(
1337 applier: &mut MemoryApplier,
1338 node_id: NodeId,
1339 constraints: Constraints,
1340 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1341 let Some(data) = Self::layout_child_measure_data(applier, node_id)? else {
1342 return Ok(None);
1343 };
1344 if data.needs_measure || data.cache.epoch() == 0 {
1345 return Ok(None);
1346 }
1347
1348 let Some(measured) = data.cache.get_measurement(constraints) else {
1349 return Ok(None);
1350 };
1351
1352 if let Some(layout_state) = data.layout_state {
1353 let mut layout_state = layout_state.borrow_mut();
1354 layout_state.size = measured.size;
1355 layout_state.measurement_constraints = constraints;
1356 drop(layout_state);
1357 let _ = applier.with_node::<LayoutNode, _>(node_id, |node| {
1358 if data.needs_layout {
1359 node.clear_needs_layout();
1360 }
1361 });
1362 } else {
1363 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1364 node.set_measured_size(measured.size);
1365 if data.needs_layout {
1366 node.clear_needs_layout();
1367 }
1368 });
1369 }
1370
1371 Ok(Some(measured))
1372 }
1373
1374 fn try_measure_subcompose(
1375 state_rc: Rc<RefCell<Self>>,
1376 node_id: NodeId,
1377 constraints: Constraints,
1378 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1379 let applier_host = {
1380 let state = state_rc.borrow();
1381 Rc::clone(&state.applier)
1382 };
1383
1384 let (node_handle, resolved_modifiers) = {
1385 let Ok(mut applier) = applier_host.try_borrow_typed() else {
1387 return Ok(None);
1388 };
1389 let node = match applier.get_mut(node_id) {
1390 Ok(node) => node,
1391 Err(NodeError::Missing { .. }) => return Ok(None),
1392 Err(err) => return Err(err),
1393 };
1394 let any = node.as_any_mut();
1395 if let Some(subcompose) =
1396 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1397 {
1398 let handle = subcompose.handle();
1399 let resolved_modifiers = handle.resolved_modifiers();
1400 (handle, resolved_modifiers)
1401 } else {
1402 return Ok(None);
1403 }
1404 };
1405
1406 let runtime_handle = {
1407 let mut state = state_rc.borrow_mut();
1408 if state.runtime_handle.is_none() {
1409 if let Ok(applier) = applier_host.try_borrow_typed() {
1411 state.runtime_handle = applier.runtime_handle();
1412 }
1413 }
1414 state
1415 .runtime_handle
1416 .clone()
1417 .ok_or(NodeError::MissingContext {
1418 id: node_id,
1419 reason: "runtime handle required for subcomposition",
1420 })?
1421 };
1422
1423 let props = resolved_modifiers.layout_properties();
1424 let padding = resolved_modifiers.padding();
1425 let offset = resolved_modifiers.offset();
1426 let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1427
1428 if let DimensionConstraint::Points(width) = props.width() {
1429 let constrained_width = width - padding.horizontal_sum();
1430 inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1431 inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1432 }
1433 if let DimensionConstraint::Points(height) = props.height() {
1434 let constrained_height = height - padding.vertical_sum();
1435 inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1436 inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1437 }
1438
1439 let mut slots_guard = SlotsGuard::take(Rc::clone(&state_rc));
1440 let slots_host = slots_guard.host();
1441 let applier_host_dyn: Rc<dyn ApplierHost> = applier_host.clone();
1442 let observer = SnapshotStateObserver::new(|callback| callback());
1443 let composer = Composer::new(
1444 Rc::clone(&slots_host),
1445 applier_host_dyn,
1446 runtime_handle.clone(),
1447 observer,
1448 Some(node_id),
1449 );
1450 composer.enter_phase(Phase::Measure);
1451
1452 let state_rc_clone = Rc::clone(&state_rc);
1453 let measure_error = RefCell::new(None);
1454 let state_rc_for_subcompose = Rc::clone(&state_rc_clone);
1455 let error_for_subcompose = &measure_error;
1456 let measured_children = node_handle.measured_children_scratch();
1457 let measured_children_for_subcompose = Rc::clone(&measured_children);
1458 let state_rc_for_cached = Rc::clone(&state_rc_clone);
1459 let error_for_cached = &measure_error;
1460 let measured_children_for_cached = Rc::clone(&measured_children);
1461 let measured_children_for_lookup = Rc::clone(&measured_children);
1462 let measured_children_for_retained = Rc::clone(&measured_children);
1463
1464 let measure_result = node_handle.measure_with_cached_batch(
1465 &composer,
1466 node_id,
1467 inner_constraints,
1468 CachedBatchMeasureInputs {
1469 measurer: Box::new(
1470 move |child_id: NodeId, child_constraints: Constraints| -> Size {
1471 match Self::measure_node(
1472 Rc::clone(&state_rc_for_subcompose),
1473 child_id,
1474 child_constraints,
1475 ) {
1476 Ok(measured) => {
1477 measured_children_for_subcompose
1478 .borrow_mut()
1479 .insert(child_id, Rc::clone(&measured));
1480 measured.size
1481 }
1482 Err(err) => {
1483 let mut slot = error_for_subcompose.borrow_mut();
1484 if slot.is_none() {
1485 *slot = Some(err);
1486 }
1487 Size::default()
1488 }
1489 }
1490 },
1491 ),
1492 cached_measure_batch_registrar: Box::new(
1493 move |child_ids: &[NodeId],
1494 child_constraints: Constraints,
1495 out: &mut Vec<Option<Size>>| {
1496 out.clear();
1497 out.resize(child_ids.len(), None);
1498
1499 let applier_host = {
1500 let state = state_rc_for_cached.borrow();
1501 Rc::clone(&state.applier)
1502 };
1503 let Ok(mut applier) = applier_host.try_borrow_typed() else {
1504 return;
1505 };
1506
1507 let mut measured_children = measured_children_for_cached.borrow_mut();
1508 for (index, &child_id) in child_ids.iter().enumerate() {
1509 match Self::cached_measure_node_with_applier(
1510 &mut applier,
1511 child_id,
1512 child_constraints,
1513 ) {
1514 Ok(Some(measured)) => {
1515 out[index] = Some(measured.size);
1516 measured_children.insert(child_id, Rc::clone(&measured));
1517 }
1518 Ok(None) => {}
1519 Err(err) => {
1520 let mut slot = error_for_cached.borrow_mut();
1521 if slot.is_none() {
1522 *slot = Some(err);
1523 }
1524 break;
1525 }
1526 }
1527 }
1528 },
1529 ),
1530 retained_measure_lookup: Box::new(move |child_id| {
1531 measured_children_for_lookup
1532 .borrow()
1533 .get(&child_id)
1534 .cloned()
1535 }),
1536 retained_measure_registrar: Box::new(move |measurements| {
1537 let mut measured_children = measured_children_for_retained.borrow_mut();
1538 for measured in measurements {
1539 measured_children.insert(measured.node_id(), Rc::clone(measured));
1540 }
1541 }),
1542 error: &measure_error,
1543 },
1544 )?;
1545 drop(composer);
1546 slots_guard.restore(slots_host.into_table()?);
1547
1548 if let Some(err) = measure_error.borrow_mut().take() {
1549 return Err(err);
1550 }
1551
1552 let cranpose_ui_layout::MeasureResult {
1556 size: measured_size,
1557 placements,
1558 } = measure_result;
1559
1560 let mut width = measured_size.width + padding.horizontal_sum();
1561 let mut height = measured_size.height + padding.vertical_sum();
1562
1563 width = resolve_dimension(
1564 width,
1565 props.width(),
1566 props.min_width(),
1567 props.max_width(),
1568 constraints.min_width,
1569 constraints.max_width,
1570 );
1571 height = resolve_dimension(
1572 height,
1573 props.height(),
1574 props.min_height(),
1575 props.max_height(),
1576 constraints.min_height,
1577 constraints.max_height,
1578 );
1579
1580 let mut children = Vec::with_capacity(placements.len());
1581 let mut measured_children_by_id = measured_children.borrow_mut();
1582
1583 if let Ok(mut applier) = applier_host.try_borrow_typed() {
1585 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1586 parent_node.set_measured_size(Size { width, height });
1587 parent_node.clear_needs_measure();
1588 parent_node.clear_needs_layout();
1589 });
1590 }
1591
1592 for placement in &placements {
1593 let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1594 measured
1595 } else {
1596 Self::measure_node(Rc::clone(&state_rc), placement.node_id, inner_constraints)?
1602 };
1603 let position = Point {
1604 x: padding.left + placement.x,
1605 y: padding.top + placement.y,
1606 };
1607
1608 if let Ok(mut applier) = applier_host.try_borrow_typed() {
1616 if applier
1617 .with_node::<LayoutNode, _>(placement.node_id, |node| {
1618 node.set_position(position);
1619 })
1620 .is_err()
1621 {
1622 let _ =
1623 applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1624 node.set_position(position);
1625 });
1626 }
1627 }
1628
1629 children.push(MeasuredChild {
1630 node: child,
1631 offset: position,
1632 });
1633 }
1634
1635 node_handle.set_active_children(children.iter().map(|c| c.node.node_id));
1637 node_handle.recycle_placement_scratch(placements);
1638
1639 Ok(Some(Rc::new(MeasuredNode::new(
1640 node_id,
1641 Size { width, height },
1642 offset,
1643 Point::default(), children,
1645 ))))
1646 }
1647 fn measure_through_modifier_chain(
1654 state_rc: &Rc<RefCell<Self>>,
1655 node_id: NodeId,
1656 runtime_state: &mut LayoutRuntimeState,
1657 measure_policy: &Rc<dyn MeasurePolicy>,
1658 constraints: Constraints,
1659 layout_node_data: &mut Vec<LayoutModifierNodeData>,
1660 placements: &mut Vec<Placement>,
1661 ) -> ModifierChainMeasurement {
1662 use cranpose_foundation::NodeCapabilities;
1663
1664 layout_node_data.clear();
1666 let mut offset = Point::default();
1667
1668 {
1669 let state = state_rc.borrow();
1670 let mut applier = state.applier.borrow_typed();
1671
1672 let _ = applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1673 let chain_handle = layout_node.modifier_chain();
1674
1675 if !chain_handle.has_layout_nodes() {
1676 return;
1677 }
1678
1679 chain_handle.chain().for_each_forward_matching(
1681 NodeCapabilities::LAYOUT,
1682 |node_ref| {
1683 if let Some(index) = node_ref.entry_index() {
1684 if let Some(node_rc) = chain_handle.chain().get_node_rc(index) {
1686 layout_node_data.push((index, Rc::clone(&node_rc)));
1687 }
1688
1689 node_ref.with_node(|node| {
1693 if let Some(offset_node) =
1694 node.as_any()
1695 .downcast_ref::<crate::modifier_nodes::OffsetNode>()
1696 {
1697 let delta = offset_node.offset();
1698 offset.x += delta.x;
1699 offset.y += delta.y;
1700 }
1701 });
1702 }
1703 },
1704 );
1705 });
1706 }
1707
1708 if layout_node_data.is_empty() {
1711 let final_size = measure_policy.measure_into(
1712 runtime_state.child_measurables(),
1713 constraints,
1714 placements,
1715 );
1716
1717 return ModifierChainMeasurement {
1718 size: final_size,
1719 content_offset: Point::default(),
1720 offset,
1721 };
1722 }
1723
1724 runtime_state.reconcile_coordinator_chain(layout_node_data.as_slice());
1725 let frame = CoordinatorFrame::new(
1726 measure_policy,
1727 runtime_state.child_measurables(),
1728 placements,
1729 );
1730
1731 let placeable = runtime_state
1733 .coordinator_chain()
1734 .measure_from(0, &frame, constraints);
1735 let final_size = Size {
1736 width: placeable.width(),
1737 height: placeable.height(),
1738 };
1739
1740 let content_offset = placeable.content_offset();
1742 let all_placement_offset = Point {
1743 x: content_offset.0,
1744 y: content_offset.1,
1745 };
1746
1747 let content_offset = Point {
1751 x: all_placement_offset.x - offset.x,
1752 y: all_placement_offset.y - offset.y,
1753 };
1754
1755 let invalidations = frame.take_invalidations();
1759 if !invalidations.is_empty() {
1760 Self::with_applier_result(state_rc, |applier| {
1762 applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1763 for kind in invalidations {
1764 match kind {
1765 InvalidationKind::Layout => layout_node.mark_needs_measure(),
1766 InvalidationKind::Draw => layout_node.mark_needs_redraw(),
1767 InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
1768 InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
1769 InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
1770 }
1771 }
1772 })
1773 })
1774 .ok();
1775 }
1776
1777 ModifierChainMeasurement {
1778 size: final_size,
1779 content_offset,
1780 offset,
1781 }
1782 }
1783
1784 fn layout_child_measure_data(
1785 applier: &mut MemoryApplier,
1786 child_id: NodeId,
1787 ) -> Result<Option<LayoutChildMeasureData>, NodeError> {
1788 match applier.with_node::<LayoutNode, _>(child_id, |n| LayoutChildMeasureData {
1789 cache: n.cache_handles(),
1790 layout_state: Some(n.layout_state_handle()),
1791 needs_layout: n.needs_layout(),
1792 needs_measure: n.needs_measure(),
1793 }) {
1794 Ok(data) => Ok(Some(data)),
1795 Err(NodeError::TypeMismatch { .. }) => {
1796 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |n| {
1797 LayoutChildMeasureData {
1798 cache: n.cache_handles(),
1799 layout_state: None,
1800 needs_layout: n.needs_layout(),
1801 needs_measure: n.needs_measure(),
1802 }
1803 }) {
1804 Ok(data) => Ok(Some(data)),
1805 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {
1806 Ok(None)
1807 }
1808 Err(err) => Err(err),
1809 }
1810 }
1811 Err(NodeError::Missing { .. }) => Ok(None),
1812 Err(err) => Err(err),
1813 }
1814 }
1815
1816 fn measure_layout_node(
1817 state_rc: Rc<RefCell<Self>>,
1818 node_id: NodeId,
1819 snapshot: LayoutNodeSnapshot,
1820 constraints: Constraints,
1821 ) -> Result<Rc<MeasuredNode>, NodeError> {
1822 let cache_epoch = {
1823 let state = state_rc.borrow();
1824 state.cache_epoch
1825 };
1826 let LayoutNodeSnapshot {
1827 measure_policy,
1828 cache,
1829 layout_runtime_state,
1830 needs_layout,
1831 needs_measure,
1832 } = snapshot;
1833 cache.activate(cache_epoch);
1834
1835 if needs_measure {
1836 }
1838
1839 if !needs_measure && !needs_layout {
1843 if let Some(cached) = cache.get_measurement(constraints) {
1845 Self::with_applier_result(&state_rc, |applier| {
1847 applier.with_node::<LayoutNode, _>(node_id, |node| {
1848 node.clear_needs_measure();
1849 node.clear_needs_layout();
1850 })
1851 })
1852 .ok();
1853 return Ok(cached);
1854 }
1855 }
1856
1857 let (runtime_handle, applier_host) = {
1858 let state = state_rc.borrow();
1859 (state.runtime_handle.clone(), Rc::clone(&state.applier))
1860 };
1861
1862 let measure_handle = LayoutMeasureHandle::new(Rc::clone(&state_rc));
1863 let error = Rc::new(RefCell::new(None));
1864 let mut pools = VecPools::acquire(Rc::clone(&state_rc));
1865 let (records, child_ids, layout_node_data, placements) = pools.parts();
1866
1867 applier_host
1868 .borrow_typed()
1869 .with_node::<LayoutNode, _>(node_id, |node| {
1870 child_ids.extend_from_slice(&node.children);
1871 })?;
1872
1873 let mut valid_child_count = 0;
1874 for index in 0..child_ids.len() {
1875 let child_id = child_ids[index];
1876 let child_exists = {
1877 let mut applier = applier_host.borrow_typed();
1878 Self::layout_child_measure_data(&mut applier, child_id)?.is_some()
1879 };
1880 if child_exists {
1881 child_ids[valid_child_count] = child_id;
1882 valid_child_count += 1;
1883 }
1884 }
1885 child_ids.truncate(valid_child_count);
1886
1887 let _frame_binding_cleanup =
1888 LayoutRuntimeFrameBindingCleanup::new(Rc::clone(&layout_runtime_state));
1889
1890 {
1891 let mut runtime_state = layout_runtime_state.borrow_mut();
1892 runtime_state.reconcile_child_measurables(child_ids.as_slice());
1893
1894 for (index, &child_id) in child_ids.iter().enumerate() {
1895 let data = {
1896 let mut applier = applier_host.borrow_typed();
1897 Self::layout_child_measure_data(&mut applier, child_id)?
1898 };
1899 let Some(data) = data else {
1900 continue;
1901 };
1902
1903 let child_is_dirty = data.needs_layout || data.needs_measure;
1904 let child_cache_epoch = if child_is_dirty {
1905 cache_epoch
1906 } else {
1907 data.cache.epoch()
1908 };
1909 let child_state = runtime_state.child_state(index);
1910 child_state.configure(LayoutChildMeasureConfig {
1911 applier: Rc::clone(&applier_host),
1912 node_id: child_id,
1913 error: Rc::clone(&error),
1914 runtime_handle: runtime_handle.clone(),
1915 cache: data.cache,
1916 cache_epoch: child_cache_epoch,
1917 force_remeasure: child_is_dirty,
1918 measure_handle: Some(measure_handle.clone()),
1919 layout_state: data.layout_state,
1920 });
1921 records.push((child_id, ChildRecord { state: child_state }));
1922 }
1923 }
1924
1925 let chain_constraints = constraints;
1926
1927 let modifier_chain_result = {
1928 let mut runtime_state = layout_runtime_state.borrow_mut();
1929 Self::measure_through_modifier_chain(
1930 &state_rc,
1931 node_id,
1932 &mut runtime_state,
1933 &measure_policy,
1934 chain_constraints,
1935 layout_node_data,
1936 placements,
1937 )
1938 };
1939
1940 let (width, height, content_offset, offset) = {
1942 let result = modifier_chain_result;
1943 if let Some(err) = error.borrow_mut().take() {
1946 return Err(err);
1947 }
1948
1949 (
1950 result.size.width,
1951 result.size.height,
1952 result.content_offset,
1953 result.offset,
1954 )
1955 };
1956
1957 let mut measured_children = Vec::with_capacity(records.len());
1958 for (child_id, record) in records.iter() {
1959 if let Some(measured) = record.state.take_measured() {
1960 let base_position = placements
1961 .iter()
1962 .find(|placement| placement.node_id == *child_id)
1963 .map(|placement| Point {
1964 x: placement.x,
1965 y: placement.y,
1966 })
1967 .or_else(|| record.state.last_position())
1968 .unwrap_or(Point { x: 0.0, y: 0.0 });
1969 let position = Point {
1971 x: content_offset.x + base_position.x,
1972 y: content_offset.y + base_position.y,
1973 };
1974 measured_children.push(MeasuredChild {
1975 node: measured,
1976 offset: position,
1977 });
1978 }
1979 }
1980
1981 let measured = Rc::new(MeasuredNode::new(
1982 node_id,
1983 Size { width, height },
1984 offset,
1985 content_offset,
1986 measured_children,
1987 ));
1988
1989 cache.store_measurement(constraints, Rc::clone(&measured));
1990
1991 Self::with_applier_result(&state_rc, |applier| {
1993 applier.with_node::<LayoutNode, _>(node_id, |node| {
1994 node.clear_needs_measure();
1995 node.clear_needs_layout();
1996 node.set_measured_size(Size { width, height });
1997 node.set_content_offset(content_offset);
1998 })
1999 })
2000 .ok();
2001
2002 Ok(measured)
2003 }
2004}
2005
2006struct LayoutChildMeasureData {
2007 cache: LayoutNodeCacheHandles,
2008 layout_state: Option<Rc<RefCell<LayoutState>>>,
2009 needs_layout: bool,
2010 needs_measure: bool,
2011}
2012
2013struct LayoutNodeSnapshot {
2020 measure_policy: Rc<dyn MeasurePolicy>,
2021 cache: LayoutNodeCacheHandles,
2022 layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
2023 needs_layout: bool,
2024 needs_measure: bool,
2026}
2027
2028impl LayoutNodeSnapshot {
2029 fn from_layout_node(node: &LayoutNode) -> Self {
2030 Self {
2031 measure_policy: Rc::clone(&node.measure_policy),
2032 cache: node.cache_handles(),
2033 layout_runtime_state: node.layout_runtime_state_handle(),
2034 needs_layout: node.needs_layout(),
2035 needs_measure: node.needs_measure(),
2036 }
2037 }
2038}
2039
2040struct VecPools {
2042 state: Rc<RefCell<LayoutBuilderState>>,
2043 records: Vec<(NodeId, ChildRecord)>,
2044 child_ids: Vec<NodeId>,
2045 layout_node_data: Vec<LayoutModifierNodeData>,
2046 placements: Vec<Placement>,
2047}
2048
2049impl VecPools {
2050 fn acquire(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2051 let (records, child_ids, layout_node_data, placements) = {
2052 let mut state_mut = state.borrow_mut();
2053 (
2054 state_mut.frame_arena.tmp_records.acquire(),
2055 state_mut.frame_arena.tmp_child_ids.acquire(),
2056 state_mut.frame_arena.tmp_layout_node_data.acquire(),
2057 state_mut.frame_arena.tmp_placements.acquire(),
2058 )
2059 };
2060 Self {
2061 state,
2062 records,
2063 child_ids,
2064 layout_node_data,
2065 placements,
2066 }
2067 }
2068
2069 #[allow(clippy::type_complexity)] fn parts(
2071 &mut self,
2072 ) -> (
2073 &mut Vec<(NodeId, ChildRecord)>,
2074 &mut Vec<NodeId>,
2075 &mut Vec<LayoutModifierNodeData>,
2076 &mut Vec<Placement>,
2077 ) {
2078 (
2079 &mut self.records,
2080 &mut self.child_ids,
2081 &mut self.layout_node_data,
2082 &mut self.placements,
2083 )
2084 }
2085}
2086
2087impl Drop for VecPools {
2088 fn drop(&mut self) {
2089 let mut state = self.state.borrow_mut();
2090 state
2091 .frame_arena
2092 .tmp_records
2093 .release(std::mem::take(&mut self.records));
2094 state
2095 .frame_arena
2096 .tmp_child_ids
2097 .release(std::mem::take(&mut self.child_ids));
2098 state
2099 .frame_arena
2100 .tmp_layout_node_data
2101 .release(std::mem::take(&mut self.layout_node_data));
2102 state
2103 .frame_arena
2104 .tmp_placements
2105 .release(std::mem::take(&mut self.placements));
2106 }
2107}
2108
2109struct SlotsGuard {
2110 state: Rc<RefCell<LayoutBuilderState>>,
2111 slots: Option<SlotTable>,
2112}
2113
2114impl SlotsGuard {
2115 fn take(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2116 let slots = {
2117 let state_ref = state.borrow();
2118 let mut slots_ref = state_ref.slots.borrow_mut();
2119 std::mem::take(&mut *slots_ref)
2120 };
2121 Self {
2122 state,
2123 slots: Some(slots),
2124 }
2125 }
2126
2127 fn host(&mut self) -> Rc<SlotsHost> {
2128 let slots = self.slots.take().unwrap_or_default();
2129 Rc::new(SlotsHost::new(slots))
2130 }
2131
2132 fn restore(&mut self, slots: SlotTable) {
2133 debug_assert!(self.slots.is_none());
2134 self.slots = Some(slots);
2135 }
2136}
2137
2138impl Drop for SlotsGuard {
2139 fn drop(&mut self) {
2140 if let Some(slots) = self.slots.take() {
2141 let state_ref = self.state.borrow();
2142 *state_ref.slots.borrow_mut() = slots;
2143 }
2144 }
2145}
2146
2147#[derive(Clone)]
2148struct LayoutMeasureHandle {
2149 state: Rc<RefCell<LayoutBuilderState>>,
2150}
2151
2152impl LayoutMeasureHandle {
2153 fn new(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2154 Self { state }
2155 }
2156
2157 fn measure(
2158 &self,
2159 node_id: NodeId,
2160 constraints: Constraints,
2161 ) -> Result<Rc<MeasuredNode>, NodeError> {
2162 LayoutBuilderState::measure_node(Rc::clone(&self.state), node_id, constraints)
2163 }
2164}
2165
2166#[derive(Debug, Clone)]
2167pub(crate) struct MeasuredNode {
2168 node_id: NodeId,
2169 size: Size,
2170 offset: Point,
2172 content_offset: Point,
2174 children: Vec<MeasuredChild>,
2175}
2176
2177impl MeasuredNode {
2178 fn new(
2179 node_id: NodeId,
2180 size: Size,
2181 offset: Point,
2182 content_offset: Point,
2183 children: Vec<MeasuredChild>,
2184 ) -> Self {
2185 Self {
2186 node_id,
2187 size,
2188 offset,
2189 content_offset,
2190 children,
2191 }
2192 }
2193
2194 #[cfg(test)]
2195 pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2196 Self::new(
2197 node_id,
2198 size,
2199 Point::default(),
2200 Point::default(),
2201 Vec::new(),
2202 )
2203 }
2204
2205 pub(crate) fn node_id(&self) -> NodeId {
2206 self.node_id
2207 }
2208
2209 pub(crate) fn size(&self) -> Size {
2210 self.size
2211 }
2212}
2213
2214#[derive(Debug, Clone)]
2215struct MeasuredChild {
2216 node: Rc<MeasuredNode>,
2217 offset: Point,
2218}
2219
2220struct ChildRecord {
2221 state: Rc<LayoutChildMeasureState>,
2222}
2223
2224struct CoordinatorFrame<'a> {
2225 measure_policy: &'a Rc<dyn MeasurePolicy>,
2226 measurables: &'a [Box<dyn Measurable>],
2227 placements: RefCell<&'a mut Vec<Placement>>,
2228 context: RefCell<LayoutNodeContext>,
2229}
2230
2231impl<'a> CoordinatorFrame<'a> {
2232 fn new(
2233 measure_policy: &'a Rc<dyn MeasurePolicy>,
2234 measurables: &'a [Box<dyn Measurable>],
2235 placements: &'a mut Vec<Placement>,
2236 ) -> Self {
2237 Self {
2238 measure_policy,
2239 measurables,
2240 placements: RefCell::new(placements),
2241 context: RefCell::new(LayoutNodeContext::new()),
2242 }
2243 }
2244
2245 fn take_invalidations(&self) -> Vec<InvalidationKind> {
2246 self.context.borrow_mut().take_invalidations()
2247 }
2248}
2249
2250struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2251 chain: &'chain CoordinatorChain,
2252 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2253 index: usize,
2254}
2255
2256impl Measurable for CoordinatorLink<'_, '_, '_> {
2257 fn measure(&self, constraints: Constraints) -> Placeable {
2258 self.chain.measure_from(self.index, self.frame, constraints)
2259 }
2260
2261 fn min_intrinsic_width(&self, height: f32) -> f32 {
2262 self.chain
2263 .min_intrinsic_width_from(self.index, self.frame, height)
2264 }
2265
2266 fn max_intrinsic_width(&self, height: f32) -> f32 {
2267 self.chain
2268 .max_intrinsic_width_from(self.index, self.frame, height)
2269 }
2270
2271 fn min_intrinsic_height(&self, width: f32) -> f32 {
2272 self.chain
2273 .min_intrinsic_height_from(self.index, self.frame, width)
2274 }
2275
2276 fn max_intrinsic_height(&self, width: f32) -> f32 {
2277 self.chain
2278 .max_intrinsic_height_from(self.index, self.frame, width)
2279 }
2280}
2281
2282struct CoordinatorNode {
2283 modifier_index: usize,
2284 node: Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2285 measured_size: Cell<Size>,
2286 accumulated_offset: Cell<Point>,
2287}
2288
2289impl CoordinatorNode {
2290 fn new(
2291 modifier_index: usize,
2292 node: Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2293 ) -> Self {
2294 Self {
2295 modifier_index,
2296 node,
2297 measured_size: Cell::new(Size::default()),
2298 accumulated_offset: Cell::new(Point::default()),
2299 }
2300 }
2301
2302 fn matches(
2303 &self,
2304 modifier_index: usize,
2305 node: &Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2306 ) -> bool {
2307 self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2308 }
2309
2310 #[cfg(test)]
2311 fn ptr(&self) -> usize {
2312 Rc::as_ptr(&self.node) as *const () as usize
2313 }
2314}
2315
2316#[derive(Default)]
2317struct CoordinatorChain {
2318 nodes: Vec<CoordinatorNode>,
2319}
2320
2321impl CoordinatorChain {
2322 fn reconcile(&mut self, layout_node_data: &[LayoutModifierNodeData]) {
2323 if self.matches(layout_node_data) {
2324 return;
2325 }
2326
2327 let mut previous_nodes = std::mem::take(&mut self.nodes);
2328 self.nodes.reserve(layout_node_data.len());
2329
2330 for (modifier_index, node) in layout_node_data.iter() {
2331 if let Some(position) = previous_nodes
2332 .iter()
2333 .position(|candidate| candidate.matches(*modifier_index, node))
2334 {
2335 self.nodes.push(previous_nodes.swap_remove(position));
2336 } else {
2337 self.nodes
2338 .push(CoordinatorNode::new(*modifier_index, Rc::clone(node)));
2339 }
2340 }
2341 }
2342
2343 fn matches(&self, layout_node_data: &[LayoutModifierNodeData]) -> bool {
2344 self.nodes.len() == layout_node_data.len()
2345 && self
2346 .nodes
2347 .iter()
2348 .zip(layout_node_data.iter())
2349 .all(|(node, (modifier_index, node_rc))| node.matches(*modifier_index, node_rc))
2350 }
2351
2352 fn measure_from(
2353 &self,
2354 index: usize,
2355 frame: &CoordinatorFrame<'_>,
2356 constraints: Constraints,
2357 ) -> Placeable {
2358 let Some(node) = self.nodes.get(index) else {
2359 let mut placements = frame.placements.borrow_mut();
2360 let size =
2361 frame
2362 .measure_policy
2363 .measure_into(frame.measurables, constraints, &mut placements);
2364 return Placeable::value(size.width, size.height, NodeId::default());
2365 };
2366
2367 let wrapped = CoordinatorLink {
2368 chain: self,
2369 frame,
2370 index: index + 1,
2371 };
2372 let node_borrow = node.node.borrow();
2373
2374 let Some(layout_node) = node_borrow.as_layout_node() else {
2375 let placeable = wrapped.measure(constraints);
2376 let child_accumulated = self.total_content_offset_from(index + 1);
2377 node.accumulated_offset.set(child_accumulated);
2378 return Placeable::value_with_offset(
2379 placeable.width(),
2380 placeable.height(),
2381 NodeId::default(),
2382 (child_accumulated.x, child_accumulated.y),
2383 );
2384 };
2385
2386 let result = match frame.context.try_borrow_mut() {
2387 Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
2388 Err(_) => {
2389 let mut temp = LayoutNodeContext::new();
2390 let result = layout_node.measure(&mut temp, &wrapped, constraints);
2391 if let Ok(mut context) = frame.context.try_borrow_mut() {
2392 for kind in temp.take_invalidations() {
2393 context.invalidate(kind);
2394 }
2395 }
2396 result
2397 }
2398 };
2399
2400 node.measured_size.set(result.size);
2401 let local_offset = Point {
2402 x: result.placement_offset_x,
2403 y: result.placement_offset_y,
2404 };
2405 let child_accumulated = self.total_content_offset_from(index + 1);
2406 let accumulated = Point {
2407 x: local_offset.x + child_accumulated.x,
2408 y: local_offset.y + child_accumulated.y,
2409 };
2410 node.accumulated_offset.set(accumulated);
2411
2412 Placeable::value_with_offset(
2413 result.size.width,
2414 result.size.height,
2415 NodeId::default(),
2416 (accumulated.x, accumulated.y),
2417 )
2418 }
2419
2420 fn min_intrinsic_width_from(
2421 &self,
2422 index: usize,
2423 frame: &CoordinatorFrame<'_>,
2424 height: f32,
2425 ) -> f32 {
2426 let Some(node) = self.nodes.get(index) else {
2427 return frame
2428 .measure_policy
2429 .min_intrinsic_width(frame.measurables, height);
2430 };
2431 let wrapped = CoordinatorLink {
2432 chain: self,
2433 frame,
2434 index: index + 1,
2435 };
2436 let node_borrow = node.node.borrow();
2437 node_borrow
2438 .as_layout_node()
2439 .map(|layout_node| layout_node.min_intrinsic_width(&wrapped, height))
2440 .unwrap_or_else(|| wrapped.min_intrinsic_width(height))
2441 }
2442
2443 fn max_intrinsic_width_from(
2444 &self,
2445 index: usize,
2446 frame: &CoordinatorFrame<'_>,
2447 height: f32,
2448 ) -> f32 {
2449 let Some(node) = self.nodes.get(index) else {
2450 return frame
2451 .measure_policy
2452 .max_intrinsic_width(frame.measurables, height);
2453 };
2454 let wrapped = CoordinatorLink {
2455 chain: self,
2456 frame,
2457 index: index + 1,
2458 };
2459 let node_borrow = node.node.borrow();
2460 node_borrow
2461 .as_layout_node()
2462 .map(|layout_node| layout_node.max_intrinsic_width(&wrapped, height))
2463 .unwrap_or_else(|| wrapped.max_intrinsic_width(height))
2464 }
2465
2466 fn min_intrinsic_height_from(
2467 &self,
2468 index: usize,
2469 frame: &CoordinatorFrame<'_>,
2470 width: f32,
2471 ) -> f32 {
2472 let Some(node) = self.nodes.get(index) else {
2473 return frame
2474 .measure_policy
2475 .min_intrinsic_height(frame.measurables, width);
2476 };
2477 let wrapped = CoordinatorLink {
2478 chain: self,
2479 frame,
2480 index: index + 1,
2481 };
2482 let node_borrow = node.node.borrow();
2483 node_borrow
2484 .as_layout_node()
2485 .map(|layout_node| layout_node.min_intrinsic_height(&wrapped, width))
2486 .unwrap_or_else(|| wrapped.min_intrinsic_height(width))
2487 }
2488
2489 fn max_intrinsic_height_from(
2490 &self,
2491 index: usize,
2492 frame: &CoordinatorFrame<'_>,
2493 width: f32,
2494 ) -> f32 {
2495 let Some(node) = self.nodes.get(index) else {
2496 return frame
2497 .measure_policy
2498 .max_intrinsic_height(frame.measurables, width);
2499 };
2500 let wrapped = CoordinatorLink {
2501 chain: self,
2502 frame,
2503 index: index + 1,
2504 };
2505 let node_borrow = node.node.borrow();
2506 node_borrow
2507 .as_layout_node()
2508 .map(|layout_node| layout_node.max_intrinsic_height(&wrapped, width))
2509 .unwrap_or_else(|| wrapped.max_intrinsic_height(width))
2510 }
2511
2512 fn total_content_offset_from(&self, index: usize) -> Point {
2513 self.nodes
2514 .get(index)
2515 .map(|node| node.accumulated_offset.get())
2516 .unwrap_or_default()
2517 }
2518
2519 #[cfg(test)]
2520 fn debug_ptrs(&self) -> Vec<usize> {
2521 self.nodes.iter().map(CoordinatorNode::ptr).collect()
2522 }
2523}
2524
2525#[derive(Default)]
2526pub(crate) struct LayoutRuntimeState {
2527 child_ids: Vec<NodeId>,
2528 child_states: Vec<Rc<LayoutChildMeasureState>>,
2529 child_measurables: Vec<Box<dyn Measurable>>,
2530 coordinator_chain: CoordinatorChain,
2531}
2532
2533impl LayoutRuntimeState {
2534 fn reconcile_child_measurables(&mut self, child_ids: &[NodeId]) {
2535 if self.child_ids == child_ids {
2536 return;
2537 }
2538
2539 let mut previous_ids = std::mem::take(&mut self.child_ids);
2540 let mut previous_states = std::mem::take(&mut self.child_states);
2541 let mut previous_measurables = std::mem::take(&mut self.child_measurables);
2542
2543 self.child_ids.reserve(child_ids.len());
2544 self.child_states.reserve(child_ids.len());
2545 self.child_measurables.reserve(child_ids.len());
2546
2547 for &child_id in child_ids {
2548 if let Some(position) = previous_ids.iter().position(|&id| id == child_id) {
2549 self.child_ids.push(previous_ids.swap_remove(position));
2550 self.child_states
2551 .push(previous_states.swap_remove(position));
2552 self.child_measurables
2553 .push(previous_measurables.swap_remove(position));
2554 } else {
2555 let state = LayoutChildMeasureState::new(child_id);
2556 self.child_ids.push(child_id);
2557 self.child_states.push(Rc::clone(&state));
2558 self.child_measurables
2559 .push(Box::new(LayoutChildMeasurable::new(state)));
2560 }
2561 }
2562 }
2563
2564 fn child_state(&self, index: usize) -> Rc<LayoutChildMeasureState> {
2565 Rc::clone(&self.child_states[index])
2566 }
2567
2568 fn child_measurables(&self) -> &[Box<dyn Measurable>] {
2569 self.child_measurables.as_slice()
2570 }
2571
2572 fn reconcile_coordinator_chain(&mut self, layout_node_data: &[LayoutModifierNodeData]) {
2573 self.coordinator_chain.reconcile(layout_node_data);
2574 }
2575
2576 fn coordinator_chain(&self) -> &CoordinatorChain {
2577 &self.coordinator_chain
2578 }
2579
2580 fn clear_frame_bindings(&self) {
2581 for child_state in &self.child_states {
2582 child_state.clear_frame_bindings();
2583 }
2584 }
2585
2586 #[cfg(test)]
2587 pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
2588 LayoutRuntimeDebugStats {
2589 child_ids: self.child_ids.clone(),
2590 child_state_ptrs: self
2591 .child_states
2592 .iter()
2593 .map(|state| Rc::as_ptr(state) as *const () as usize)
2594 .collect(),
2595 child_measurable_ptrs: self
2596 .child_measurables
2597 .iter()
2598 .map(|measurable| {
2599 measurable.as_ref() as *const dyn Measurable as *const () as usize
2600 })
2601 .collect(),
2602 child_measurable_count: self.child_measurables.len(),
2603 coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
2604 coordinator_node_count: self.coordinator_chain.nodes.len(),
2605 }
2606 }
2607}
2608
2609#[cfg(test)]
2610#[derive(Debug, Clone, PartialEq, Eq)]
2611pub(crate) struct LayoutRuntimeDebugStats {
2612 pub(crate) child_ids: Vec<NodeId>,
2613 pub(crate) child_state_ptrs: Vec<usize>,
2614 pub(crate) child_measurable_ptrs: Vec<usize>,
2615 pub(crate) child_measurable_count: usize,
2616 pub(crate) coordinator_node_ptrs: Vec<usize>,
2617 pub(crate) coordinator_node_count: usize,
2618}
2619
2620struct LayoutChildMeasureConfig {
2621 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
2622 node_id: NodeId,
2623 error: Rc<RefCell<Option<NodeError>>>,
2624 runtime_handle: Option<RuntimeHandle>,
2625 cache: LayoutNodeCacheHandles,
2626 cache_epoch: u64,
2627 force_remeasure: bool,
2628 measure_handle: Option<LayoutMeasureHandle>,
2629 layout_state: Option<Rc<RefCell<LayoutState>>>,
2630}
2631
2632struct LayoutChildMeasureState {
2633 applier: RefCell<Option<Rc<ConcreteApplierHost<MemoryApplier>>>>,
2634 node_id: Cell<NodeId>,
2635 measured: RefCell<Option<Rc<MeasuredNode>>>,
2636 last_position: Cell<Option<Point>>,
2637 error: RefCell<Option<Rc<RefCell<Option<NodeError>>>>>,
2638 runtime_handle: RefCell<Option<RuntimeHandle>>,
2639 cache: RefCell<LayoutNodeCacheHandles>,
2640 cache_epoch: Cell<u64>,
2641 force_remeasure: Cell<bool>,
2642 measure_handle: RefCell<Option<LayoutMeasureHandle>>,
2643 layout_state: RefCell<Option<Rc<RefCell<LayoutState>>>>,
2644}
2645
2646impl LayoutChildMeasureState {
2647 fn new(node_id: NodeId) -> Rc<Self> {
2648 Rc::new(Self {
2649 applier: RefCell::new(None),
2650 node_id: Cell::new(node_id),
2651 measured: RefCell::new(None),
2652 last_position: Cell::new(None),
2653 error: RefCell::new(None),
2654 runtime_handle: RefCell::new(None),
2655 cache: RefCell::new(LayoutNodeCacheHandles::default()),
2656 cache_epoch: Cell::new(0),
2657 force_remeasure: Cell::new(true),
2658 measure_handle: RefCell::new(None),
2659 layout_state: RefCell::new(None),
2660 })
2661 }
2662
2663 fn configure(&self, config: LayoutChildMeasureConfig) {
2664 config.cache.activate(config.cache_epoch);
2665 *self.applier.borrow_mut() = Some(config.applier);
2666 self.node_id.set(config.node_id);
2667 self.measured.borrow_mut().take();
2668 self.last_position.set(None);
2669 *self.error.borrow_mut() = Some(config.error);
2670 *self.runtime_handle.borrow_mut() = config.runtime_handle;
2671 *self.cache.borrow_mut() = config.cache;
2672 self.cache_epoch.set(config.cache_epoch);
2673 self.force_remeasure.set(config.force_remeasure);
2674 *self.measure_handle.borrow_mut() = config.measure_handle;
2675 *self.layout_state.borrow_mut() = config.layout_state;
2676 }
2677
2678 fn clear_frame_bindings(&self) {
2679 self.measured.borrow_mut().take();
2680 *self.applier.borrow_mut() = None;
2681 *self.error.borrow_mut() = None;
2682 *self.runtime_handle.borrow_mut() = None;
2683 *self.measure_handle.borrow_mut() = None;
2684 *self.layout_state.borrow_mut() = None;
2685 }
2686
2687 fn node_id(&self) -> NodeId {
2688 self.node_id.get()
2689 }
2690
2691 fn cache(&self) -> LayoutNodeCacheHandles {
2692 self.cache.borrow().clone()
2693 }
2694
2695 fn applier(&self) -> Option<Rc<ConcreteApplierHost<MemoryApplier>>> {
2696 self.applier.borrow().clone()
2697 }
2698
2699 fn layout_state(&self) -> Option<Rc<RefCell<LayoutState>>> {
2700 self.layout_state.borrow().clone()
2701 }
2702
2703 fn take_measured(&self) -> Option<Rc<MeasuredNode>> {
2704 self.measured.borrow_mut().take()
2705 }
2706
2707 fn last_position(&self) -> Option<Point> {
2708 self.last_position.get()
2709 }
2710
2711 fn set_last_position(&self, position: Point) {
2712 self.last_position.set(Some(position));
2713 }
2714
2715 fn set_measured(&self, measured: Option<Rc<MeasuredNode>>) {
2716 *self.measured.borrow_mut() = measured;
2717 }
2718
2719 fn record_error(&self, err: NodeError) {
2720 let Some(error) = self.error.borrow().clone() else {
2721 return;
2722 };
2723 let mut slot = error.borrow_mut();
2724 if slot.is_none() {
2725 *slot = Some(err);
2726 }
2727 }
2728
2729 fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
2730 let node_id = self.node_id();
2731 if let Some(handle) = self.measure_handle.borrow().clone() {
2732 return handle.measure(node_id, constraints);
2733 }
2734 let applier = self.applier().ok_or(NodeError::MissingContext {
2735 id: node_id,
2736 reason: "layout child applier not configured",
2737 })?;
2738 measure_node_with_host(
2739 applier,
2740 self.runtime_handle.borrow().clone(),
2741 node_id,
2742 constraints,
2743 self.cache_epoch.get(),
2744 )
2745 }
2746
2747 fn intrinsic_measure(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
2748 let cache = self.cache();
2749 cache.activate(self.cache_epoch.get());
2750 if !self.force_remeasure.get() {
2751 if let Some(cached) = cache.get_measurement(constraints) {
2752 return Some(cached);
2753 }
2754 }
2755
2756 match self.perform_measure(constraints) {
2757 Ok(measured) => {
2758 self.force_remeasure.set(false);
2759 cache.store_measurement(constraints, Rc::clone(&measured));
2760 Some(measured)
2761 }
2762 Err(err) => {
2763 self.record_error(err);
2764 None
2765 }
2766 }
2767 }
2768}
2769
2770struct LayoutChildMeasurable {
2771 state: Rc<LayoutChildMeasureState>,
2772}
2773
2774impl LayoutChildMeasurable {
2775 fn new(state: Rc<LayoutChildMeasureState>) -> Self {
2776 Self { state }
2777 }
2778}
2779
2780impl Measurable for LayoutChildMeasurable {
2781 fn measure(&self, constraints: Constraints) -> Placeable {
2782 let state = &self.state;
2783 let cache = state.cache();
2784 cache.activate(state.cache_epoch.get());
2785 let measured_size;
2786 if !state.force_remeasure.get() {
2787 if let Some(cached) = cache.get_measurement(constraints) {
2788 measured_size = cached.size;
2789 state.set_measured(Some(Rc::clone(&cached)));
2790 } else {
2791 match state.perform_measure(constraints) {
2792 Ok(measured) => {
2793 state.force_remeasure.set(false);
2794 measured_size = measured.size;
2795 cache.store_measurement(constraints, Rc::clone(&measured));
2796 state.set_measured(Some(measured));
2797 }
2798 Err(err) => {
2799 state.record_error(err);
2800 state.set_measured(None);
2801 measured_size = Size {
2802 width: 0.0,
2803 height: 0.0,
2804 };
2805 }
2806 }
2807 }
2808 } else {
2809 match state.perform_measure(constraints) {
2810 Ok(measured) => {
2811 state.force_remeasure.set(false);
2812 measured_size = measured.size;
2813 cache.store_measurement(constraints, Rc::clone(&measured));
2814 state.set_measured(Some(measured));
2815 }
2816 Err(err) => {
2817 state.record_error(err);
2818 state.set_measured(None);
2819 measured_size = Size {
2820 width: 0.0,
2821 height: 0.0,
2822 };
2823 }
2824 }
2825 }
2826
2827 if let Some(layout_state) = state.layout_state() {
2828 let mut layout_state = layout_state.borrow_mut();
2829 layout_state.size = measured_size;
2830 layout_state.measurement_constraints = constraints;
2831 } else if let Some(applier) = state.applier() {
2832 let Ok(mut applier) = applier.try_borrow_typed() else {
2833 return Placeable::value(
2834 measured_size.width,
2835 measured_size.height,
2836 state.node_id(),
2837 );
2838 };
2839 let _ = applier.with_node::<LayoutNode, _>(state.node_id(), |node| {
2840 node.set_measured_size(measured_size);
2841 node.set_measurement_constraints(constraints);
2842 });
2843 }
2844
2845 let state = Rc::clone(&self.state);
2846 let applier = state.applier();
2847 let node_id = state.node_id();
2848 let layout_state = state.layout_state();
2849
2850 let place_fn = Rc::new(move |x: f32, y: f32| {
2851 let internal_offset = state
2852 .measured
2853 .borrow()
2854 .as_ref()
2855 .map(|m| m.offset)
2856 .unwrap_or_default();
2857
2858 let position = Point {
2859 x: x + internal_offset.x,
2860 y: y + internal_offset.y,
2861 };
2862 state.set_last_position(position);
2863
2864 if let Some(layout_state) = &layout_state {
2865 let mut layout_state = layout_state.borrow_mut();
2866 layout_state.position = position;
2867 layout_state.is_placed = true;
2868 } else if let Some(applier) = &applier {
2869 let Ok(mut applier) = applier.try_borrow_typed() else {
2870 return;
2871 };
2872 if applier
2873 .with_node::<LayoutNode, _>(node_id, |node| {
2874 node.set_position(position);
2875 })
2876 .is_err()
2877 {
2878 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
2879 node.set_position(position);
2880 });
2881 }
2882 }
2883 });
2884
2885 Placeable::with_place_fn(measured_size.width, measured_size.height, node_id, place_fn)
2886 }
2887
2888 fn min_intrinsic_width(&self, height: f32) -> f32 {
2889 let kind = IntrinsicKind::MinWidth(height);
2890 let cache = self.state.cache();
2891 cache.activate(self.state.cache_epoch.get());
2892 if !self.state.force_remeasure.get() {
2893 if let Some(value) = cache.get_intrinsic(&kind) {
2894 return value;
2895 }
2896 }
2897 let constraints = Constraints {
2898 min_width: 0.0,
2899 max_width: f32::INFINITY,
2900 min_height: height,
2901 max_height: height,
2902 };
2903 if let Some(node) = self.state.intrinsic_measure(constraints) {
2904 let value = node.size.width;
2905 cache.store_intrinsic(kind, value);
2906 value
2907 } else {
2908 0.0
2909 }
2910 }
2911
2912 fn max_intrinsic_width(&self, height: f32) -> f32 {
2913 let kind = IntrinsicKind::MaxWidth(height);
2914 let cache = self.state.cache();
2915 cache.activate(self.state.cache_epoch.get());
2916 if !self.state.force_remeasure.get() {
2917 if let Some(value) = cache.get_intrinsic(&kind) {
2918 return value;
2919 }
2920 }
2921 let constraints = Constraints {
2922 min_width: 0.0,
2923 max_width: f32::INFINITY,
2924 min_height: 0.0,
2925 max_height: height,
2926 };
2927 if let Some(node) = self.state.intrinsic_measure(constraints) {
2928 let value = node.size.width;
2929 cache.store_intrinsic(kind, value);
2930 value
2931 } else {
2932 0.0
2933 }
2934 }
2935
2936 fn min_intrinsic_height(&self, width: f32) -> f32 {
2937 let kind = IntrinsicKind::MinHeight(width);
2938 let cache = self.state.cache();
2939 cache.activate(self.state.cache_epoch.get());
2940 if !self.state.force_remeasure.get() {
2941 if let Some(value) = cache.get_intrinsic(&kind) {
2942 return value;
2943 }
2944 }
2945 let constraints = Constraints {
2946 min_width: width,
2947 max_width: width,
2948 min_height: 0.0,
2949 max_height: f32::INFINITY,
2950 };
2951 if let Some(node) = self.state.intrinsic_measure(constraints) {
2952 let value = node.size.height;
2953 cache.store_intrinsic(kind, value);
2954 value
2955 } else {
2956 0.0
2957 }
2958 }
2959
2960 fn max_intrinsic_height(&self, width: f32) -> f32 {
2961 let kind = IntrinsicKind::MaxHeight(width);
2962 let cache = self.state.cache();
2963 cache.activate(self.state.cache_epoch.get());
2964 if !self.state.force_remeasure.get() {
2965 if let Some(value) = cache.get_intrinsic(&kind) {
2966 return value;
2967 }
2968 }
2969 let constraints = Constraints {
2970 min_width: 0.0,
2971 max_width: width,
2972 min_height: 0.0,
2973 max_height: f32::INFINITY,
2974 };
2975 if let Some(node) = self.state.intrinsic_measure(constraints) {
2976 let value = node.size.height;
2977 cache.store_intrinsic(kind, value);
2978 value
2979 } else {
2980 0.0
2981 }
2982 }
2983
2984 fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
2985 let applier = self.state.applier()?;
2986 let node_id = self.state.node_id();
2987 let Ok(mut applier) = applier.try_borrow_typed() else {
2988 return None;
2989 };
2990
2991 applier
2992 .with_node::<LayoutNode, _>(node_id, |layout_node| {
2993 let props = layout_node.resolved_modifiers().layout_properties();
2994 props.weight().map(|weight_data| {
2995 cranpose_ui_layout::FlexParentData::new(weight_data.weight, weight_data.fill)
2996 })
2997 })
2998 .ok()
2999 .flatten()
3000 }
3001}
3002
3003fn measure_node_with_host(
3004 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
3005 runtime_handle: Option<RuntimeHandle>,
3006 node_id: NodeId,
3007 constraints: Constraints,
3008 epoch: u64,
3009) -> Result<Rc<MeasuredNode>, NodeError> {
3010 let runtime_handle = match runtime_handle {
3011 Some(handle) => Some(handle),
3012 None => applier.borrow_typed().runtime_handle(),
3013 };
3014 let mut builder = LayoutBuilder::new_with_epoch(
3015 applier,
3016 epoch,
3017 Rc::new(RefCell::new(SlotTable::default())),
3018 FrameLayoutArena::default(),
3019 );
3020 builder.set_runtime_handle(runtime_handle);
3021 builder.measure_node(node_id, constraints)
3022}
3023
3024#[derive(Clone)]
3025struct RuntimeNodeMetadata {
3026 modifier: Modifier,
3027 resolved_modifiers: ResolvedModifiers,
3028 modifier_slices: Rc<ModifierNodeSlices>,
3029 role: SemanticsRole,
3030 button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3031}
3032
3033impl Default for RuntimeNodeMetadata {
3034 fn default() -> Self {
3035 Self {
3036 modifier: Modifier::empty(),
3037 resolved_modifiers: ResolvedModifiers::default(),
3038 modifier_slices: Rc::default(),
3039 role: SemanticsRole::Unknown,
3040 button_handler: None,
3041 }
3042 }
3043}
3044
3045fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3046 modifier_slices
3047 .text_content()
3048 .map(|text| SemanticsRole::Text {
3049 value: text.to_string(),
3050 })
3051 .unwrap_or(SemanticsRole::Layout)
3052}
3053
3054fn runtime_metadata_for(
3055 applier: &mut MemoryApplier,
3056 node_id: NodeId,
3057) -> Result<RuntimeNodeMetadata, NodeError> {
3058 if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3063 let modifier = layout.modifier.clone();
3064 let resolved_modifiers = layout.resolved_modifiers();
3065 let modifier_slices = layout.modifier_slices_snapshot();
3066 let role = role_from_modifier_slices(&modifier_slices);
3067
3068 RuntimeNodeMetadata {
3069 modifier,
3070 resolved_modifiers,
3071 modifier_slices,
3072 role,
3073 button_handler: None,
3074 }
3075 }) {
3076 return Ok(meta);
3077 }
3078
3079 if let Ok((modifier, resolved_modifiers, modifier_slices)) = applier
3081 .with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3082 (
3083 node.modifier(),
3084 node.resolved_modifiers(),
3085 node.modifier_slices_snapshot(),
3086 )
3087 })
3088 {
3089 return Ok(RuntimeNodeMetadata {
3090 modifier,
3091 resolved_modifiers,
3092 modifier_slices,
3093 role: SemanticsRole::Subcompose,
3094 button_handler: None,
3095 });
3096 }
3097 Ok(RuntimeNodeMetadata::default())
3098}
3099
3100fn clear_semantics_dirty_flags(
3101 applier: &mut MemoryApplier,
3102 node: &MeasuredNode,
3103) -> Result<(), NodeError> {
3104 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3105 layout.clear_needs_semantics();
3106 }) {
3107 Ok(()) => {}
3108 Err(NodeError::Missing { .. }) => {}
3109 Err(NodeError::TypeMismatch { .. }) => {
3110 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3111 subcompose.clear_needs_semantics();
3112 }) {
3113 Ok(()) | Err(NodeError::Missing { .. }) | Err(NodeError::TypeMismatch { .. }) => {}
3114 Err(err) => return Err(err),
3115 }
3116 }
3117 Err(err) => return Err(err),
3118 }
3119
3120 for child in &node.children {
3121 clear_semantics_dirty_flags(applier, &child.node)?;
3122 }
3123
3124 Ok(())
3125}
3126
3127fn build_semantics_tree_from_live_nodes(
3128 applier: &mut MemoryApplier,
3129 node: &MeasuredNode,
3130) -> Result<SemanticsTree, NodeError> {
3131 Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3132 applier, node,
3133 )?))
3134}
3135
3136fn semantics_node_from_parts(
3137 node_id: NodeId,
3138 mut role: SemanticsRole,
3139 config: Option<SemanticsConfiguration>,
3140 children: Vec<SemanticsNode>,
3141) -> SemanticsNode {
3142 let mut actions = Vec::new();
3143 let mut description = None;
3144 let mut editable_text = false;
3145 let mut text_selection = None;
3146
3147 if let Some(config) = config {
3148 if config.is_button {
3149 role = SemanticsRole::Button;
3150 }
3151 if config.is_clickable {
3152 actions.push(SemanticsAction::Click {
3153 handler: SemanticsCallback::new(node_id),
3154 });
3155 }
3156 description = config.content_description;
3157 editable_text = config.is_editable_text;
3158 text_selection = config.text_selection;
3159 }
3160
3161 SemanticsNode::new(
3162 node_id,
3163 role,
3164 actions,
3165 children,
3166 description,
3167 editable_text,
3168 text_selection,
3169 )
3170}
3171
3172fn build_semantics_node_from_live_nodes(
3173 applier: &mut MemoryApplier,
3174 node: &MeasuredNode,
3175) -> Result<SemanticsNode, NodeError> {
3176 let (role, config) = match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3177 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
3178 let config = layout.semantics_configuration();
3179 layout.clear_needs_semantics();
3180 (role, config)
3181 }) {
3182 Ok(data) => data,
3183 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {
3184 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3185 subcompose.clear_needs_semantics();
3186 (
3187 SemanticsRole::Subcompose,
3188 collect_semantics_from_modifier(&subcompose.modifier()),
3189 )
3190 }) {
3191 Ok(data) => data,
3192 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {
3193 (SemanticsRole::Unknown, None)
3194 }
3195 Err(err) => return Err(err),
3196 }
3197 }
3198 Err(err) => return Err(err),
3199 };
3200
3201 let mut children = Vec::with_capacity(node.children.len());
3202 for child in &node.children {
3203 children.push(build_semantics_node_from_live_nodes(applier, &child.node)?);
3204 }
3205
3206 Ok(semantics_node_from_parts(
3207 node.node_id,
3208 role,
3209 config,
3210 children,
3211 ))
3212}
3213
3214fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
3215 stats.semantics_node_count += 1;
3216 stats.semantics_action_count += node.actions.len();
3217 stats.semantics_action_capacity += node.actions.capacity();
3218 stats.semantics_child_count += node.children.len();
3219 stats.semantics_child_capacity += node.children.capacity();
3220 stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
3221 stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
3222
3223 if let Some(description) = &node.description {
3224 stats.semantics_description_count += 1;
3225 stats.semantics_description_bytes += description.capacity();
3226 stats.semantics_heap_bytes += description.capacity();
3227 }
3228 if let SemanticsRole::Text { value } = &node.role {
3229 stats.semantics_text_role_bytes += value.capacity();
3230 stats.semantics_heap_bytes += value.capacity();
3231 }
3232
3233 for child in &node.children {
3234 record_semantics_allocation_stats(child, stats);
3235 }
3236}
3237
3238fn record_layout_box_allocation_stats(
3239 layout_box: &LayoutBox,
3240 stats: &mut LayoutAllocationDebugStats,
3241) {
3242 stats.layout_box_count += 1;
3243 stats.layout_box_child_count += layout_box.children.len();
3244 stats.layout_box_child_capacity += layout_box.children.capacity();
3245 stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
3246 stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
3247
3248 for child in &layout_box.children {
3249 record_layout_box_allocation_stats(child, stats);
3250 }
3251}
3252
3253fn build_layout_tree(
3254 applier: &mut MemoryApplier,
3255 node: &MeasuredNode,
3256) -> Result<LayoutTree, NodeError> {
3257 fn place(
3258 applier: &mut MemoryApplier,
3259 node: &MeasuredNode,
3260 origin: Point,
3261 parent_layer_translation: Point,
3269 ) -> Result<LayoutBox, NodeError> {
3270 let top_left = Point {
3272 x: origin.x + node.offset.x,
3273 y: origin.y + node.offset.y,
3274 };
3275 let rect = GeometryRect {
3276 x: top_left.x,
3277 y: top_left.y,
3278 width: node.size.width,
3279 height: node.size.height,
3280 };
3281 let info = runtime_metadata_for(applier, node.node_id)?;
3282 let kind = layout_kind_from_metadata(node.node_id, &info);
3283 let RuntimeNodeMetadata {
3284 modifier,
3285 resolved_modifiers,
3286 modifier_slices,
3287 ..
3288 } = info;
3289
3290 let layer_translation = match modifier_slices.graphics_layer() {
3291 Some(layer) => Point {
3292 x: parent_layer_translation.x + layer.translation_x,
3293 y: parent_layer_translation.y + layer.translation_y,
3294 },
3295 None => parent_layer_translation,
3296 };
3297
3298 if let Some(sink) = modifier_slices.text_field_window_origin() {
3304 sink.set(Point {
3305 x: top_left.x + layer_translation.x,
3306 y: top_left.y + layer_translation.y,
3307 });
3308 }
3309
3310 let data = LayoutNodeData::new(modifier, resolved_modifiers, modifier_slices, kind);
3311 let mut children = Vec::with_capacity(node.children.len());
3312 for child in &node.children {
3313 let child_origin = Point {
3314 x: top_left.x + child.offset.x,
3315 y: top_left.y + child.offset.y,
3316 };
3317 children.push(place(
3318 applier,
3319 &child.node,
3320 child_origin,
3321 layer_translation,
3322 )?);
3323 }
3324 Ok(LayoutBox::new(
3325 node.node_id,
3326 rect,
3327 node.content_offset,
3328 data,
3329 children,
3330 ))
3331 }
3332
3333 Ok(LayoutTree::new(place(
3334 applier,
3335 node,
3336 Point { x: 0.0, y: 0.0 },
3337 Point { x: 0.0, y: 0.0 },
3338 )?))
3339}
3340
3341fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
3342 match &layout_box.node_data.kind {
3343 LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
3344 LayoutNodeKind::Spacer => SemanticsRole::Spacer,
3345 LayoutNodeKind::Unknown => SemanticsRole::Unknown,
3346 LayoutNodeKind::Button { .. } => SemanticsRole::Button,
3347 LayoutNodeKind::Layout => layout_box
3348 .node_data
3349 .modifier_slices()
3350 .text_content()
3351 .map(|text| SemanticsRole::Text {
3352 value: text.to_string(),
3353 })
3354 .unwrap_or(SemanticsRole::Layout),
3355 }
3356}
3357
3358fn build_semantics_node_from_layout_box(layout_box: &LayoutBox) -> SemanticsNode {
3359 let children = layout_box
3360 .children
3361 .iter()
3362 .map(build_semantics_node_from_layout_box)
3363 .collect();
3364
3365 semantics_node_from_parts(
3366 layout_box.node_id,
3367 semantics_role_from_layout_box(layout_box),
3368 collect_semantics_from_modifier(&layout_box.node_data.modifier),
3369 children,
3370 )
3371}
3372
3373fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
3374 match &info.role {
3375 SemanticsRole::Layout => LayoutNodeKind::Layout,
3376 SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
3377 SemanticsRole::Text { .. } => {
3378 LayoutNodeKind::Layout
3382 }
3383 SemanticsRole::Spacer => LayoutNodeKind::Spacer,
3384 SemanticsRole::Button => {
3385 let handler = info
3386 .button_handler
3387 .as_ref()
3388 .cloned()
3389 .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
3390 LayoutNodeKind::Button { on_click: handler }
3391 }
3392 SemanticsRole::Unknown => LayoutNodeKind::Unknown,
3393 }
3394}
3395
3396fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
3397 let horizontal = padding.horizontal_sum();
3398 let vertical = padding.vertical_sum();
3399 let min_width = (constraints.min_width - horizontal).max(0.0);
3400 let mut max_width = constraints.max_width;
3401 if max_width.is_finite() {
3402 max_width = (max_width - horizontal).max(0.0);
3403 }
3404 let min_height = (constraints.min_height - vertical).max(0.0);
3405 let mut max_height = constraints.max_height;
3406 if max_height.is_finite() {
3407 max_height = (max_height - vertical).max(0.0);
3408 }
3409 normalize_constraints(Constraints {
3410 min_width,
3411 max_width,
3412 min_height,
3413 max_height,
3414 })
3415}
3416
3417#[cfg(test)]
3418pub(crate) fn align_horizontal(alignment: HorizontalAlignment, available: f32, child: f32) -> f32 {
3419 match alignment {
3420 HorizontalAlignment::Start => 0.0,
3421 HorizontalAlignment::CenterHorizontally => ((available - child) / 2.0).max(0.0),
3422 HorizontalAlignment::End => (available - child).max(0.0),
3423 }
3424}
3425
3426#[cfg(test)]
3427pub(crate) fn align_vertical(alignment: VerticalAlignment, available: f32, child: f32) -> f32 {
3428 match alignment {
3429 VerticalAlignment::Top => 0.0,
3430 VerticalAlignment::CenterVertically => ((available - child) / 2.0).max(0.0),
3431 VerticalAlignment::Bottom => (available - child).max(0.0),
3432 }
3433}
3434
3435fn resolve_dimension(
3436 base: f32,
3437 explicit: DimensionConstraint,
3438 min_override: Option<f32>,
3439 max_override: Option<f32>,
3440 min_limit: f32,
3441 max_limit: f32,
3442) -> f32 {
3443 let mut min_bound = min_limit;
3444 if let Some(min_value) = min_override {
3445 min_bound = min_bound.max(min_value);
3446 }
3447
3448 let mut max_bound = if max_limit.is_finite() {
3449 max_limit
3450 } else {
3451 max_override.unwrap_or(max_limit)
3452 };
3453 if let Some(max_value) = max_override {
3454 if max_bound.is_finite() {
3455 max_bound = max_bound.min(max_value);
3456 } else {
3457 max_bound = max_value;
3458 }
3459 }
3460 if max_bound < min_bound {
3461 max_bound = min_bound;
3462 }
3463
3464 let mut size = match explicit {
3465 DimensionConstraint::Points(points) => points,
3466 DimensionConstraint::Fraction(fraction) => {
3467 if max_limit.is_finite() {
3468 max_limit * fraction.clamp(0.0, 1.0)
3469 } else {
3470 base
3471 }
3472 }
3473 DimensionConstraint::Unspecified => base,
3474 DimensionConstraint::Intrinsic(_) => base,
3477 };
3478
3479 size = clamp_dimension(size, min_bound, max_bound);
3480 size = clamp_dimension(size, min_limit, max_limit);
3481 size.max(0.0)
3482}
3483
3484fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
3485 let mut result = value.max(min);
3486 if max.is_finite() {
3487 result = result.min(max);
3488 }
3489 result
3490}
3491
3492fn normalize_constraints(mut constraints: Constraints) -> Constraints {
3493 if constraints.max_width < constraints.min_width {
3494 constraints.max_width = constraints.min_width;
3495 }
3496 if constraints.max_height < constraints.min_height {
3497 constraints.max_height = constraints.min_height;
3498 }
3499 constraints
3500}
3501
3502#[cfg(test)]
3503#[path = "tests/layout_tests.rs"]
3504mod tests;