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