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