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, CanvasSemanticsNode, InvalidationKind, ModifierNodeContext, NodeCapabilities,
30 SemanticsConfiguration, SemanticsCustomAction, SemanticsWidgetRole,
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)]
403pub struct SemanticsNode {
404 pub node_id: NodeId,
405 pub role: SemanticsRole,
407 pub widget_role: Option<SemanticsWidgetRole>,
411 pub actions: Vec<SemanticsAction>,
412 pub children: Vec<SemanticsNode>,
413 pub description: Option<String>,
414 pub state_description: Option<String>,
415 pub on_click_label: Option<String>,
416 pub selected: Option<bool>,
417 pub toggled: Option<bool>,
418 pub enabled: bool,
419 pub custom_actions: Vec<SemanticsCustomAction>,
420 pub canvas_children: Vec<CanvasSemanticsNode>,
423 pub editable_text: bool,
424 pub text_selection: Option<TextRange>,
425}
426
427impl Default for SemanticsNode {
428 fn default() -> Self {
429 Self {
430 node_id: 0,
431 role: SemanticsRole::Unknown,
432 widget_role: None,
433 actions: Vec::new(),
434 children: Vec::new(),
435 description: None,
436 state_description: None,
437 on_click_label: None,
438 selected: None,
439 toggled: None,
440 enabled: true,
441 custom_actions: Vec::new(),
442 canvas_children: Vec::new(),
443 editable_text: false,
444 text_selection: None,
445 }
446 }
447}
448
449#[derive(Clone, Debug, PartialEq)]
451pub struct SemanticsTree {
452 root: SemanticsNode,
453}
454
455impl SemanticsTree {
456 fn new(root: SemanticsNode) -> Self {
457 Self { root }
458 }
459
460 pub fn root(&self) -> &SemanticsNode {
461 &self.root
462 }
463}
464
465#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
466pub struct LayoutAllocationDebugStats {
467 pub layout_box_count: usize,
468 pub layout_box_child_count: usize,
469 pub layout_box_child_capacity: usize,
470 pub layout_box_heap_bytes: usize,
471 pub modifier_slice_count: usize,
472 pub modifier_slice_heap_bytes: usize,
473 pub modifier_draw_command_count: usize,
474 pub modifier_draw_command_capacity: usize,
475 pub modifier_pointer_input_count: usize,
476 pub modifier_pointer_input_capacity: usize,
477 pub modifier_click_handler_count: usize,
478 pub modifier_click_handler_capacity: usize,
479 pub modifier_text_content_count: usize,
480 pub modifier_text_style_count: usize,
481 pub modifier_text_layout_options_count: usize,
482 pub modifier_prepared_text_layout_count: usize,
483 pub modifier_graphics_layer_count: usize,
484 pub modifier_graphics_layer_resolver_count: usize,
485 pub semantics_node_count: usize,
486 pub semantics_action_count: usize,
487 pub semantics_action_capacity: usize,
488 pub semantics_child_count: usize,
489 pub semantics_child_capacity: usize,
490 pub semantics_description_count: usize,
491 pub semantics_description_bytes: usize,
492 pub semantics_text_role_bytes: usize,
493 pub semantics_heap_bytes: usize,
494}
495
496impl LayoutAllocationDebugStats {
497 fn add_modifier_slice(&mut self, stats: ModifierNodeSlicesDebugStats) {
498 self.modifier_slice_count += 1;
499 self.modifier_slice_heap_bytes += stats.heap_bytes;
500 self.modifier_draw_command_count += stats.draw_command_count;
501 self.modifier_draw_command_capacity += stats.draw_command_capacity;
502 self.modifier_pointer_input_count += stats.pointer_input_count;
503 self.modifier_pointer_input_capacity += stats.pointer_input_capacity;
504 self.modifier_click_handler_count += stats.click_handler_count;
505 self.modifier_click_handler_capacity += stats.click_handler_capacity;
506 self.modifier_text_content_count += usize::from(stats.has_text_content);
507 self.modifier_text_style_count += usize::from(stats.has_text_style);
508 self.modifier_text_layout_options_count += usize::from(stats.has_text_layout_options);
509 self.modifier_prepared_text_layout_count += usize::from(stats.has_prepared_text_layout);
510 self.modifier_graphics_layer_count += usize::from(stats.has_graphics_layer);
511 self.modifier_graphics_layer_resolver_count +=
512 usize::from(stats.has_graphics_layer_resolver);
513 }
514}
515
516#[derive(Debug, Clone)]
518pub struct LayoutTree {
519 root: LayoutBox,
520}
521
522impl LayoutTree {
523 pub fn new(root: LayoutBox) -> Self {
524 Self { root }
525 }
526
527 pub fn root(&self) -> &LayoutBox {
528 &self.root
529 }
530
531 pub fn root_mut(&mut self) -> &mut LayoutBox {
532 &mut self.root
533 }
534
535 pub fn into_root(self) -> LayoutBox {
536 self.root
537 }
538
539 pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
540 let mut stats = LayoutAllocationDebugStats::default();
541 record_layout_box_allocation_stats(&self.root, &mut stats);
542 stats
543 }
544}
545
546#[derive(Debug, Clone)]
548pub struct LayoutBox {
549 pub node_id: NodeId,
550 pub rect: GeometryRect,
551 pub content_offset: Point,
553 pub node_data: LayoutNodeData,
554 pub children: Vec<LayoutBox>,
555}
556
557impl LayoutBox {
558 pub fn new(
559 node_id: NodeId,
560 rect: GeometryRect,
561 content_offset: Point,
562 node_data: LayoutNodeData,
563 children: Vec<LayoutBox>,
564 ) -> Self {
565 Self {
566 node_id,
567 rect,
568 content_offset,
569 node_data,
570 children,
571 }
572 }
573}
574
575#[derive(Debug, Clone)]
577pub struct LayoutNodeData {
578 pub modifier: Modifier,
579 pub resolved_modifiers: ResolvedModifiers,
580 pub modifier_slices: Rc<ModifierNodeSlices>,
581 pub kind: LayoutNodeKind,
582}
583
584impl LayoutNodeData {
585 pub fn new(
586 modifier: Modifier,
587 resolved_modifiers: ResolvedModifiers,
588 modifier_slices: Rc<ModifierNodeSlices>,
589 kind: LayoutNodeKind,
590 ) -> Self {
591 Self {
592 modifier,
593 resolved_modifiers,
594 modifier_slices,
595 kind,
596 }
597 }
598
599 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
600 self.resolved_modifiers
601 }
602
603 pub fn modifier_slices(&self) -> &ModifierNodeSlices {
604 &self.modifier_slices
605 }
606}
607
608#[derive(Clone)]
615pub enum LayoutNodeKind {
616 Layout,
617 Subcompose,
618 Spacer,
619 Button { on_click: Rc<RefCell<dyn FnMut()>> },
620 Unknown,
621}
622
623impl fmt::Debug for LayoutNodeKind {
624 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
625 match self {
626 LayoutNodeKind::Layout => f.write_str("Layout"),
627 LayoutNodeKind::Subcompose => f.write_str("Subcompose"),
628 LayoutNodeKind::Spacer => f.write_str("Spacer"),
629 LayoutNodeKind::Button { .. } => f.write_str("Button"),
630 LayoutNodeKind::Unknown => f.write_str("Unknown"),
631 }
632 }
633}
634
635pub trait LayoutEngine {
637 fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError>;
638}
639
640impl LayoutEngine for MemoryApplier {
641 fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError> {
642 let measurements = measure_layout(self, root, max_size)?;
643 measurements
644 .into_layout_tree()
645 .ok_or(NodeError::MissingContext {
646 id: root,
647 reason: "layout tree was not requested",
648 })
649 }
650}
651
652#[derive(Debug, Clone)]
654pub struct LayoutMeasurements {
655 root: Rc<MeasuredNode>,
656 semantics: Option<SemanticsTree>,
657 layout_tree: Option<LayoutTree>,
658}
659
660impl LayoutMeasurements {
661 fn new(
662 root: Rc<MeasuredNode>,
663 semantics: Option<SemanticsTree>,
664 layout_tree: Option<LayoutTree>,
665 ) -> Self {
666 Self {
667 root,
668 semantics,
669 layout_tree,
670 }
671 }
672
673 pub fn root_size(&self) -> Size {
675 self.root.size
676 }
677
678 pub fn semantics_tree(&self) -> Option<&SemanticsTree> {
679 self.semantics.as_ref()
680 }
681
682 pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
683 let mut stats = self
684 .layout_tree
685 .as_ref()
686 .map(LayoutTree::debug_allocation_stats)
687 .unwrap_or_default();
688 if let Some(semantics) = &self.semantics {
689 record_semantics_allocation_stats(semantics.root(), &mut stats);
690 }
691 stats
692 }
693
694 pub fn into_layout_tree(self) -> Option<LayoutTree> {
696 self.layout_tree
697 }
698
699 pub fn layout_tree(&self) -> Option<LayoutTree> {
701 self.layout_tree.clone()
702 }
703}
704
705pub fn build_semantics_tree_from_layout_tree(layout_tree: &LayoutTree) -> SemanticsTree {
710 SemanticsTree::new(build_semantics_node_from_layout_box(layout_tree.root()))
711}
712
713pub fn build_layout_tree_from_applier(
719 applier: &mut MemoryApplier,
720 root: NodeId,
721) -> Result<Option<LayoutTree>, NodeError> {
722 fn snapshot(
723 applier: &mut MemoryApplier,
724 node_id: NodeId,
725 ) -> Result<Option<(crate::widgets::nodes::layout_node::LayoutState, Vec<NodeId>)>, NodeError>
726 {
727 match applier.with_node::<LayoutNode, _>(node_id, |node| {
728 (node.layout_state(), node.children.clone())
729 }) {
730 Ok(snapshot) => return Ok(Some(snapshot)),
731 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {}
732 Err(err) => return Err(err),
733 }
734
735 match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
736 (node.layout_state(), node.active_children())
737 }) {
738 Ok(snapshot) => Ok(Some(snapshot)),
739 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => Ok(None),
740 Err(err) => Err(err),
741 }
742 }
743
744 fn place(
745 applier: &mut MemoryApplier,
746 node_id: NodeId,
747 parent_content_origin: Point,
748 parent_layer_translation: Point,
754 ) -> Result<Option<LayoutBox>, NodeError> {
755 let Some((state, child_ids)) = snapshot(applier, node_id)? else {
756 return Ok(None);
757 };
758 if !state.is_placed {
759 return Ok(None);
760 }
761
762 let top_left = Point {
763 x: parent_content_origin.x + state.position.x,
764 y: parent_content_origin.y + state.position.y,
765 };
766 let rect = GeometryRect {
767 x: top_left.x,
768 y: top_left.y,
769 width: state.size.width,
770 height: state.size.height,
771 };
772 let info = runtime_metadata_for(applier, node_id)?;
773 let kind = layout_kind_from_metadata(node_id, &info);
774 let RuntimeNodeMetadata {
775 modifier,
776 resolved_modifiers,
777 modifier_slices,
778 ..
779 } = info;
780
781 let layer_translation = match modifier_slices.graphics_layer() {
786 Some(layer) => Point {
787 x: parent_layer_translation.x + layer.translation_x,
788 y: parent_layer_translation.y + layer.translation_y,
789 },
790 None => parent_layer_translation,
791 };
792
793 if let Some(sink) = modifier_slices.text_field_window_origin() {
797 sink.set(Point {
798 x: top_left.x + layer_translation.x,
799 y: top_left.y + layer_translation.y,
800 });
801 }
802
803 if let Some(sink) = modifier_slices.viewport_window_rect() {
807 sink.set(GeometryRect {
808 x: top_left.x + layer_translation.x,
809 y: top_left.y + layer_translation.y,
810 width: state.size.width,
811 height: state.size.height,
812 });
813 }
814
815 modifier_slices.publish_pointer_input_size(state.size);
819
820 let data = LayoutNodeData::new(modifier, resolved_modifiers, modifier_slices, kind);
821 let child_origin = Point {
822 x: top_left.x + state.content_offset.x,
823 y: top_left.y + state.content_offset.y,
824 };
825 let mut children = Vec::with_capacity(child_ids.len());
826 for child_id in child_ids {
827 if let Some(child) = place(applier, child_id, child_origin, layer_translation)? {
828 children.push(child);
829 }
830 }
831
832 Ok(Some(LayoutBox::new(
833 node_id,
834 rect,
835 state.content_offset,
836 data,
837 children,
838 )))
839 }
840
841 place(applier, root, Point::default(), Point::default()).map(|root| root.map(LayoutTree::new))
842}
843
844pub fn build_semantics_tree_from_applier(
850 applier: &mut MemoryApplier,
851 root: NodeId,
852) -> Result<Option<SemanticsTree>, NodeError> {
853 fn node(
854 applier: &mut MemoryApplier,
855 node_id: NodeId,
856 ) -> Result<Option<SemanticsNode>, NodeError> {
857 match applier.with_node::<LayoutNode, _>(node_id, |layout| {
858 let state = layout.layout_state();
859 if !state.is_placed {
860 return None;
861 }
862 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
863 let config = layout.semantics_configuration();
864 let children = layout.children.clone();
865 layout.clear_needs_semantics();
866 Some((role, config, children))
867 }) {
868 Ok(Some((role, config, child_ids))) => {
869 let mut children = Vec::with_capacity(child_ids.len());
870 for child_id in child_ids {
871 if let Some(child) = node(applier, child_id)? {
872 children.push(child);
873 }
874 }
875 return Ok(Some(semantics_node_from_parts(
876 node_id, role, config, children,
877 )));
878 }
879 Ok(None) => return Ok(None),
880 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {}
881 Err(err) => return Err(err),
882 }
883
884 match applier.with_node::<SubcomposeLayoutNode, _>(node_id, |subcompose| {
885 let state = subcompose.layout_state();
886 if !state.is_placed {
887 return None;
888 }
889 let config = collect_semantics_from_modifier(&subcompose.modifier());
890 let children = subcompose.active_children();
891 subcompose.clear_needs_semantics();
892 Some((config, children))
893 }) {
894 Ok(Some((config, child_ids))) => {
895 let mut children = Vec::with_capacity(child_ids.len());
896 for child_id in child_ids {
897 if let Some(child) = node(applier, child_id)? {
898 children.push(child);
899 }
900 }
901 Ok(Some(semantics_node_from_parts(
902 node_id,
903 SemanticsRole::Subcompose,
904 config,
905 children,
906 )))
907 }
908 Ok(None) | Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {
909 Ok(None)
910 }
911 Err(err) => Err(err),
912 }
913 }
914
915 node(applier, root).map(|root| root.map(SemanticsTree::new))
916}
917
918#[derive(Clone, Copy, Debug, PartialEq, Eq)]
919pub struct MeasureLayoutOptions {
920 pub collect_semantics: bool,
921 pub build_layout_tree: bool,
922}
923
924impl Default for MeasureLayoutOptions {
925 fn default() -> Self {
926 Self {
927 collect_semantics: true,
928 build_layout_tree: true,
929 }
930 }
931}
932
933pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
941 Ok(applier.get_mut(root)?.needs_layout())
942}
943
944pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
950 Ok(applier.get_mut(root)?.needs_semantics())
951}
952
953#[cfg(test)]
955pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
956 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
957}
958
959pub fn measure_layout(
961 applier: &mut MemoryApplier,
962 root: NodeId,
963 max_size: Size,
964) -> Result<LayoutMeasurements, NodeError> {
965 measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
966}
967
968pub fn measure_layout_with_options(
969 applier: &mut MemoryApplier,
970 root: NodeId,
971 max_size: Size,
972 options: MeasureLayoutOptions,
973) -> Result<LayoutMeasurements, NodeError> {
974 let telemetry_start = Instant::now();
975 process_pending_layout_repasses(applier, root)?;
976 let after_repasses = Instant::now();
977
978 let constraints = Constraints {
979 min_width: 0.0,
980 max_width: max_size.width,
981 min_height: 0.0,
982 max_height: max_size.height,
983 };
984
985 let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
996 .with_node::<LayoutNode, _>(root, |node| {
997 (
998 node.needs_measure(), node.needs_semantics(),
1000 node.cache_handles().epoch(),
1001 )
1002 }) {
1003 Ok(tuple) => tuple,
1004 Err(NodeError::TypeMismatch { .. }) => {
1005 let node = applier.get_mut(root)?;
1006 let measure_dirty = node.needs_measure();
1010 let semantics_dirty = node.needs_semantics();
1011 (measure_dirty, semantics_dirty, 0)
1012 }
1013 Err(err) => return Err(err),
1014 };
1015
1016 let epoch = if needs_remeasure {
1017 crate::render_state::next_layout_cache_epoch()
1018 } else if cached_epoch != 0 {
1019 cached_epoch
1020 } else {
1021 crate::render_state::current_layout_cache_epoch()
1023 };
1024
1025 let guard = ApplierSlotGuard::new(applier);
1033 let applier_host = guard.host();
1034 let slots_handle = guard.slots_handle();
1035 let after_guard = Instant::now();
1036
1037 let frame_arena = crate::render_state::take_layout_frame_arena();
1040 let mut builder = LayoutBuilder::new_with_epoch(
1041 Rc::clone(&applier_host),
1042 epoch,
1043 Rc::clone(&slots_handle),
1044 frame_arena,
1045 );
1046 let after_builder = Instant::now();
1047
1048 let measured = builder.measure_node(root, normalize_constraints(constraints))?;
1053 let after_measure = Instant::now();
1054
1055 if let Ok(mut applier) = applier_host.try_borrow_typed() {
1059 if applier
1060 .with_node::<LayoutNode, _>(root, |node| {
1061 node.set_position(Point::default());
1062 })
1063 .is_err()
1064 {
1065 let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
1066 node.set_position(Point::default());
1067 });
1068 }
1069 }
1070 let after_root_place = Instant::now();
1071
1072 let (layout_tree, semantics) = {
1073 let mut applier_ref = applier_host.borrow_typed();
1074 let layout_tree = if options.build_layout_tree {
1075 Some(build_layout_tree(&mut applier_ref, &measured)?)
1076 } else {
1077 None
1078 };
1079 let semantics = if options.collect_semantics {
1080 let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
1081 clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
1082 build_semantics_tree_from_layout_tree(layout_tree)
1083 } else {
1084 build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
1085 };
1086 Some(semantics_tree)
1087 } else {
1088 None
1089 };
1090 (layout_tree, semantics)
1091 };
1092 let after_aux = Instant::now();
1093
1094 drop(builder);
1097 let after_builder_drop = Instant::now();
1098
1099 drop(guard);
1102 let after_guard_drop = Instant::now();
1103
1104 log_layout_measure_telemetry(LayoutMeasureTelemetry {
1105 root,
1106 start: telemetry_start,
1107 after_repasses,
1108 after_guard,
1109 after_builder,
1110 after_measure,
1111 after_root_place,
1112 after_aux,
1113 after_builder_drop,
1114 after_guard_drop,
1115 });
1116
1117 Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
1118}
1119
1120fn process_pending_layout_repasses(
1121 applier: &mut MemoryApplier,
1122 root: NodeId,
1123) -> Result<(), NodeError> {
1124 for node_id in crate::render_state::take_modifier_slice_repass_nodes() {
1125 if let Ok(node) = applier.get_mut(node_id) {
1126 let any = node.as_any_mut();
1127 if let Some(layout) = any.downcast_mut::<crate::widgets::nodes::LayoutNode>() {
1128 layout.mark_modifier_slices_dirty();
1129 } else if let Some(subcompose) =
1130 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1131 {
1132 subcompose.mark_modifier_slices_dirty();
1133 }
1134 }
1135 }
1136 let measure_repass_nodes = crate::take_measure_repass_nodes();
1139 let repass_nodes = crate::take_layout_repass_nodes();
1140 if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
1141 return Ok(());
1142 }
1143 for node_id in measure_repass_nodes {
1144 cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
1145 }
1146 for node_id in repass_nodes {
1147 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
1148 }
1149 applier.get_mut(root)?.mark_needs_layout();
1150 Ok(())
1151}
1152
1153struct LayoutBuilder {
1154 state: Rc<RefCell<LayoutBuilderState>>,
1155}
1156
1157impl LayoutBuilder {
1158 fn new_with_epoch(
1159 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1160 epoch: u64,
1161 slots: Rc<RefCell<SlotTable>>,
1162 frame_arena: FrameLayoutArena,
1163 ) -> Self {
1164 Self {
1165 state: Rc::new(RefCell::new(LayoutBuilderState::new_with_epoch(
1166 applier,
1167 epoch,
1168 slots,
1169 frame_arena,
1170 ))),
1171 }
1172 }
1173
1174 fn measure_node(
1175 &mut self,
1176 node_id: NodeId,
1177 constraints: Constraints,
1178 ) -> Result<Rc<MeasuredNode>, NodeError> {
1179 LayoutBuilderState::measure_node(Rc::clone(&self.state), node_id, constraints)
1180 }
1181
1182 fn set_runtime_handle(&mut self, handle: Option<RuntimeHandle>) {
1183 self.state.borrow_mut().runtime_handle = handle;
1184 }
1185}
1186
1187impl Drop for LayoutBuilder {
1188 fn drop(&mut self) {
1189 if Rc::strong_count(&self.state) != 1 {
1190 return;
1191 }
1192 let Ok(mut state) = self.state.try_borrow_mut() else {
1193 return;
1194 };
1195 crate::render_state::replace_layout_frame_arena(std::mem::take(&mut state.frame_arena));
1196 }
1197}
1198
1199struct LayoutBuilderState {
1200 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1201 runtime_handle: Option<RuntimeHandle>,
1202 slots: Rc<RefCell<SlotTable>>,
1205 cache_epoch: u64,
1206 frame_arena: FrameLayoutArena,
1207}
1208
1209struct LayoutRuntimeFrameBindingCleanup {
1210 state: Rc<RefCell<LayoutRuntimeState>>,
1211}
1212
1213impl LayoutRuntimeFrameBindingCleanup {
1214 fn new(state: Rc<RefCell<LayoutRuntimeState>>) -> Self {
1215 Self { state }
1216 }
1217}
1218
1219impl Drop for LayoutRuntimeFrameBindingCleanup {
1220 fn drop(&mut self) {
1221 self.state.borrow().clear_frame_bindings();
1222 }
1223}
1224
1225impl LayoutBuilderState {
1226 fn new_with_epoch(
1227 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1228 epoch: u64,
1229 slots: Rc<RefCell<SlotTable>>,
1230 frame_arena: FrameLayoutArena,
1231 ) -> Self {
1232 let runtime_handle = applier.borrow_typed().runtime_handle();
1233
1234 Self {
1235 applier,
1236 runtime_handle,
1237 slots,
1238 cache_epoch: epoch,
1239 frame_arena,
1240 }
1241 }
1242
1243 fn try_with_applier_result<R>(
1244 state_rc: &Rc<RefCell<Self>>,
1245 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1246 ) -> Option<Result<R, NodeError>> {
1247 let host = {
1248 let state = state_rc.borrow();
1249 Rc::clone(&state.applier)
1250 };
1251
1252 let Ok(mut applier) = host.try_borrow_typed() else {
1254 return None;
1255 };
1256
1257 Some(f(&mut applier))
1258 }
1259
1260 fn with_applier_result<R>(
1261 state_rc: &Rc<RefCell<Self>>,
1262 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1263 ) -> Result<R, NodeError> {
1264 Self::try_with_applier_result(state_rc, f).unwrap_or_else(|| {
1265 Err(NodeError::MissingContext {
1266 id: NodeId::default(),
1267 reason: "applier already borrowed",
1268 })
1269 })
1270 }
1271
1272 fn clear_node_placed(state_rc: &Rc<RefCell<Self>>, node_id: NodeId) {
1275 let host = {
1276 let state = state_rc.borrow();
1277 Rc::clone(&state.applier)
1278 };
1279 let Ok(mut applier) = host.try_borrow_typed() else {
1280 return;
1281 };
1282 if applier
1284 .with_node::<LayoutNode, _>(node_id, |node| {
1285 node.clear_placed();
1286 })
1287 .is_err()
1288 {
1289 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1290 node.clear_placed();
1291 });
1292 }
1293 }
1294
1295 fn measure_node(
1296 state_rc: Rc<RefCell<Self>>,
1297 node_id: NodeId,
1298 constraints: Constraints,
1299 ) -> Result<Rc<MeasuredNode>, NodeError> {
1300 let telemetry_start = Instant::now();
1301 Self::clear_node_placed(&state_rc, node_id);
1305
1306 if let Some(subcompose) =
1308 Self::try_measure_subcompose(Rc::clone(&state_rc), node_id, constraints)?
1309 {
1310 log_node_measure_telemetry(
1311 "subcompose",
1312 node_id,
1313 constraints,
1314 subcompose.size,
1315 subcompose.children.len(),
1316 telemetry_start,
1317 );
1318 return Ok(subcompose);
1319 }
1320
1321 if let Some(result) = Self::try_with_applier_result(&state_rc, |applier| {
1323 match applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1324 LayoutNodeSnapshot::from_layout_node(layout_node)
1325 }) {
1326 Ok(snapshot) => Ok(Some(snapshot)),
1327 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => Ok(None),
1328 Err(err) => Err(err),
1329 }
1330 }) {
1331 if let Some(snapshot) = result? {
1333 let measured = Self::measure_layout_node(
1334 Rc::clone(&state_rc),
1335 node_id,
1336 snapshot,
1337 constraints,
1338 )?;
1339 log_node_measure_telemetry(
1340 "layout",
1341 node_id,
1342 constraints,
1343 measured.size,
1344 measured.children.len(),
1345 telemetry_start,
1346 );
1347 return Ok(measured);
1348 }
1349 }
1350 let measured = Rc::new(MeasuredNode::new(
1355 node_id,
1356 Size::default(),
1357 Point { x: 0.0, y: 0.0 },
1358 Point::default(), Vec::new(),
1360 ));
1361 log_node_measure_telemetry(
1362 "fallback",
1363 node_id,
1364 constraints,
1365 measured.size,
1366 measured.children.len(),
1367 telemetry_start,
1368 );
1369 Ok(measured)
1370 }
1371
1372 fn cached_measure_node_with_applier(
1373 applier: &mut MemoryApplier,
1374 node_id: NodeId,
1375 constraints: Constraints,
1376 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1377 let Some(data) = Self::layout_child_measure_data(applier, node_id)? else {
1378 return Ok(None);
1379 };
1380 if data.needs_measure || data.cache.epoch() == 0 {
1381 return Ok(None);
1382 }
1383
1384 let Some(measured) = data.cache.get_measurement(constraints) else {
1385 return Ok(None);
1386 };
1387
1388 if let Some(layout_state) = data.layout_state {
1389 let mut layout_state = layout_state.borrow_mut();
1390 layout_state.size = measured.size;
1391 layout_state.measurement_constraints = constraints;
1392 drop(layout_state);
1393 let _ = applier.with_node::<LayoutNode, _>(node_id, |node| {
1394 if data.needs_layout {
1395 node.clear_needs_layout();
1396 }
1397 });
1398 } else {
1399 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1400 node.set_measured_size(measured.size);
1401 if data.needs_layout {
1402 node.clear_needs_layout();
1403 }
1404 });
1405 }
1406
1407 Ok(Some(measured))
1408 }
1409
1410 fn try_measure_subcompose(
1411 state_rc: Rc<RefCell<Self>>,
1412 node_id: NodeId,
1413 constraints: Constraints,
1414 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1415 let applier_host = {
1416 let state = state_rc.borrow();
1417 Rc::clone(&state.applier)
1418 };
1419
1420 let (node_handle, resolved_modifiers) = {
1421 let Ok(mut applier) = applier_host.try_borrow_typed() else {
1423 return Ok(None);
1424 };
1425 let node = match applier.get_mut(node_id) {
1426 Ok(node) => node,
1427 Err(NodeError::Missing { .. }) => return Ok(None),
1428 Err(err) => return Err(err),
1429 };
1430 let any = node.as_any_mut();
1431 if let Some(subcompose) =
1432 any.downcast_mut::<crate::subcompose_layout::SubcomposeLayoutNode>()
1433 {
1434 let handle = subcompose.handle();
1435 let resolved_modifiers = handle.resolved_modifiers();
1436 (handle, resolved_modifiers)
1437 } else {
1438 return Ok(None);
1439 }
1440 };
1441
1442 let runtime_handle = {
1443 let mut state = state_rc.borrow_mut();
1444 if state.runtime_handle.is_none() {
1445 if let Ok(applier) = applier_host.try_borrow_typed() {
1447 state.runtime_handle = applier.runtime_handle();
1448 }
1449 }
1450 state
1451 .runtime_handle
1452 .clone()
1453 .ok_or(NodeError::MissingContext {
1454 id: node_id,
1455 reason: "runtime handle required for subcomposition",
1456 })?
1457 };
1458
1459 let props = resolved_modifiers.layout_properties();
1460 let padding = resolved_modifiers.padding();
1461 let offset = resolved_modifiers.offset();
1462 let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1463
1464 if let DimensionConstraint::Points(width) = props.width() {
1465 let constrained_width = width - padding.horizontal_sum();
1466 inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1467 inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1468 }
1469 if let DimensionConstraint::Points(height) = props.height() {
1470 let constrained_height = height - padding.vertical_sum();
1471 inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1472 inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1473 }
1474
1475 let mut slots_guard = SlotsGuard::take(Rc::clone(&state_rc));
1476 let slots_host = slots_guard.host();
1477 let applier_host_dyn: Rc<dyn ApplierHost> = applier_host.clone();
1478 let observer = SnapshotStateObserver::new(|callback| callback());
1479 let composer = Composer::new(
1480 Rc::clone(&slots_host),
1481 applier_host_dyn,
1482 runtime_handle.clone(),
1483 observer,
1484 Some(node_id),
1485 );
1486 composer.enter_phase(Phase::Measure);
1487
1488 let state_rc_clone = Rc::clone(&state_rc);
1489 let measure_error = RefCell::new(None);
1490 let state_rc_for_subcompose = Rc::clone(&state_rc_clone);
1491 let error_for_subcompose = &measure_error;
1492 let measured_children = node_handle.measured_children_scratch();
1493 let measured_children_for_subcompose = Rc::clone(&measured_children);
1494 let state_rc_for_cached = Rc::clone(&state_rc_clone);
1495 let error_for_cached = &measure_error;
1496 let measured_children_for_cached = Rc::clone(&measured_children);
1497 let measured_children_for_lookup = Rc::clone(&measured_children);
1498 let measured_children_for_retained = Rc::clone(&measured_children);
1499
1500 let measure_result = node_handle.measure_with_cached_batch(
1501 &composer,
1502 node_id,
1503 inner_constraints,
1504 CachedBatchMeasureInputs {
1505 measurer: Box::new(
1506 move |child_id: NodeId, child_constraints: Constraints| -> Size {
1507 match Self::measure_node(
1508 Rc::clone(&state_rc_for_subcompose),
1509 child_id,
1510 child_constraints,
1511 ) {
1512 Ok(measured) => {
1513 measured_children_for_subcompose
1514 .borrow_mut()
1515 .insert(child_id, Rc::clone(&measured));
1516 measured.size
1517 }
1518 Err(err) => {
1519 let mut slot = error_for_subcompose.borrow_mut();
1520 if slot.is_none() {
1521 *slot = Some(err);
1522 }
1523 Size::default()
1524 }
1525 }
1526 },
1527 ),
1528 cached_measure_batch_registrar: Box::new(
1529 move |child_ids: &[NodeId],
1530 child_constraints: Constraints,
1531 out: &mut Vec<Option<Size>>| {
1532 out.clear();
1533 out.resize(child_ids.len(), None);
1534
1535 let applier_host = {
1536 let state = state_rc_for_cached.borrow();
1537 Rc::clone(&state.applier)
1538 };
1539 let Ok(mut applier) = applier_host.try_borrow_typed() else {
1540 return;
1541 };
1542
1543 let mut measured_children = measured_children_for_cached.borrow_mut();
1544 for (index, &child_id) in child_ids.iter().enumerate() {
1545 match Self::cached_measure_node_with_applier(
1546 &mut applier,
1547 child_id,
1548 child_constraints,
1549 ) {
1550 Ok(Some(measured)) => {
1551 out[index] = Some(measured.size);
1552 measured_children.insert(child_id, Rc::clone(&measured));
1553 }
1554 Ok(None) => {}
1555 Err(err) => {
1556 let mut slot = error_for_cached.borrow_mut();
1557 if slot.is_none() {
1558 *slot = Some(err);
1559 }
1560 break;
1561 }
1562 }
1563 }
1564 },
1565 ),
1566 retained_measure_lookup: Box::new(move |child_id| {
1567 measured_children_for_lookup
1568 .borrow()
1569 .get(&child_id)
1570 .cloned()
1571 }),
1572 retained_measure_registrar: Box::new(move |measurements| {
1573 let mut measured_children = measured_children_for_retained.borrow_mut();
1574 for measured in measurements {
1575 measured_children.insert(measured.node_id(), Rc::clone(measured));
1576 }
1577 }),
1578 error: &measure_error,
1579 },
1580 )?;
1581 drop(composer);
1582 slots_guard.restore(slots_host.into_table()?);
1583
1584 if let Some(err) = measure_error.borrow_mut().take() {
1585 return Err(err);
1586 }
1587
1588 let cranpose_ui_layout::MeasureResult {
1592 size: measured_size,
1593 placements,
1594 } = measure_result;
1595
1596 let mut width = measured_size.width + padding.horizontal_sum();
1597 let mut height = measured_size.height + padding.vertical_sum();
1598
1599 width = resolve_dimension(
1600 width,
1601 props.width(),
1602 props.min_width(),
1603 props.max_width(),
1604 constraints.min_width,
1605 constraints.max_width,
1606 );
1607 height = resolve_dimension(
1608 height,
1609 props.height(),
1610 props.min_height(),
1611 props.max_height(),
1612 constraints.min_height,
1613 constraints.max_height,
1614 );
1615
1616 let mut children = Vec::with_capacity(placements.len());
1617 let mut measured_children_by_id = measured_children.borrow_mut();
1618
1619 if let Ok(mut applier) = applier_host.try_borrow_typed() {
1621 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1622 parent_node.set_measured_size(Size { width, height });
1623 parent_node.clear_needs_measure();
1624 parent_node.clear_needs_layout();
1625 });
1626 }
1627
1628 for placement in &placements {
1629 let child = if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1630 measured
1631 } else {
1632 Self::measure_node(Rc::clone(&state_rc), placement.node_id, inner_constraints)?
1638 };
1639 let policy_position = Point {
1640 x: padding.left + placement.x,
1641 y: padding.top + placement.y,
1642 };
1643 let retained_position = Point {
1650 x: policy_position.x + child.offset.x,
1651 y: policy_position.y + child.offset.y,
1652 };
1653
1654 if let Ok(mut applier) = applier_host.try_borrow_typed() {
1662 if applier
1663 .with_node::<LayoutNode, _>(placement.node_id, |node| {
1664 node.set_position(retained_position);
1665 })
1666 .is_err()
1667 {
1668 let _ =
1669 applier.with_node::<SubcomposeLayoutNode, _>(placement.node_id, |node| {
1670 node.set_position(retained_position);
1671 });
1672 }
1673 }
1674
1675 children.push(MeasuredChild {
1676 node: child,
1677 offset: policy_position,
1678 });
1679 }
1680
1681 node_handle.set_active_children(children.iter().map(|c| c.node.node_id));
1683 node_handle.recycle_placement_scratch(placements);
1684
1685 Ok(Some(Rc::new(MeasuredNode::new(
1686 node_id,
1687 Size { width, height },
1688 offset,
1689 Point::default(), children,
1691 ))))
1692 }
1693 fn measure_through_modifier_chain(
1700 state_rc: &Rc<RefCell<Self>>,
1701 node_id: NodeId,
1702 runtime_state: &mut LayoutRuntimeState,
1703 measure_policy: &Rc<dyn MeasurePolicy>,
1704 constraints: Constraints,
1705 layout_node_data: &mut Vec<LayoutModifierNodeData>,
1706 placements: &mut Vec<Placement>,
1707 ) -> ModifierChainMeasurement {
1708 use cranpose_foundation::NodeCapabilities;
1709
1710 layout_node_data.clear();
1712 let mut offset = Point::default();
1713
1714 {
1715 let state = state_rc.borrow();
1716 let mut applier = state.applier.borrow_typed();
1717
1718 let _ = applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1719 let chain_handle = layout_node.modifier_chain();
1720
1721 if !chain_handle.has_layout_nodes() {
1722 return;
1723 }
1724
1725 chain_handle.chain().for_each_forward_matching(
1727 NodeCapabilities::LAYOUT,
1728 |node_ref| {
1729 if let Some(index) = node_ref.entry_index() {
1730 if let Some(node_rc) = chain_handle.chain().get_node_rc(index) {
1732 layout_node_data.push((index, Rc::clone(&node_rc)));
1733 }
1734
1735 node_ref.with_node(|node| {
1739 if let Some(offset_node) =
1740 node.as_any()
1741 .downcast_ref::<crate::modifier_nodes::OffsetNode>()
1742 {
1743 let delta = offset_node.offset();
1744 offset.x += delta.x;
1745 offset.y += delta.y;
1746 }
1747 });
1748 }
1749 },
1750 );
1751 });
1752 }
1753
1754 if layout_node_data.is_empty() {
1757 let final_size = measure_policy.measure_into(
1758 runtime_state.child_measurables(),
1759 constraints,
1760 placements,
1761 );
1762
1763 return ModifierChainMeasurement {
1764 size: final_size,
1765 content_offset: Point::default(),
1766 offset,
1767 };
1768 }
1769
1770 runtime_state.reconcile_coordinator_chain(layout_node_data.as_slice());
1771 let frame = CoordinatorFrame::new(
1772 measure_policy,
1773 runtime_state.child_measurables(),
1774 placements,
1775 );
1776
1777 let placeable = runtime_state
1779 .coordinator_chain()
1780 .measure_from(0, &frame, constraints);
1781 let final_size = Size {
1782 width: placeable.width(),
1783 height: placeable.height(),
1784 };
1785
1786 let content_offset = placeable.content_offset();
1788 let all_placement_offset = Point {
1789 x: content_offset.0,
1790 y: content_offset.1,
1791 };
1792
1793 let content_offset = Point {
1797 x: all_placement_offset.x - offset.x,
1798 y: all_placement_offset.y - offset.y,
1799 };
1800
1801 let invalidations = frame.take_invalidations();
1805 if !invalidations.is_empty() {
1806 Self::with_applier_result(state_rc, |applier| {
1808 applier.with_node::<LayoutNode, _>(node_id, |layout_node| {
1809 for kind in invalidations {
1810 match kind {
1811 InvalidationKind::Layout => layout_node.mark_needs_measure(),
1812 InvalidationKind::Draw => layout_node.mark_needs_redraw(),
1813 InvalidationKind::Semantics => layout_node.mark_needs_semantics(),
1814 InvalidationKind::PointerInput => layout_node.mark_needs_pointer_pass(),
1815 InvalidationKind::Focus => layout_node.mark_needs_focus_sync(),
1816 }
1817 }
1818 })
1819 })
1820 .ok();
1821 }
1822
1823 ModifierChainMeasurement {
1824 size: final_size,
1825 content_offset,
1826 offset,
1827 }
1828 }
1829
1830 fn layout_child_measure_data(
1831 applier: &mut MemoryApplier,
1832 child_id: NodeId,
1833 ) -> Result<Option<LayoutChildMeasureData>, NodeError> {
1834 match applier.with_node::<LayoutNode, _>(child_id, |n| LayoutChildMeasureData {
1835 cache: n.cache_handles(),
1836 layout_state: Some(n.layout_state_handle()),
1837 needs_layout: n.needs_layout(),
1838 needs_measure: n.needs_measure(),
1839 }) {
1840 Ok(data) => Ok(Some(data)),
1841 Err(NodeError::TypeMismatch { .. }) => {
1842 match applier.with_node::<SubcomposeLayoutNode, _>(child_id, |n| {
1843 LayoutChildMeasureData {
1844 cache: n.cache_handles(),
1845 layout_state: None,
1846 needs_layout: n.needs_layout(),
1847 needs_measure: n.needs_measure(),
1848 }
1849 }) {
1850 Ok(data) => Ok(Some(data)),
1851 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {
1852 Ok(None)
1853 }
1854 Err(err) => Err(err),
1855 }
1856 }
1857 Err(NodeError::Missing { .. }) => Ok(None),
1858 Err(err) => Err(err),
1859 }
1860 }
1861
1862 fn measure_layout_node(
1863 state_rc: Rc<RefCell<Self>>,
1864 node_id: NodeId,
1865 snapshot: LayoutNodeSnapshot,
1866 constraints: Constraints,
1867 ) -> Result<Rc<MeasuredNode>, NodeError> {
1868 let cache_epoch = {
1869 let state = state_rc.borrow();
1870 state.cache_epoch
1871 };
1872 let LayoutNodeSnapshot {
1873 measure_policy,
1874 cache,
1875 layout_runtime_state,
1876 needs_layout,
1877 needs_measure,
1878 } = snapshot;
1879 cache.activate(cache_epoch);
1880
1881 if needs_measure {
1882 }
1884
1885 if !needs_measure && !needs_layout {
1889 if let Some(cached) = cache.get_measurement(constraints) {
1891 Self::with_applier_result(&state_rc, |applier| {
1893 applier.with_node::<LayoutNode, _>(node_id, |node| {
1894 node.clear_needs_measure();
1895 node.clear_needs_layout();
1896 })
1897 })
1898 .ok();
1899 return Ok(cached);
1900 }
1901 }
1902
1903 let (runtime_handle, applier_host) = {
1904 let state = state_rc.borrow();
1905 (state.runtime_handle.clone(), Rc::clone(&state.applier))
1906 };
1907
1908 let measure_handle = LayoutMeasureHandle::new(Rc::clone(&state_rc));
1909 let error = Rc::new(RefCell::new(None));
1910 let mut pools = VecPools::acquire(Rc::clone(&state_rc));
1911 let (records, child_ids, layout_node_data, placements) = pools.parts();
1912
1913 applier_host
1914 .borrow_typed()
1915 .with_node::<LayoutNode, _>(node_id, |node| {
1916 child_ids.extend_from_slice(&node.children);
1917 })?;
1918
1919 let mut valid_child_count = 0;
1920 for index in 0..child_ids.len() {
1921 let child_id = child_ids[index];
1922 let child_exists = {
1923 let mut applier = applier_host.borrow_typed();
1924 Self::layout_child_measure_data(&mut applier, child_id)?.is_some()
1925 };
1926 if child_exists {
1927 child_ids[valid_child_count] = child_id;
1928 valid_child_count += 1;
1929 }
1930 }
1931 child_ids.truncate(valid_child_count);
1932
1933 let _frame_binding_cleanup =
1934 LayoutRuntimeFrameBindingCleanup::new(Rc::clone(&layout_runtime_state));
1935
1936 {
1937 let mut runtime_state = layout_runtime_state.borrow_mut();
1938 runtime_state.reconcile_child_measurables(child_ids.as_slice());
1939
1940 for (index, &child_id) in child_ids.iter().enumerate() {
1941 let data = {
1942 let mut applier = applier_host.borrow_typed();
1943 Self::layout_child_measure_data(&mut applier, child_id)?
1944 };
1945 let Some(data) = data else {
1946 continue;
1947 };
1948
1949 let child_is_dirty = data.needs_layout || data.needs_measure;
1950 let child_cache_epoch = if child_is_dirty {
1951 cache_epoch
1952 } else {
1953 data.cache.epoch()
1954 };
1955 let child_state = runtime_state.child_state(index);
1956 child_state.configure(LayoutChildMeasureConfig {
1957 applier: Rc::clone(&applier_host),
1958 node_id: child_id,
1959 error: Rc::clone(&error),
1960 runtime_handle: runtime_handle.clone(),
1961 cache: data.cache,
1962 cache_epoch: child_cache_epoch,
1963 force_remeasure: child_is_dirty,
1964 measure_handle: Some(measure_handle.clone()),
1965 layout_state: data.layout_state,
1966 });
1967 records.push((child_id, ChildRecord { state: child_state }));
1968 }
1969 }
1970
1971 let chain_constraints = constraints;
1972
1973 let modifier_chain_result = {
1974 let mut runtime_state = layout_runtime_state.borrow_mut();
1975 Self::measure_through_modifier_chain(
1976 &state_rc,
1977 node_id,
1978 &mut runtime_state,
1979 &measure_policy,
1980 chain_constraints,
1981 layout_node_data,
1982 placements,
1983 )
1984 };
1985
1986 let (width, height, content_offset, offset) = {
1988 let result = modifier_chain_result;
1989 if let Some(err) = error.borrow_mut().take() {
1992 return Err(err);
1993 }
1994
1995 (
1996 result.size.width,
1997 result.size.height,
1998 result.content_offset,
1999 result.offset,
2000 )
2001 };
2002
2003 let mut measured_children = Vec::with_capacity(records.len());
2004 for (child_id, record) in records.iter() {
2005 if let Some(measured) = record.state.take_measured() {
2006 let placed = placements
2007 .iter()
2008 .find(|placement| placement.node_id == *child_id)
2009 .map(|placement| Point {
2010 x: placement.x,
2011 y: placement.y,
2012 });
2013 if let Some(raw) = placed {
2034 record.state.place_retained(Point {
2035 x: raw.x + measured.offset.x,
2036 y: raw.y + measured.offset.y,
2037 });
2038 }
2039 let base_position = placed
2040 .or_else(|| record.state.last_position())
2041 .unwrap_or(Point { x: 0.0, y: 0.0 });
2042 let position = Point {
2044 x: content_offset.x + base_position.x,
2045 y: content_offset.y + base_position.y,
2046 };
2047 measured_children.push(MeasuredChild {
2048 node: measured,
2049 offset: position,
2050 });
2051 }
2052 }
2053
2054 let measured = Rc::new(MeasuredNode::new(
2055 node_id,
2056 Size { width, height },
2057 offset,
2058 content_offset,
2059 measured_children,
2060 ));
2061
2062 cache.store_measurement(constraints, Rc::clone(&measured));
2063
2064 Self::with_applier_result(&state_rc, |applier| {
2066 applier.with_node::<LayoutNode, _>(node_id, |node| {
2067 node.clear_needs_measure();
2068 node.clear_needs_layout();
2069 node.set_measured_size(Size { width, height });
2070 node.set_content_offset(content_offset);
2071 })
2072 })
2073 .ok();
2074
2075 Ok(measured)
2076 }
2077}
2078
2079struct LayoutChildMeasureData {
2080 cache: LayoutNodeCacheHandles,
2081 layout_state: Option<Rc<RefCell<LayoutState>>>,
2082 needs_layout: bool,
2083 needs_measure: bool,
2084}
2085
2086struct LayoutNodeSnapshot {
2093 measure_policy: Rc<dyn MeasurePolicy>,
2094 cache: LayoutNodeCacheHandles,
2095 layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
2096 needs_layout: bool,
2097 needs_measure: bool,
2099}
2100
2101impl LayoutNodeSnapshot {
2102 fn from_layout_node(node: &LayoutNode) -> Self {
2103 Self {
2104 measure_policy: Rc::clone(&node.measure_policy),
2105 cache: node.cache_handles(),
2106 layout_runtime_state: node.layout_runtime_state_handle(),
2107 needs_layout: node.needs_layout(),
2108 needs_measure: node.needs_measure(),
2109 }
2110 }
2111}
2112
2113struct VecPools {
2115 state: Rc<RefCell<LayoutBuilderState>>,
2116 records: Vec<(NodeId, ChildRecord)>,
2117 child_ids: Vec<NodeId>,
2118 layout_node_data: Vec<LayoutModifierNodeData>,
2119 placements: Vec<Placement>,
2120}
2121
2122impl VecPools {
2123 fn acquire(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2124 let (records, child_ids, layout_node_data, placements) = {
2125 let mut state_mut = state.borrow_mut();
2126 (
2127 state_mut.frame_arena.tmp_records.acquire(),
2128 state_mut.frame_arena.tmp_child_ids.acquire(),
2129 state_mut.frame_arena.tmp_layout_node_data.acquire(),
2130 state_mut.frame_arena.tmp_placements.acquire(),
2131 )
2132 };
2133 Self {
2134 state,
2135 records,
2136 child_ids,
2137 layout_node_data,
2138 placements,
2139 }
2140 }
2141
2142 #[allow(clippy::type_complexity)] fn parts(
2144 &mut self,
2145 ) -> (
2146 &mut Vec<(NodeId, ChildRecord)>,
2147 &mut Vec<NodeId>,
2148 &mut Vec<LayoutModifierNodeData>,
2149 &mut Vec<Placement>,
2150 ) {
2151 (
2152 &mut self.records,
2153 &mut self.child_ids,
2154 &mut self.layout_node_data,
2155 &mut self.placements,
2156 )
2157 }
2158}
2159
2160impl Drop for VecPools {
2161 fn drop(&mut self) {
2162 let mut state = self.state.borrow_mut();
2163 state
2164 .frame_arena
2165 .tmp_records
2166 .release(std::mem::take(&mut self.records));
2167 state
2168 .frame_arena
2169 .tmp_child_ids
2170 .release(std::mem::take(&mut self.child_ids));
2171 state
2172 .frame_arena
2173 .tmp_layout_node_data
2174 .release(std::mem::take(&mut self.layout_node_data));
2175 state
2176 .frame_arena
2177 .tmp_placements
2178 .release(std::mem::take(&mut self.placements));
2179 }
2180}
2181
2182struct SlotsGuard {
2183 state: Rc<RefCell<LayoutBuilderState>>,
2184 slots: Option<SlotTable>,
2185}
2186
2187impl SlotsGuard {
2188 fn take(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2189 let slots = {
2190 let state_ref = state.borrow();
2191 let mut slots_ref = state_ref.slots.borrow_mut();
2192 std::mem::take(&mut *slots_ref)
2193 };
2194 Self {
2195 state,
2196 slots: Some(slots),
2197 }
2198 }
2199
2200 fn host(&mut self) -> Rc<SlotsHost> {
2201 let slots = self.slots.take().unwrap_or_default();
2202 Rc::new(SlotsHost::new(slots))
2203 }
2204
2205 fn restore(&mut self, slots: SlotTable) {
2206 debug_assert!(self.slots.is_none());
2207 self.slots = Some(slots);
2208 }
2209}
2210
2211impl Drop for SlotsGuard {
2212 fn drop(&mut self) {
2213 if let Some(slots) = self.slots.take() {
2214 let state_ref = self.state.borrow();
2215 *state_ref.slots.borrow_mut() = slots;
2216 }
2217 }
2218}
2219
2220#[derive(Clone)]
2221struct LayoutMeasureHandle {
2222 state: Rc<RefCell<LayoutBuilderState>>,
2223}
2224
2225impl LayoutMeasureHandle {
2226 fn new(state: Rc<RefCell<LayoutBuilderState>>) -> Self {
2227 Self { state }
2228 }
2229
2230 fn measure(
2231 &self,
2232 node_id: NodeId,
2233 constraints: Constraints,
2234 ) -> Result<Rc<MeasuredNode>, NodeError> {
2235 LayoutBuilderState::measure_node(Rc::clone(&self.state), node_id, constraints)
2236 }
2237}
2238
2239#[derive(Debug, Clone)]
2240pub(crate) struct MeasuredNode {
2241 node_id: NodeId,
2242 size: Size,
2243 offset: Point,
2245 content_offset: Point,
2247 children: Vec<MeasuredChild>,
2248}
2249
2250impl MeasuredNode {
2251 fn new(
2252 node_id: NodeId,
2253 size: Size,
2254 offset: Point,
2255 content_offset: Point,
2256 children: Vec<MeasuredChild>,
2257 ) -> Self {
2258 Self {
2259 node_id,
2260 size,
2261 offset,
2262 content_offset,
2263 children,
2264 }
2265 }
2266
2267 #[cfg(test)]
2268 pub(crate) fn leaf(node_id: NodeId, size: Size) -> Self {
2269 Self::new(
2270 node_id,
2271 size,
2272 Point::default(),
2273 Point::default(),
2274 Vec::new(),
2275 )
2276 }
2277
2278 pub(crate) fn node_id(&self) -> NodeId {
2279 self.node_id
2280 }
2281
2282 pub(crate) fn size(&self) -> Size {
2283 self.size
2284 }
2285}
2286
2287#[derive(Debug, Clone)]
2288struct MeasuredChild {
2289 node: Rc<MeasuredNode>,
2290 offset: Point,
2291}
2292
2293struct ChildRecord {
2294 state: Rc<LayoutChildMeasureState>,
2295}
2296
2297struct CoordinatorFrame<'a> {
2298 measure_policy: &'a Rc<dyn MeasurePolicy>,
2299 measurables: &'a [Box<dyn Measurable>],
2300 placements: RefCell<&'a mut Vec<Placement>>,
2301 context: RefCell<LayoutNodeContext>,
2302}
2303
2304impl<'a> CoordinatorFrame<'a> {
2305 fn new(
2306 measure_policy: &'a Rc<dyn MeasurePolicy>,
2307 measurables: &'a [Box<dyn Measurable>],
2308 placements: &'a mut Vec<Placement>,
2309 ) -> Self {
2310 Self {
2311 measure_policy,
2312 measurables,
2313 placements: RefCell::new(placements),
2314 context: RefCell::new(LayoutNodeContext::new()),
2315 }
2316 }
2317
2318 fn take_invalidations(&self) -> Vec<InvalidationKind> {
2319 self.context.borrow_mut().take_invalidations()
2320 }
2321}
2322
2323struct CoordinatorLink<'chain, 'frame_ref, 'frame_data> {
2324 chain: &'chain CoordinatorChain,
2325 frame: &'frame_ref CoordinatorFrame<'frame_data>,
2326 index: usize,
2327}
2328
2329impl Measurable for CoordinatorLink<'_, '_, '_> {
2330 fn measure(&self, constraints: Constraints) -> Placeable {
2331 self.chain.measure_from(self.index, self.frame, constraints)
2332 }
2333
2334 fn min_intrinsic_width(&self, height: f32) -> f32 {
2335 self.chain
2336 .min_intrinsic_width_from(self.index, self.frame, height)
2337 }
2338
2339 fn max_intrinsic_width(&self, height: f32) -> f32 {
2340 self.chain
2341 .max_intrinsic_width_from(self.index, self.frame, height)
2342 }
2343
2344 fn min_intrinsic_height(&self, width: f32) -> f32 {
2345 self.chain
2346 .min_intrinsic_height_from(self.index, self.frame, width)
2347 }
2348
2349 fn max_intrinsic_height(&self, width: f32) -> f32 {
2350 self.chain
2351 .max_intrinsic_height_from(self.index, self.frame, width)
2352 }
2353}
2354
2355struct CoordinatorNode {
2356 modifier_index: usize,
2357 node: Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2358 measured_size: Cell<Size>,
2359 accumulated_offset: Cell<Point>,
2360}
2361
2362impl CoordinatorNode {
2363 fn new(
2364 modifier_index: usize,
2365 node: Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2366 ) -> Self {
2367 Self {
2368 modifier_index,
2369 node,
2370 measured_size: Cell::new(Size::default()),
2371 accumulated_offset: Cell::new(Point::default()),
2372 }
2373 }
2374
2375 fn matches(
2376 &self,
2377 modifier_index: usize,
2378 node: &Rc<RefCell<Box<dyn cranpose_foundation::ModifierNode>>>,
2379 ) -> bool {
2380 self.modifier_index == modifier_index && Rc::ptr_eq(&self.node, node)
2381 }
2382
2383 #[cfg(test)]
2384 fn ptr(&self) -> usize {
2385 Rc::as_ptr(&self.node) as *const () as usize
2386 }
2387}
2388
2389#[derive(Default)]
2390struct CoordinatorChain {
2391 nodes: Vec<CoordinatorNode>,
2392}
2393
2394impl CoordinatorChain {
2395 fn reconcile(&mut self, layout_node_data: &[LayoutModifierNodeData]) {
2396 if self.matches(layout_node_data) {
2397 return;
2398 }
2399
2400 let mut previous_nodes = std::mem::take(&mut self.nodes);
2401 self.nodes.reserve(layout_node_data.len());
2402
2403 for (modifier_index, node) in layout_node_data.iter() {
2404 if let Some(position) = previous_nodes
2405 .iter()
2406 .position(|candidate| candidate.matches(*modifier_index, node))
2407 {
2408 self.nodes.push(previous_nodes.swap_remove(position));
2409 } else {
2410 self.nodes
2411 .push(CoordinatorNode::new(*modifier_index, Rc::clone(node)));
2412 }
2413 }
2414 }
2415
2416 fn matches(&self, layout_node_data: &[LayoutModifierNodeData]) -> bool {
2417 self.nodes.len() == layout_node_data.len()
2418 && self
2419 .nodes
2420 .iter()
2421 .zip(layout_node_data.iter())
2422 .all(|(node, (modifier_index, node_rc))| node.matches(*modifier_index, node_rc))
2423 }
2424
2425 fn measure_from(
2426 &self,
2427 index: usize,
2428 frame: &CoordinatorFrame<'_>,
2429 constraints: Constraints,
2430 ) -> Placeable {
2431 let Some(node) = self.nodes.get(index) else {
2432 let mut placements = frame.placements.borrow_mut();
2433 let size =
2434 frame
2435 .measure_policy
2436 .measure_into(frame.measurables, constraints, &mut placements);
2437 return Placeable::value(size.width, size.height, NodeId::default());
2438 };
2439
2440 let wrapped = CoordinatorLink {
2441 chain: self,
2442 frame,
2443 index: index + 1,
2444 };
2445 let node_borrow = node.node.borrow();
2446
2447 let Some(layout_node) = node_borrow.as_layout_node() else {
2448 let placeable = wrapped.measure(constraints);
2449 let child_accumulated = self.total_content_offset_from(index + 1);
2450 node.accumulated_offset.set(child_accumulated);
2451 return Placeable::value_with_offset(
2452 placeable.width(),
2453 placeable.height(),
2454 NodeId::default(),
2455 (child_accumulated.x, child_accumulated.y),
2456 );
2457 };
2458
2459 let result = match frame.context.try_borrow_mut() {
2460 Ok(mut context) => layout_node.measure(&mut *context, &wrapped, constraints),
2461 Err(_) => {
2462 let mut temp = LayoutNodeContext::new();
2463 let result = layout_node.measure(&mut temp, &wrapped, constraints);
2464 if let Ok(mut context) = frame.context.try_borrow_mut() {
2465 for kind in temp.take_invalidations() {
2466 context.invalidate(kind);
2467 }
2468 }
2469 result
2470 }
2471 };
2472
2473 node.measured_size.set(result.size);
2474 let local_offset = Point {
2475 x: result.placement_offset_x,
2476 y: result.placement_offset_y,
2477 };
2478 let child_accumulated = self.total_content_offset_from(index + 1);
2479 let accumulated = Point {
2480 x: local_offset.x + child_accumulated.x,
2481 y: local_offset.y + child_accumulated.y,
2482 };
2483 node.accumulated_offset.set(accumulated);
2484
2485 Placeable::value_with_offset(
2486 result.size.width,
2487 result.size.height,
2488 NodeId::default(),
2489 (accumulated.x, accumulated.y),
2490 )
2491 }
2492
2493 fn min_intrinsic_width_from(
2494 &self,
2495 index: usize,
2496 frame: &CoordinatorFrame<'_>,
2497 height: f32,
2498 ) -> f32 {
2499 let Some(node) = self.nodes.get(index) else {
2500 return frame
2501 .measure_policy
2502 .min_intrinsic_width(frame.measurables, height);
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_width(&wrapped, height))
2513 .unwrap_or_else(|| wrapped.min_intrinsic_width(height))
2514 }
2515
2516 fn max_intrinsic_width_from(
2517 &self,
2518 index: usize,
2519 frame: &CoordinatorFrame<'_>,
2520 height: f32,
2521 ) -> f32 {
2522 let Some(node) = self.nodes.get(index) else {
2523 return frame
2524 .measure_policy
2525 .max_intrinsic_width(frame.measurables, height);
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_width(&wrapped, height))
2536 .unwrap_or_else(|| wrapped.max_intrinsic_width(height))
2537 }
2538
2539 fn min_intrinsic_height_from(
2540 &self,
2541 index: usize,
2542 frame: &CoordinatorFrame<'_>,
2543 width: f32,
2544 ) -> f32 {
2545 let Some(node) = self.nodes.get(index) else {
2546 return frame
2547 .measure_policy
2548 .min_intrinsic_height(frame.measurables, width);
2549 };
2550 let wrapped = CoordinatorLink {
2551 chain: self,
2552 frame,
2553 index: index + 1,
2554 };
2555 let node_borrow = node.node.borrow();
2556 node_borrow
2557 .as_layout_node()
2558 .map(|layout_node| layout_node.min_intrinsic_height(&wrapped, width))
2559 .unwrap_or_else(|| wrapped.min_intrinsic_height(width))
2560 }
2561
2562 fn max_intrinsic_height_from(
2563 &self,
2564 index: usize,
2565 frame: &CoordinatorFrame<'_>,
2566 width: f32,
2567 ) -> f32 {
2568 let Some(node) = self.nodes.get(index) else {
2569 return frame
2570 .measure_policy
2571 .max_intrinsic_height(frame.measurables, width);
2572 };
2573 let wrapped = CoordinatorLink {
2574 chain: self,
2575 frame,
2576 index: index + 1,
2577 };
2578 let node_borrow = node.node.borrow();
2579 node_borrow
2580 .as_layout_node()
2581 .map(|layout_node| layout_node.max_intrinsic_height(&wrapped, width))
2582 .unwrap_or_else(|| wrapped.max_intrinsic_height(width))
2583 }
2584
2585 fn total_content_offset_from(&self, index: usize) -> Point {
2586 self.nodes
2587 .get(index)
2588 .map(|node| node.accumulated_offset.get())
2589 .unwrap_or_default()
2590 }
2591
2592 #[cfg(test)]
2593 fn debug_ptrs(&self) -> Vec<usize> {
2594 self.nodes.iter().map(CoordinatorNode::ptr).collect()
2595 }
2596}
2597
2598#[derive(Default)]
2599pub(crate) struct LayoutRuntimeState {
2600 child_ids: Vec<NodeId>,
2601 child_states: Vec<Rc<LayoutChildMeasureState>>,
2602 child_measurables: Vec<Box<dyn Measurable>>,
2603 coordinator_chain: CoordinatorChain,
2604}
2605
2606impl LayoutRuntimeState {
2607 fn reconcile_child_measurables(&mut self, child_ids: &[NodeId]) {
2608 if self.child_ids == child_ids {
2609 return;
2610 }
2611
2612 let mut previous_ids = std::mem::take(&mut self.child_ids);
2613 let mut previous_states = std::mem::take(&mut self.child_states);
2614 let mut previous_measurables = std::mem::take(&mut self.child_measurables);
2615
2616 self.child_ids.reserve(child_ids.len());
2617 self.child_states.reserve(child_ids.len());
2618 self.child_measurables.reserve(child_ids.len());
2619
2620 for &child_id in child_ids {
2621 if let Some(position) = previous_ids.iter().position(|&id| id == child_id) {
2622 self.child_ids.push(previous_ids.swap_remove(position));
2623 self.child_states
2624 .push(previous_states.swap_remove(position));
2625 self.child_measurables
2626 .push(previous_measurables.swap_remove(position));
2627 } else {
2628 let state = LayoutChildMeasureState::new(child_id);
2629 self.child_ids.push(child_id);
2630 self.child_states.push(Rc::clone(&state));
2631 self.child_measurables
2632 .push(Box::new(LayoutChildMeasurable::new(state)));
2633 }
2634 }
2635 }
2636
2637 fn child_state(&self, index: usize) -> Rc<LayoutChildMeasureState> {
2638 Rc::clone(&self.child_states[index])
2639 }
2640
2641 fn child_measurables(&self) -> &[Box<dyn Measurable>] {
2642 self.child_measurables.as_slice()
2643 }
2644
2645 fn reconcile_coordinator_chain(&mut self, layout_node_data: &[LayoutModifierNodeData]) {
2646 self.coordinator_chain.reconcile(layout_node_data);
2647 }
2648
2649 fn coordinator_chain(&self) -> &CoordinatorChain {
2650 &self.coordinator_chain
2651 }
2652
2653 fn clear_frame_bindings(&self) {
2654 for child_state in &self.child_states {
2655 child_state.clear_frame_bindings();
2656 }
2657 }
2658
2659 #[cfg(test)]
2660 pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
2661 LayoutRuntimeDebugStats {
2662 child_ids: self.child_ids.clone(),
2663 child_state_ptrs: self
2664 .child_states
2665 .iter()
2666 .map(|state| Rc::as_ptr(state) as *const () as usize)
2667 .collect(),
2668 child_measurable_ptrs: self
2669 .child_measurables
2670 .iter()
2671 .map(|measurable| {
2672 measurable.as_ref() as *const dyn Measurable as *const () as usize
2673 })
2674 .collect(),
2675 child_measurable_count: self.child_measurables.len(),
2676 coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
2677 coordinator_node_count: self.coordinator_chain.nodes.len(),
2678 }
2679 }
2680}
2681
2682#[cfg(test)]
2683#[derive(Debug, Clone, PartialEq, Eq)]
2684pub(crate) struct LayoutRuntimeDebugStats {
2685 pub(crate) child_ids: Vec<NodeId>,
2686 pub(crate) child_state_ptrs: Vec<usize>,
2687 pub(crate) child_measurable_ptrs: Vec<usize>,
2688 pub(crate) child_measurable_count: usize,
2689 pub(crate) coordinator_node_ptrs: Vec<usize>,
2690 pub(crate) coordinator_node_count: usize,
2691}
2692
2693struct LayoutChildMeasureConfig {
2694 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
2695 node_id: NodeId,
2696 error: Rc<RefCell<Option<NodeError>>>,
2697 runtime_handle: Option<RuntimeHandle>,
2698 cache: LayoutNodeCacheHandles,
2699 cache_epoch: u64,
2700 force_remeasure: bool,
2701 measure_handle: Option<LayoutMeasureHandle>,
2702 layout_state: Option<Rc<RefCell<LayoutState>>>,
2703}
2704
2705struct LayoutChildMeasureState {
2706 applier: RefCell<Option<Rc<ConcreteApplierHost<MemoryApplier>>>>,
2707 node_id: Cell<NodeId>,
2708 measured: RefCell<Option<Rc<MeasuredNode>>>,
2709 last_position: Cell<Option<Point>>,
2710 error: RefCell<Option<Rc<RefCell<Option<NodeError>>>>>,
2711 runtime_handle: RefCell<Option<RuntimeHandle>>,
2712 cache: RefCell<LayoutNodeCacheHandles>,
2713 cache_epoch: Cell<u64>,
2714 force_remeasure: Cell<bool>,
2715 measure_handle: RefCell<Option<LayoutMeasureHandle>>,
2716 layout_state: RefCell<Option<Rc<RefCell<LayoutState>>>>,
2717}
2718
2719impl LayoutChildMeasureState {
2720 fn new(node_id: NodeId) -> Rc<Self> {
2721 Rc::new(Self {
2722 applier: RefCell::new(None),
2723 node_id: Cell::new(node_id),
2724 measured: RefCell::new(None),
2725 last_position: Cell::new(None),
2726 error: RefCell::new(None),
2727 runtime_handle: RefCell::new(None),
2728 cache: RefCell::new(LayoutNodeCacheHandles::default()),
2729 cache_epoch: Cell::new(0),
2730 force_remeasure: Cell::new(true),
2731 measure_handle: RefCell::new(None),
2732 layout_state: RefCell::new(None),
2733 })
2734 }
2735
2736 fn configure(&self, config: LayoutChildMeasureConfig) {
2737 config.cache.activate(config.cache_epoch);
2738 *self.applier.borrow_mut() = Some(config.applier);
2739 self.node_id.set(config.node_id);
2740 self.measured.borrow_mut().take();
2741 self.last_position.set(None);
2742 *self.error.borrow_mut() = Some(config.error);
2743 *self.runtime_handle.borrow_mut() = config.runtime_handle;
2744 *self.cache.borrow_mut() = config.cache;
2745 self.cache_epoch.set(config.cache_epoch);
2746 self.force_remeasure.set(config.force_remeasure);
2747 *self.measure_handle.borrow_mut() = config.measure_handle;
2748 *self.layout_state.borrow_mut() = config.layout_state;
2749 }
2750
2751 fn clear_frame_bindings(&self) {
2752 self.measured.borrow_mut().take();
2753 *self.applier.borrow_mut() = None;
2754 *self.error.borrow_mut() = None;
2755 *self.runtime_handle.borrow_mut() = None;
2756 *self.measure_handle.borrow_mut() = None;
2757 *self.layout_state.borrow_mut() = None;
2758 }
2759
2760 fn node_id(&self) -> NodeId {
2761 self.node_id.get()
2762 }
2763
2764 fn cache(&self) -> LayoutNodeCacheHandles {
2765 self.cache.borrow().clone()
2766 }
2767
2768 fn applier(&self) -> Option<Rc<ConcreteApplierHost<MemoryApplier>>> {
2769 self.applier.borrow().clone()
2770 }
2771
2772 fn layout_state(&self) -> Option<Rc<RefCell<LayoutState>>> {
2773 self.layout_state.borrow().clone()
2774 }
2775
2776 fn take_measured(&self) -> Option<Rc<MeasuredNode>> {
2777 self.measured.borrow_mut().take()
2778 }
2779
2780 fn last_position(&self) -> Option<Point> {
2781 self.last_position.get()
2782 }
2783
2784 fn set_last_position(&self, position: Point) {
2785 self.last_position.set(Some(position));
2786 }
2787
2788 fn place_retained(&self, position: Point) {
2798 self.set_last_position(position);
2799 if let Some(layout_state) = self.layout_state() {
2800 let mut layout_state = layout_state.borrow_mut();
2801 layout_state.position = position;
2802 layout_state.is_placed = true;
2803 return;
2804 }
2805 let Some(applier) = self.applier() else {
2806 return;
2807 };
2808 let Ok(mut applier) = applier.try_borrow_typed() else {
2809 return;
2810 };
2811 let node_id = self.node_id();
2812 if applier
2813 .with_node::<LayoutNode, _>(node_id, |node| {
2814 node.set_position(position);
2815 })
2816 .is_err()
2817 {
2818 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
2819 node.set_position(position);
2820 });
2821 }
2822 }
2823
2824 fn set_measured(&self, measured: Option<Rc<MeasuredNode>>) {
2825 *self.measured.borrow_mut() = measured;
2826 }
2827
2828 fn record_error(&self, err: NodeError) {
2829 let Some(error) = self.error.borrow().clone() else {
2830 return;
2831 };
2832 let mut slot = error.borrow_mut();
2833 if slot.is_none() {
2834 *slot = Some(err);
2835 }
2836 }
2837
2838 fn perform_measure(&self, constraints: Constraints) -> Result<Rc<MeasuredNode>, NodeError> {
2839 let node_id = self.node_id();
2840 if let Some(handle) = self.measure_handle.borrow().clone() {
2841 return handle.measure(node_id, constraints);
2842 }
2843 let applier = self.applier().ok_or(NodeError::MissingContext {
2844 id: node_id,
2845 reason: "layout child applier not configured",
2846 })?;
2847 measure_node_with_host(
2848 applier,
2849 self.runtime_handle.borrow().clone(),
2850 node_id,
2851 constraints,
2852 self.cache_epoch.get(),
2853 )
2854 }
2855
2856 fn intrinsic_measure(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
2857 let cache = self.cache();
2858 cache.activate(self.cache_epoch.get());
2859 if !self.force_remeasure.get() {
2860 if let Some(cached) = cache.get_measurement(constraints) {
2861 return Some(cached);
2862 }
2863 }
2864
2865 match self.perform_measure(constraints) {
2866 Ok(measured) => {
2867 self.force_remeasure.set(false);
2868 cache.store_measurement(constraints, Rc::clone(&measured));
2869 Some(measured)
2870 }
2871 Err(err) => {
2872 self.record_error(err);
2873 None
2874 }
2875 }
2876 }
2877}
2878
2879struct LayoutChildMeasurable {
2880 state: Rc<LayoutChildMeasureState>,
2881}
2882
2883impl LayoutChildMeasurable {
2884 fn new(state: Rc<LayoutChildMeasureState>) -> Self {
2885 Self { state }
2886 }
2887
2888 fn resolved_parent_data(&self) -> Option<cranpose_ui_layout::ParentData> {
2889 let applier = self.state.applier()?;
2890 let node_id = self.state.node_id();
2891 let Ok(mut applier) = applier.try_borrow_typed() else {
2892 return None;
2893 };
2894
2895 applier
2896 .with_node::<LayoutNode, _>(node_id, |layout_node| {
2897 let props = layout_node.resolved_modifiers().layout_properties();
2898 let weight = props.weight().unwrap_or_default();
2899 cranpose_ui_layout::ParentData {
2900 weight: weight.weight,
2901 fill: weight.fill,
2902 box_alignment: props.box_alignment(),
2903 row_alignment: props.row_alignment(),
2904 column_alignment: props.column_alignment(),
2905 }
2906 })
2907 .ok()
2908 }
2909}
2910
2911impl Measurable for LayoutChildMeasurable {
2912 fn measure(&self, constraints: Constraints) -> Placeable {
2913 let state = &self.state;
2914 let cache = state.cache();
2915 cache.activate(state.cache_epoch.get());
2916 let measured_size;
2917 if !state.force_remeasure.get() {
2918 if let Some(cached) = cache.get_measurement(constraints) {
2919 measured_size = cached.size;
2920 state.set_measured(Some(Rc::clone(&cached)));
2921 } else {
2922 match state.perform_measure(constraints) {
2923 Ok(measured) => {
2924 state.force_remeasure.set(false);
2925 measured_size = measured.size;
2926 cache.store_measurement(constraints, Rc::clone(&measured));
2927 state.set_measured(Some(measured));
2928 }
2929 Err(err) => {
2930 state.record_error(err);
2931 state.set_measured(None);
2932 measured_size = Size {
2933 width: 0.0,
2934 height: 0.0,
2935 };
2936 }
2937 }
2938 }
2939 } else {
2940 match state.perform_measure(constraints) {
2941 Ok(measured) => {
2942 state.force_remeasure.set(false);
2943 measured_size = measured.size;
2944 cache.store_measurement(constraints, Rc::clone(&measured));
2945 state.set_measured(Some(measured));
2946 }
2947 Err(err) => {
2948 state.record_error(err);
2949 state.set_measured(None);
2950 measured_size = Size {
2951 width: 0.0,
2952 height: 0.0,
2953 };
2954 }
2955 }
2956 }
2957
2958 if let Some(layout_state) = state.layout_state() {
2959 let mut layout_state = layout_state.borrow_mut();
2960 layout_state.size = measured_size;
2961 layout_state.measurement_constraints = constraints;
2962 } else if let Some(applier) = state.applier() {
2963 let Ok(mut applier) = applier.try_borrow_typed() else {
2964 return Placeable::value(
2965 measured_size.width,
2966 measured_size.height,
2967 state.node_id(),
2968 );
2969 };
2970 let _ = applier.with_node::<LayoutNode, _>(state.node_id(), |node| {
2971 node.set_measured_size(measured_size);
2972 node.set_measurement_constraints(constraints);
2973 });
2974 }
2975
2976 let state = Rc::clone(&self.state);
2977 let node_id = state.node_id();
2978
2979 let place_fn = Rc::new(move |x: f32, y: f32| {
2980 let internal_offset = state
2981 .measured
2982 .borrow()
2983 .as_ref()
2984 .map(|m| m.offset)
2985 .unwrap_or_default();
2986
2987 state.place_retained(Point {
2988 x: x + internal_offset.x,
2989 y: y + internal_offset.y,
2990 });
2991 });
2992
2993 Placeable::with_place_fn(measured_size.width, measured_size.height, node_id, place_fn)
2994 }
2995
2996 fn min_intrinsic_width(&self, height: f32) -> f32 {
2997 let kind = IntrinsicKind::MinWidth(height);
2998 let cache = self.state.cache();
2999 cache.activate(self.state.cache_epoch.get());
3000 if !self.state.force_remeasure.get() {
3001 if let Some(value) = cache.get_intrinsic(&kind) {
3002 return value;
3003 }
3004 }
3005 let constraints = Constraints {
3006 min_width: 0.0,
3007 max_width: f32::INFINITY,
3008 min_height: height,
3009 max_height: height,
3010 };
3011 if let Some(node) = self.state.intrinsic_measure(constraints) {
3012 let value = node.size.width;
3013 cache.store_intrinsic(kind, value);
3014 value
3015 } else {
3016 0.0
3017 }
3018 }
3019
3020 fn max_intrinsic_width(&self, height: f32) -> f32 {
3021 let kind = IntrinsicKind::MaxWidth(height);
3022 let cache = self.state.cache();
3023 cache.activate(self.state.cache_epoch.get());
3024 if !self.state.force_remeasure.get() {
3025 if let Some(value) = cache.get_intrinsic(&kind) {
3026 return value;
3027 }
3028 }
3029 let constraints = Constraints {
3030 min_width: 0.0,
3031 max_width: f32::INFINITY,
3032 min_height: 0.0,
3033 max_height: height,
3034 };
3035 if let Some(node) = self.state.intrinsic_measure(constraints) {
3036 let value = node.size.width;
3037 cache.store_intrinsic(kind, value);
3038 value
3039 } else {
3040 0.0
3041 }
3042 }
3043
3044 fn min_intrinsic_height(&self, width: f32) -> f32 {
3045 let kind = IntrinsicKind::MinHeight(width);
3046 let cache = self.state.cache();
3047 cache.activate(self.state.cache_epoch.get());
3048 if !self.state.force_remeasure.get() {
3049 if let Some(value) = cache.get_intrinsic(&kind) {
3050 return value;
3051 }
3052 }
3053 let constraints = Constraints {
3054 min_width: width,
3055 max_width: width,
3056 min_height: 0.0,
3057 max_height: f32::INFINITY,
3058 };
3059 if let Some(node) = self.state.intrinsic_measure(constraints) {
3060 let value = node.size.height;
3061 cache.store_intrinsic(kind, value);
3062 value
3063 } else {
3064 0.0
3065 }
3066 }
3067
3068 fn max_intrinsic_height(&self, width: f32) -> f32 {
3069 let kind = IntrinsicKind::MaxHeight(width);
3070 let cache = self.state.cache();
3071 cache.activate(self.state.cache_epoch.get());
3072 if !self.state.force_remeasure.get() {
3073 if let Some(value) = cache.get_intrinsic(&kind) {
3074 return value;
3075 }
3076 }
3077 let constraints = Constraints {
3078 min_width: 0.0,
3079 max_width: width,
3080 min_height: 0.0,
3081 max_height: f32::INFINITY,
3082 };
3083 if let Some(node) = self.state.intrinsic_measure(constraints) {
3084 let value = node.size.height;
3085 cache.store_intrinsic(kind, value);
3086 value
3087 } else {
3088 0.0
3089 }
3090 }
3091
3092 fn flex_parent_data(&self) -> Option<cranpose_ui_layout::FlexParentData> {
3093 let parent_data = self.resolved_parent_data()?;
3094 if !parent_data.has_weight() {
3095 return None;
3096 }
3097 Some(cranpose_ui_layout::FlexParentData::new(
3098 parent_data.weight,
3099 parent_data.fill,
3100 ))
3101 }
3102
3103 fn parent_data(&self) -> cranpose_ui_layout::ParentData {
3104 self.resolved_parent_data().unwrap_or_default()
3105 }
3106}
3107
3108fn measure_node_with_host(
3109 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
3110 runtime_handle: Option<RuntimeHandle>,
3111 node_id: NodeId,
3112 constraints: Constraints,
3113 epoch: u64,
3114) -> Result<Rc<MeasuredNode>, NodeError> {
3115 let runtime_handle = match runtime_handle {
3116 Some(handle) => Some(handle),
3117 None => applier.borrow_typed().runtime_handle(),
3118 };
3119 let mut builder = LayoutBuilder::new_with_epoch(
3120 applier,
3121 epoch,
3122 Rc::new(RefCell::new(SlotTable::default())),
3123 FrameLayoutArena::default(),
3124 );
3125 builder.set_runtime_handle(runtime_handle);
3126 builder.measure_node(node_id, constraints)
3127}
3128
3129#[derive(Clone)]
3130struct RuntimeNodeMetadata {
3131 modifier: Modifier,
3132 resolved_modifiers: ResolvedModifiers,
3133 modifier_slices: Rc<ModifierNodeSlices>,
3134 role: SemanticsRole,
3135 button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
3136}
3137
3138impl Default for RuntimeNodeMetadata {
3139 fn default() -> Self {
3140 Self {
3141 modifier: Modifier::empty(),
3142 resolved_modifiers: ResolvedModifiers::default(),
3143 modifier_slices: Rc::default(),
3144 role: SemanticsRole::Unknown,
3145 button_handler: None,
3146 }
3147 }
3148}
3149
3150fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
3151 modifier_slices
3152 .text_content()
3153 .map(|text| SemanticsRole::Text {
3154 value: text.to_string(),
3155 })
3156 .unwrap_or(SemanticsRole::Layout)
3157}
3158
3159fn runtime_metadata_for(
3160 applier: &mut MemoryApplier,
3161 node_id: NodeId,
3162) -> Result<RuntimeNodeMetadata, NodeError> {
3163 if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
3168 let modifier = layout.modifier.clone();
3169 let resolved_modifiers = layout.resolved_modifiers();
3170 let modifier_slices = layout.modifier_slices_snapshot();
3171 let role = role_from_modifier_slices(&modifier_slices);
3172
3173 RuntimeNodeMetadata {
3174 modifier,
3175 resolved_modifiers,
3176 modifier_slices,
3177 role,
3178 button_handler: None,
3179 }
3180 }) {
3181 return Ok(meta);
3182 }
3183
3184 if let Ok((modifier, resolved_modifiers, modifier_slices)) = applier
3186 .with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
3187 (
3188 node.modifier(),
3189 node.resolved_modifiers(),
3190 node.modifier_slices_snapshot(),
3191 )
3192 })
3193 {
3194 return Ok(RuntimeNodeMetadata {
3195 modifier,
3196 resolved_modifiers,
3197 modifier_slices,
3198 role: SemanticsRole::Subcompose,
3199 button_handler: None,
3200 });
3201 }
3202 Ok(RuntimeNodeMetadata::default())
3203}
3204
3205fn clear_semantics_dirty_flags(
3206 applier: &mut MemoryApplier,
3207 node: &MeasuredNode,
3208) -> Result<(), NodeError> {
3209 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3210 layout.clear_needs_semantics();
3211 }) {
3212 Ok(()) => {}
3213 Err(NodeError::Missing { .. }) => {}
3214 Err(NodeError::TypeMismatch { .. }) => {
3215 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3216 subcompose.clear_needs_semantics();
3217 }) {
3218 Ok(()) | Err(NodeError::Missing { .. }) | Err(NodeError::TypeMismatch { .. }) => {}
3219 Err(err) => return Err(err),
3220 }
3221 }
3222 Err(err) => return Err(err),
3223 }
3224
3225 for child in &node.children {
3226 clear_semantics_dirty_flags(applier, &child.node)?;
3227 }
3228
3229 Ok(())
3230}
3231
3232fn build_semantics_tree_from_live_nodes(
3233 applier: &mut MemoryApplier,
3234 node: &MeasuredNode,
3235) -> Result<SemanticsTree, NodeError> {
3236 Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
3237 applier, node,
3238 )?))
3239}
3240
3241fn semantics_node_from_parts(
3242 node_id: NodeId,
3243 mut role: SemanticsRole,
3244 config: Option<SemanticsConfiguration>,
3245 children: Vec<SemanticsNode>,
3246) -> SemanticsNode {
3247 let mut node = SemanticsNode {
3248 node_id,
3249 children,
3250 ..SemanticsNode::default()
3251 };
3252
3253 if let Some(config) = config {
3254 if config.role == Some(SemanticsWidgetRole::Button) {
3255 role = SemanticsRole::Button;
3256 }
3257 if config.is_activatable() {
3261 node.actions.push(SemanticsAction::Click {
3262 handler: SemanticsCallback::new(node_id),
3263 });
3264 }
3265 node.widget_role = config.role;
3266 node.description = config.content_description;
3267 node.state_description = config.state_description;
3268 node.on_click_label = config.on_click_label;
3269 node.selected = config.selected;
3270 node.toggled = config.toggled;
3271 node.enabled = config.enabled;
3272 node.custom_actions = config.custom_actions;
3273 node.canvas_children = config.canvas_children;
3274 node.editable_text = config.is_editable_text;
3275 node.text_selection = config.text_selection;
3276 }
3277
3278 node.role = role;
3279 node
3280}
3281
3282fn build_semantics_node_from_live_nodes(
3283 applier: &mut MemoryApplier,
3284 node: &MeasuredNode,
3285) -> Result<SemanticsNode, NodeError> {
3286 let (role, config) = match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
3287 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
3288 let config = layout.semantics_configuration();
3289 layout.clear_needs_semantics();
3290 (role, config)
3291 }) {
3292 Ok(data) => data,
3293 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {
3294 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
3295 subcompose.clear_needs_semantics();
3296 (
3297 SemanticsRole::Subcompose,
3298 collect_semantics_from_modifier(&subcompose.modifier()),
3299 )
3300 }) {
3301 Ok(data) => data,
3302 Err(NodeError::TypeMismatch { .. }) | Err(NodeError::Missing { .. }) => {
3303 (SemanticsRole::Unknown, None)
3304 }
3305 Err(err) => return Err(err),
3306 }
3307 }
3308 Err(err) => return Err(err),
3309 };
3310
3311 let mut children = Vec::with_capacity(node.children.len());
3312 for child in &node.children {
3313 children.push(build_semantics_node_from_live_nodes(applier, &child.node)?);
3314 }
3315
3316 Ok(semantics_node_from_parts(
3317 node.node_id,
3318 role,
3319 config,
3320 children,
3321 ))
3322}
3323
3324fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
3325 stats.semantics_node_count += 1;
3326 stats.semantics_action_count += node.actions.len();
3327 stats.semantics_action_capacity += node.actions.capacity();
3328 stats.semantics_child_count += node.children.len();
3329 stats.semantics_child_capacity += node.children.capacity();
3330 stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
3331 stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
3332
3333 if let Some(description) = &node.description {
3334 stats.semantics_description_count += 1;
3335 stats.semantics_description_bytes += description.capacity();
3336 stats.semantics_heap_bytes += description.capacity();
3337 }
3338 if let SemanticsRole::Text { value } = &node.role {
3339 stats.semantics_text_role_bytes += value.capacity();
3340 stats.semantics_heap_bytes += value.capacity();
3341 }
3342
3343 for child in &node.children {
3344 record_semantics_allocation_stats(child, stats);
3345 }
3346}
3347
3348fn record_layout_box_allocation_stats(
3349 layout_box: &LayoutBox,
3350 stats: &mut LayoutAllocationDebugStats,
3351) {
3352 stats.layout_box_count += 1;
3353 stats.layout_box_child_count += layout_box.children.len();
3354 stats.layout_box_child_capacity += layout_box.children.capacity();
3355 stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
3356 stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
3357
3358 for child in &layout_box.children {
3359 record_layout_box_allocation_stats(child, stats);
3360 }
3361}
3362
3363fn build_layout_tree(
3364 applier: &mut MemoryApplier,
3365 node: &MeasuredNode,
3366) -> Result<LayoutTree, NodeError> {
3367 fn place(
3368 applier: &mut MemoryApplier,
3369 node: &MeasuredNode,
3370 origin: Point,
3371 parent_layer_translation: Point,
3379 ) -> Result<LayoutBox, NodeError> {
3380 let top_left = Point {
3382 x: origin.x + node.offset.x,
3383 y: origin.y + node.offset.y,
3384 };
3385 let rect = GeometryRect {
3386 x: top_left.x,
3387 y: top_left.y,
3388 width: node.size.width,
3389 height: node.size.height,
3390 };
3391 let info = runtime_metadata_for(applier, node.node_id)?;
3392 let kind = layout_kind_from_metadata(node.node_id, &info);
3393 let RuntimeNodeMetadata {
3394 modifier,
3395 resolved_modifiers,
3396 modifier_slices,
3397 ..
3398 } = info;
3399
3400 let layer_translation = match modifier_slices.graphics_layer() {
3401 Some(layer) => Point {
3402 x: parent_layer_translation.x + layer.translation_x,
3403 y: parent_layer_translation.y + layer.translation_y,
3404 },
3405 None => parent_layer_translation,
3406 };
3407
3408 if let Some(sink) = modifier_slices.text_field_window_origin() {
3414 sink.set(Point {
3415 x: top_left.x + layer_translation.x,
3416 y: top_left.y + layer_translation.y,
3417 });
3418 }
3419
3420 if let Some(sink) = modifier_slices.viewport_window_rect() {
3423 sink.set(GeometryRect {
3424 x: top_left.x + layer_translation.x,
3425 y: top_left.y + layer_translation.y,
3426 width: node.size.width,
3427 height: node.size.height,
3428 });
3429 }
3430
3431 modifier_slices.publish_pointer_input_size(node.size);
3434
3435 let data = LayoutNodeData::new(modifier, resolved_modifiers, modifier_slices, kind);
3436 let mut children = Vec::with_capacity(node.children.len());
3437 for child in &node.children {
3438 let child_origin = Point {
3439 x: top_left.x + child.offset.x,
3440 y: top_left.y + child.offset.y,
3441 };
3442 children.push(place(
3443 applier,
3444 &child.node,
3445 child_origin,
3446 layer_translation,
3447 )?);
3448 }
3449 Ok(LayoutBox::new(
3450 node.node_id,
3451 rect,
3452 node.content_offset,
3453 data,
3454 children,
3455 ))
3456 }
3457
3458 Ok(LayoutTree::new(place(
3459 applier,
3460 node,
3461 Point { x: 0.0, y: 0.0 },
3462 Point { x: 0.0, y: 0.0 },
3463 )?))
3464}
3465
3466fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
3467 match &layout_box.node_data.kind {
3468 LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
3469 LayoutNodeKind::Spacer => SemanticsRole::Spacer,
3470 LayoutNodeKind::Unknown => SemanticsRole::Unknown,
3471 LayoutNodeKind::Button { .. } => SemanticsRole::Button,
3472 LayoutNodeKind::Layout => layout_box
3473 .node_data
3474 .modifier_slices()
3475 .text_content()
3476 .map(|text| SemanticsRole::Text {
3477 value: text.to_string(),
3478 })
3479 .unwrap_or(SemanticsRole::Layout),
3480 }
3481}
3482
3483fn build_semantics_node_from_layout_box(layout_box: &LayoutBox) -> SemanticsNode {
3484 let children = layout_box
3485 .children
3486 .iter()
3487 .map(build_semantics_node_from_layout_box)
3488 .collect();
3489
3490 semantics_node_from_parts(
3491 layout_box.node_id,
3492 semantics_role_from_layout_box(layout_box),
3493 collect_semantics_from_modifier(&layout_box.node_data.modifier),
3494 children,
3495 )
3496}
3497
3498fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
3499 match &info.role {
3500 SemanticsRole::Layout => LayoutNodeKind::Layout,
3501 SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
3502 SemanticsRole::Text { .. } => {
3503 LayoutNodeKind::Layout
3507 }
3508 SemanticsRole::Spacer => LayoutNodeKind::Spacer,
3509 SemanticsRole::Button => {
3510 let handler = info
3511 .button_handler
3512 .as_ref()
3513 .cloned()
3514 .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
3515 LayoutNodeKind::Button { on_click: handler }
3516 }
3517 SemanticsRole::Unknown => LayoutNodeKind::Unknown,
3518 }
3519}
3520
3521fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
3522 let horizontal = padding.horizontal_sum();
3523 let vertical = padding.vertical_sum();
3524 let min_width = (constraints.min_width - horizontal).max(0.0);
3525 let mut max_width = constraints.max_width;
3526 if max_width.is_finite() {
3527 max_width = (max_width - horizontal).max(0.0);
3528 }
3529 let min_height = (constraints.min_height - vertical).max(0.0);
3530 let mut max_height = constraints.max_height;
3531 if max_height.is_finite() {
3532 max_height = (max_height - vertical).max(0.0);
3533 }
3534 normalize_constraints(Constraints {
3535 min_width,
3536 max_width,
3537 min_height,
3538 max_height,
3539 })
3540}
3541
3542#[cfg(test)]
3543pub(crate) fn align_horizontal(alignment: HorizontalAlignment, available: f32, child: f32) -> f32 {
3544 match alignment {
3545 HorizontalAlignment::Start => 0.0,
3546 HorizontalAlignment::CenterHorizontally => ((available - child) / 2.0).max(0.0),
3547 HorizontalAlignment::End => (available - child).max(0.0),
3548 }
3549}
3550
3551#[cfg(test)]
3552pub(crate) fn align_vertical(alignment: VerticalAlignment, available: f32, child: f32) -> f32 {
3553 match alignment {
3554 VerticalAlignment::Top => 0.0,
3555 VerticalAlignment::CenterVertically => ((available - child) / 2.0).max(0.0),
3556 VerticalAlignment::Bottom => (available - child).max(0.0),
3557 }
3558}
3559
3560fn resolve_dimension(
3561 base: f32,
3562 explicit: DimensionConstraint,
3563 min_override: Option<f32>,
3564 max_override: Option<f32>,
3565 min_limit: f32,
3566 max_limit: f32,
3567) -> f32 {
3568 let mut min_bound = min_limit;
3569 if let Some(min_value) = min_override {
3570 min_bound = min_bound.max(min_value);
3571 }
3572
3573 let mut max_bound = if max_limit.is_finite() {
3574 max_limit
3575 } else {
3576 max_override.unwrap_or(max_limit)
3577 };
3578 if let Some(max_value) = max_override {
3579 if max_bound.is_finite() {
3580 max_bound = max_bound.min(max_value);
3581 } else {
3582 max_bound = max_value;
3583 }
3584 }
3585 if max_bound < min_bound {
3586 max_bound = min_bound;
3587 }
3588
3589 let mut size = match explicit {
3590 DimensionConstraint::Points(points) => points,
3591 DimensionConstraint::Fraction(fraction) => {
3592 if max_limit.is_finite() {
3593 max_limit * fraction.clamp(0.0, 1.0)
3594 } else {
3595 base
3596 }
3597 }
3598 DimensionConstraint::Unspecified => base,
3599 DimensionConstraint::Intrinsic(_) => base,
3602 };
3603
3604 size = clamp_dimension(size, min_bound, max_bound);
3605 size = clamp_dimension(size, min_limit, max_limit);
3606 size.max(0.0)
3607}
3608
3609fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
3610 let mut result = value.max(min);
3611 if max.is_finite() {
3612 result = result.min(max);
3613 }
3614 result
3615}
3616
3617fn normalize_constraints(mut constraints: Constraints) -> Constraints {
3618 if constraints.max_width < constraints.min_width {
3619 constraints.max_width = constraints.min_width;
3620 }
3621 if constraints.max_height < constraints.min_height {
3622 constraints.max_height = constraints.min_height;
3623 }
3624 constraints
3625}
3626
3627#[cfg(test)]
3628#[path = "tests/layout_tests.rs"]
3629mod tests;