1mod bottom_up;
2mod coordinator_chain;
3pub mod core;
5pub(crate) mod flex_box;
6mod frame_layout_arena;
7pub(crate) mod grid;
8mod layout_child_measurable;
9mod layout_child_measure_state;
10mod layout_node_data;
11mod live_layout_node;
12mod measure_telemetry;
13mod measured_spare;
14mod modifier_chain_measure;
15mod placed_root_measurements;
16pub mod policies;
18mod reveal;
19mod semantics_details;
20mod semantics_labels;
21mod semantics_tree;
22mod semantics_update;
23mod subcompose_measure;
24
25use std::{cell::RefCell, mem::size_of, rc::Rc};
26
27use cranpose_core::{
28 Applier, ConcreteApplierHost, MemoryApplier, Node, NodeError, NodeId, RuntimeHandle,
29};
30use cranpose_foundation::{
31 InvalidationKind, ModifierNodeContext, SemanticsConfiguration, SemanticsWidgetRole,
32};
33use cranpose_ui_graphics::ProjectiveTransform;
34use cranpose_ui_layout::{AlignmentLines, Constraints, MeasurePolicy, PlaceTarget};
35pub(crate) use frame_layout_arena::FrameLayoutArena;
36use frame_layout_arena::VecPools;
37use layout_child_measure_state::{
38 ChildPass, LayoutChildMeasureState, LayoutChildren, inherited_alignment_lines, read_cell,
39};
40use layout_node_data::{role_from_modifier_slices, snapshot_node_data};
41use live_layout_node::{LiveLayoutNode, read_live_layout_node};
42use measured_spare::MeasuredSpare;
43use placed_root_measurements::PlacedRootMeasurements;
44pub use semantics_tree::{
45 SemanticsAction, SemanticsCallback, SemanticsNode, SemanticsPlacement, SemanticsRole,
46 SemanticsText, SemanticsTree,
47};
48use semantics_tree::{modal_takes_space, top_modal_path};
49use web_time::Instant;
50
51#[cfg(test)]
52use self::core::{HorizontalAlignment, VerticalAlignment};
53use self::{
54 coordinator_chain::{CoordinatorChain, CoordinatorFrame},
55 core::Measurable,
56 measure_telemetry::{
57 LayoutStages, layout_measure_telemetry_threshold_ms, log_layout_measure_telemetry,
58 log_node_measure_telemetry,
59 },
60 semantics_update::semantics_placement,
61};
62pub use self::{
63 layout_node_data::{LayoutNodeData, LayoutNodeKind},
64 reveal::can_skip_scroll_reveal_from_applier,
65 semantics_details::SemanticsDetails,
66 semantics_update::{build_semantics_tree_from_applier, update_semantics_tree_from_applier},
67};
68use crate::{
69 modifier::{
70 DimensionConstraint, EdgeInsets, ModifierNodeSlicesDebugStats, Point, Rect as GeometryRect,
71 Size,
72 },
73 render_state::AppContext,
74 subcompose_layout::SubcomposeLayoutNode,
75 widgets::nodes::{ChildPlacement, IntrinsicKind, LayoutNode, LayoutParts, LayoutState},
76};
77
78#[derive(Default)]
79pub(crate) struct LayoutNodeContext {
80 invalidations: Vec<InvalidationKind>,
81 update_requested: bool,
82 density: f32,
83}
84
85impl LayoutNodeContext {
86 pub(crate) fn new(density: f32) -> Self {
87 Self {
88 density,
89 ..Self::default()
90 }
91 }
92
93 pub(crate) fn take_invalidations(&mut self) -> Vec<InvalidationKind> {
94 std::mem::take(&mut self.invalidations)
95 }
96}
97
98impl ModifierNodeContext for LayoutNodeContext {
99 fn invalidate(&mut self, kind: InvalidationKind) {
100 if !self.invalidations.contains(&kind) {
101 self.invalidations.push(kind);
102 }
103 }
104
105 fn request_update(&mut self) {
106 self.update_requested = true;
107 }
108
109 fn density(&self) -> f32 {
110 self.density
111 }
112}
113
114#[doc(hidden)]
115pub fn invalidate_all_layout_caches() {
116 crate::render_state::invalidate_layout_cache_epoch();
117}
118
119pub(crate) struct LayoutHost {
122 host: Rc<ConcreteApplierHost<MemoryApplier>>,
123}
124
125impl Default for LayoutHost {
126 fn default() -> Self {
127 Self {
128 host: Rc::new(ConcreteApplierHost::new(MemoryApplier::new())),
129 }
130 }
131}
132
133struct ApplierSlotGuard<'a> {
136 target: &'a mut MemoryApplier,
137 lent: LayoutHost,
138 context: &'a AppContext,
139}
140
141impl<'a> ApplierSlotGuard<'a> {
142 fn new(target: &'a mut MemoryApplier, context: &'a AppContext) -> Self {
143 let lent = context.take_layout_host();
144 std::mem::swap(target, &mut *lent.host.borrow_typed());
145 Self {
146 target,
147 lent,
148 context,
149 }
150 }
151
152 fn host(&self) -> &Rc<ConcreteApplierHost<MemoryApplier>> {
153 &self.lent.host
154 }
155}
156
157impl Drop for ApplierSlotGuard<'_> {
158 fn drop(&mut self) {
159 let host = &self.lent.host;
160 std::mem::swap(self.target, &mut *host.borrow_typed());
161 if Rc::strong_count(host) == 1 {
163 self.context.replace_layout_host(LayoutHost {
164 host: Rc::clone(host),
165 });
166 }
167 }
168}
169
170#[derive(Clone, Copy, PartialEq)]
171struct ModifierChainInputs {
172 density: crate::density::Density,
173 window_root: bool,
174 offset: Point,
175 uses_chain: bool,
176}
177
178struct ModifierChainMeasurement {
179 size: Size,
180 alignment_lines: AlignmentLines,
181 content_offset: Point,
182 offset: Point,
183 window_root: bool,
184 hold: Option<cranpose_ui_layout::ConstraintsHold>,
186 ignores_child_sizes: bool,
189}
190
191#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
194pub struct LayoutAllocationDebugStats {
195 pub layout_box_count: usize,
197 pub layout_box_child_count: usize,
199 pub layout_box_child_capacity: usize,
201 pub layout_box_heap_bytes: usize,
203 pub modifier_slice_count: usize,
205 pub modifier_slice_heap_bytes: usize,
207 pub modifier_draw_command_count: usize,
209 pub modifier_draw_command_capacity: usize,
211 pub modifier_pointer_input_count: usize,
213 pub modifier_pointer_input_capacity: usize,
215 pub modifier_text_content_count: usize,
217 pub modifier_text_style_count: usize,
219 pub modifier_text_layout_options_count: usize,
221 pub modifier_prepared_text_layout_count: usize,
223 pub modifier_graphics_layer_count: usize,
225 pub modifier_graphics_layer_resolver_count: usize,
227 pub semantics_node_count: usize,
229 pub semantics_action_count: usize,
231 pub semantics_action_capacity: usize,
233 pub semantics_child_count: usize,
235 pub semantics_child_capacity: usize,
237 pub semantics_description_count: usize,
239 pub semantics_description_bytes: usize,
241 pub semantics_heap_bytes: usize,
243}
244
245impl LayoutAllocationDebugStats {
246 fn add_modifier_slice(&mut self, stats: ModifierNodeSlicesDebugStats) {
247 self.modifier_slice_count += 1;
248 self.modifier_slice_heap_bytes += stats.heap_bytes;
249 self.modifier_draw_command_count += stats.draw_command_count;
250 self.modifier_draw_command_capacity += stats.draw_command_capacity;
251 self.modifier_pointer_input_count += stats.pointer_input_count;
252 self.modifier_pointer_input_capacity += stats.pointer_input_capacity;
253 self.modifier_text_content_count += usize::from(stats.has_text_content);
254 self.modifier_text_style_count += usize::from(stats.has_text_style);
255 self.modifier_text_layout_options_count += usize::from(stats.has_text_layout_options);
256 self.modifier_prepared_text_layout_count += usize::from(stats.has_prepared_text_layout);
257 self.modifier_graphics_layer_count += usize::from(stats.has_graphics_layer);
258 self.modifier_graphics_layer_resolver_count +=
259 usize::from(stats.has_graphics_layer_resolver);
260 }
261}
262
263#[derive(Debug, Clone)]
265pub struct LayoutTree {
266 root: LayoutBox,
267}
268
269impl LayoutTree {
270 pub fn new(root: LayoutBox) -> Self {
272 Self { root }
273 }
274
275 pub fn root(&self) -> &LayoutBox {
277 &self.root
278 }
279
280 pub fn root_mut(&mut self) -> &mut LayoutBox {
282 &mut self.root
283 }
284
285 pub fn into_root(self) -> LayoutBox {
287 self.root
288 }
289
290 pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
292 let mut stats = LayoutAllocationDebugStats::default();
293 record_layout_box_allocation_stats(&self.root, &mut stats);
294 stats
295 }
296}
297
298#[derive(Debug, Clone)]
300pub struct LayoutBox {
301 pub node_id: NodeId,
303 pub node_generation: u32,
305 pub rect: GeometryRect,
308 pub content_offset: Point,
310 pub node_data: LayoutNodeData,
312 pub children: Vec<Self>,
314}
315
316impl LayoutBox {
317 pub fn new(
319 node_id: NodeId,
320 rect: GeometryRect,
321 content_offset: Point,
322 node_data: LayoutNodeData,
323 children: Vec<Self>,
324 ) -> Self {
325 Self {
326 node_id,
327 node_generation: 0,
328 rect,
329 content_offset,
330 node_data,
331 children,
332 }
333 }
334}
335
336pub trait LayoutEngine {
338 fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError>;
340}
341
342impl LayoutEngine for MemoryApplier {
343 fn compute_layout(&mut self, root: NodeId, max_size: Size) -> Result<LayoutTree, NodeError> {
344 let measurements = measure_layout(self, root, max_size)?;
345 measurements
346 .into_layout_tree()
347 .ok_or(NodeError::MissingContext {
348 id: root,
349 reason: "layout tree was not requested",
350 })
351 }
352}
353
354#[derive(Debug, Clone)]
356pub struct LayoutMeasurements {
357 root: Rc<MeasuredNode>,
358 semantics: Option<Box<SemanticsTree>>,
361 layout_tree: Option<Box<LayoutTree>>,
362}
363
364impl LayoutMeasurements {
365 fn new(
366 root: Rc<MeasuredNode>,
367 semantics: Option<SemanticsTree>,
368 layout_tree: Option<LayoutTree>,
369 ) -> Self {
370 Self {
371 root,
372 semantics: semantics.map(Box::new),
373 layout_tree: layout_tree.map(Box::new),
374 }
375 }
376
377 pub fn root_size(&self) -> Size {
379 self.root.size
380 }
381
382 pub fn semantics_tree(&self) -> Option<&SemanticsTree> {
384 self.semantics.as_deref()
385 }
386
387 pub fn debug_allocation_stats(&self) -> LayoutAllocationDebugStats {
389 let mut stats = self
390 .layout_tree
391 .as_deref()
392 .map(LayoutTree::debug_allocation_stats)
393 .unwrap_or_default();
394 if let Some(semantics) = &self.semantics {
395 record_semantics_allocation_stats(semantics.root(), &mut stats);
396 }
397 stats
398 }
399
400 pub fn into_layout_tree(self) -> Option<LayoutTree> {
402 self.layout_tree.map(|tree| *tree)
403 }
404
405 pub fn layout_tree(&self) -> Option<LayoutTree> {
407 self.layout_tree.as_deref().cloned()
408 }
409}
410
411pub fn build_semantics_tree_from_layout_tree(layout_tree: &LayoutTree) -> SemanticsTree {
416 SemanticsTree::new(build_semantics_node_from_layout_box(
417 layout_tree.root(),
418 Point::default(),
419 ))
420}
421
422pub fn build_layout_tree_from_applier(
428 applier: &mut MemoryApplier,
429 root: NodeId,
430) -> Result<Option<LayoutTree>, NodeError> {
431 let origin = layout_tree_origin(read_layout_node(applier, root, |state, _| state)?);
432 let mut child_stack = Vec::new();
433 place_layout_box(
434 applier,
435 root,
436 origin,
437 ProjectiveTransform::identity(),
438 &mut child_stack,
439 )
440 .map(|root| root.map(LayoutTree::new))
441}
442
443pub fn has_placed_content(applier: &mut MemoryApplier, root: NodeId) -> Result<bool, NodeError> {
447 walk_placed_boxes(applier, root, |_| std::ops::ControlFlow::Break(()))
448}
449
450pub fn placed_content_extent(
455 applier: &mut MemoryApplier,
456 root: NodeId,
457) -> Result<Option<Size>, NodeError> {
458 let mut extent: Option<Size> = None;
459 walk_placed_boxes(applier, root, |rect| {
460 let (right, bottom) = (rect.x + rect.width, rect.y + rect.height);
461 extent = Some(extent.map_or_else(
462 || Size::new(right, bottom),
463 |extent| Size::new(extent.width.max(right), extent.height.max(bottom)),
464 ));
465 std::ops::ControlFlow::Continue(())
466 })?;
467 Ok(extent)
468}
469
470fn walk_placed_boxes(
475 applier: &mut MemoryApplier,
476 root: NodeId,
477 mut visit: impl FnMut(GeometryRect) -> std::ops::ControlFlow<()>,
478) -> Result<bool, NodeError> {
479 let mut pending: Vec<(NodeId, Point)> = Vec::new();
480 let placed = read_layout_node(applier, root, |state, children| {
481 if state.is_placed() {
482 pending.extend(
483 children
484 .iter()
485 .map(|&child| (child, state.content_offset())),
486 );
487 }
488 })?;
489 if placed.is_none() {
490 return Ok(false);
491 }
492 while let Some((node_id, origin)) = pending.pop() {
493 if crate::modifier::is_window_root(applier, node_id) {
494 continue;
495 }
496 let rect = read_layout_node(applier, node_id, |state, children| {
497 if !state.is_placed() {
498 return None;
499 }
500 let (rect, content) = semantics_placement(&state, Some(origin));
501 pending.extend(children.iter().map(|&child| (child, content)));
502 Some(rect)
503 })?
504 .flatten();
505 if let Some(rect) = rect
506 && rect.width > 0.0
507 && rect.height > 0.0
508 && visit(rect).is_break()
509 {
510 return Ok(true);
511 }
512 }
513 Ok(false)
514}
515
516fn layout_tree_origin(root: Option<LayoutState>) -> Point {
517 let Some(state) = root else {
518 return Point::default();
519 };
520 let position = state.position();
521 Point {
522 x: -position.x,
523 y: -position.y,
524 }
525}
526
527fn finish_placement(applier: &mut MemoryApplier, root: NodeId, context: &AppContext) {
530 if applier
531 .with_node::<LayoutNode, _>(root, |node| {
532 node.place(ChildPlacement::default(), None, context);
533 })
534 .is_err()
535 {
536 let _ = applier.with_node::<SubcomposeLayoutNode, _>(root, |node| {
537 node.place(ChildPlacement::default(), None, context);
538 });
539 }
540 if let Some(pass) = context.placement_pass().with_unplaced_nodes() {
541 record_unplaced_parents(applier, root, pass, context);
542 }
543}
544
545#[inline(never)]
549fn record_unplaced_parents(
550 applier: &mut MemoryApplier,
551 root: NodeId,
552 pass: u64,
553 context: &AppContext,
554) {
555 let mut parents = vec![root];
556 let mut children = Vec::new();
557 while let Some(parent) = parents.pop() {
558 children.clear();
559 if !matches!(
560 read_layout_node(applier, parent, |_, ids| children.extend_from_slice(ids)),
561 Ok(Some(()))
562 ) {
563 continue;
564 }
565 for &child in &children {
566 match read_layout_node(applier, child, |state, _| state.unplaced_in(pass)) {
567 Ok(Some(true)) => context.record_geometry_scene_node(parent),
568 Ok(Some(false)) => parents.push(child),
569 Ok(None) | Err(_) => {}
570 }
571 }
572 }
573}
574
575fn read_layout_node<R>(
576 applier: &mut MemoryApplier,
577 node_id: NodeId,
578 mut read: impl FnMut(LayoutState, &[NodeId]) -> R,
579) -> Result<Option<R>, NodeError> {
580 read_live_layout_node(applier, node_id, |node| {
581 node.with_children(|children| read(node.state(), children))
582 })
583}
584
585fn place_layout_box(
590 applier: &mut MemoryApplier,
591 node_id: NodeId,
592 parent_content_origin: Point,
593 parent_transform: ProjectiveTransform,
594 child_stack: &mut Vec<NodeId>,
595) -> Result<Option<LayoutBox>, NodeError> {
596 let first_child = child_stack.len();
597 let Some(state) = read_layout_node(applier, node_id, |state, children| {
598 if state.is_placed() {
599 child_stack.extend_from_slice(children);
600 }
601 state
602 })?
603 else {
604 return Ok(None);
605 };
606 if !state.is_placed() {
607 return Ok(None);
608 }
609
610 let top_left = Point {
611 x: parent_content_origin.x + state.position().x,
612 y: parent_content_origin.y + state.position().y,
613 };
614 let rect = GeometryRect {
615 x: top_left.x,
616 y: top_left.y,
617 width: state.size().width,
618 height: state.size().height,
619 };
620 let (data, window_transform) =
621 snapshot_node_data(applier, node_id, top_left, state.size(), parent_transform)?;
622 let child_origin = Point {
623 x: top_left.x + state.content_offset().x,
624 y: top_left.y + state.content_offset().y,
625 };
626 let end = child_stack.len();
627 let mut children = Vec::with_capacity(end - first_child);
628 for index in first_child..end {
629 let child_id = child_stack[index];
630 if crate::modifier::is_window_root(applier, child_id) {
631 continue;
632 }
633 if let Some(child) = place_layout_box(
634 applier,
635 child_id,
636 child_origin,
637 window_transform,
638 child_stack,
639 )? {
640 children.push(child);
641 }
642 }
643 child_stack.truncate(first_child);
644
645 Ok(Some(LayoutBox {
646 node_generation: applier.node_generation(node_id),
647 ..LayoutBox::new(node_id, rect, state.content_offset(), data, children)
648 }))
649}
650
651pub fn top_modal_from_applier(
656 applier: &mut MemoryApplier,
657 root: NodeId,
658) -> Result<Option<NodeId>, NodeError> {
659 enum Step {
664 Enter { node: NodeId, child: bool },
665 Modal(NodeId),
666 }
667
668 fn push_placed(
669 steps: &mut Vec<Step>,
670 node: NodeId,
671 reach: cranpose_foundation::SemanticsReach,
672 size: Size,
673 children: impl IntoIterator<Item = NodeId>,
674 ) {
675 if reach.hidden {
676 return;
677 }
678 if reach.is_modal && modal_takes_space(size) {
679 steps.push(Step::Modal(node));
680 }
681 steps.extend(
682 children
683 .into_iter()
684 .map(|node| Step::Enter { node, child: true }),
685 );
686 }
687
688 let mut steps = vec![Step::Enter {
689 node: root,
690 child: false,
691 }];
692 while let Some(step) = steps.pop() {
693 let (node_id, child) = match step {
694 Step::Modal(node) => return Ok(Some(node)),
695 Step::Enter { node, child } => (node, child),
696 };
697 let node = match applier.get_mut(node_id) {
698 Ok(node) => node.as_any_mut(),
699 Err(NodeError::Missing { .. }) => continue,
700 Err(error) => return Err(error),
701 };
702 if let Some(layout) = node.downcast_mut::<LayoutNode>() {
703 let state = layout.layout_state();
704 if state.is_placed() && !(child && layout.is_window_root()) {
705 let children = layout.children.iter().copied();
706 push_placed(
707 &mut steps,
708 node_id,
709 layout.semantics_reach(),
710 state.size(),
711 children,
712 );
713 }
714 } else if let Some(subcompose) = node.downcast_mut::<SubcomposeLayoutNode>() {
715 let state = subcompose.layout_state();
716 if state.is_placed() {
717 let reach = subcompose.semantics_reach();
718 subcompose.with_active_children(|children| {
719 push_placed(
720 &mut steps,
721 node_id,
722 reach,
723 state.size(),
724 children.iter().copied(),
725 );
726 });
727 }
728 }
729 }
730 Ok(None)
731}
732
733#[derive(Clone, Copy, Debug, PartialEq, Eq)]
735pub struct MeasureLayoutOptions {
736 pub collect_semantics: bool,
738 pub build_layout_tree: bool,
740}
741
742impl Default for MeasureLayoutOptions {
743 fn default() -> Self {
744 Self {
745 collect_semantics: true,
746 build_layout_tree: true,
747 }
748 }
749}
750
751pub fn tree_needs_layout(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
759 Ok(applier.get_mut(root)?.layout_dirty())
760}
761
762pub fn tree_needs_semantics(applier: &mut dyn Applier, root: NodeId) -> Result<bool, NodeError> {
768 Ok(applier.get_mut(root)?.needs_semantics())
769}
770
771#[cfg(test)]
772pub(crate) fn bubble_layout_dirty(applier: &mut MemoryApplier, node_id: NodeId) {
773 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
774}
775
776pub fn measure_layout(
778 applier: &mut MemoryApplier,
779 root: NodeId,
780 max_size: Size,
781) -> Result<LayoutMeasurements, NodeError> {
782 measure_layout_with_options(applier, root, max_size, MeasureLayoutOptions::default())
783}
784
785pub fn measure_layout_with_options(
788 applier: &mut MemoryApplier,
789 root: NodeId,
790 max_size: Size,
791 options: MeasureLayoutOptions,
792) -> Result<LayoutMeasurements, NodeError> {
793 let mut stages = LayoutStages::start(layout_measure_telemetry_threshold_ms().is_some());
794 let context = crate::render_state::require_current_app_context("a layout pass");
795 context.begin_text_layout_pass();
796 context.placement_pass().begin();
797 process_pending_layout_repasses(&context, applier, root)?;
798 stages.reached();
799
800 let constraints = Constraints {
801 min_width: 0.0,
802 max_width: max_size.width,
803 min_height: 0.0,
804 max_height: max_size.height,
805 };
806
807 let (needs_remeasure, _needs_semantics, cached_epoch) = match applier
808 .with_node::<LayoutNode, _>(root, |node| {
809 (
810 node.needs_measure() || Node::descendant_needs_measure(node),
811 node.needs_semantics(),
812 node.cache().epoch(),
813 )
814 }) {
815 Ok(tuple) => tuple,
816 Err(NodeError::TypeMismatch { .. }) => {
817 let node = applier.get_mut(root)?;
818 let measure_dirty = node.needs_measure() || node.descendant_needs_measure();
819 let semantics_dirty = node.needs_semantics();
820 (measure_dirty, semantics_dirty, 0)
821 }
822 Err(err) => return Err(err),
823 };
824
825 let epoch = if needs_remeasure {
826 context.next_layout_cache_epoch()
827 } else if cached_epoch != 0 {
828 cached_epoch
829 } else {
830 context.current_layout_cache_epoch()
831 };
832
833 let guard = ApplierSlotGuard::new(applier, &context);
834 let applier_host = guard.host();
835 stages.reached();
836
837 let builder = LayoutBuilder::new_with_epoch(Rc::clone(applier_host), epoch, &context);
838 stages.reached();
839
840 let measured = builder
841 .state
842 .measure_root(root, normalize_constraints(constraints))?;
843 stages.reached();
844
845 if let Ok(mut applier) = applier_host.try_borrow_typed() {
846 finish_placement(&mut applier, root, &context);
847 }
848 stages.reached();
849
850 let (layout_tree, semantics) = {
851 let mut applier_ref = applier_host.borrow_typed();
852 let layout_tree = if options.build_layout_tree {
853 Some(build_layout_tree(&mut applier_ref, &measured)?)
854 } else {
855 None
856 };
857 let semantics = if options.collect_semantics {
858 let semantics_tree = if let Some(layout_tree) = layout_tree.as_ref() {
859 clear_semantics_dirty_flags(&mut applier_ref, &measured)?;
860 build_semantics_tree_from_layout_tree(layout_tree)
861 } else {
862 build_semantics_tree_from_live_nodes(&mut applier_ref, &measured)?
863 };
864 Some(semantics_tree)
865 } else {
866 None
867 };
868 (layout_tree, semantics)
869 };
870 stages.reached();
871
872 drop(builder);
873 stages.reached();
874
875 drop(guard);
876 stages.reached();
877
878 log_layout_measure_telemetry(root, &stages);
879
880 Ok(LayoutMeasurements::new(measured, semantics, layout_tree))
881}
882
883fn process_pending_layout_repasses(
884 context: &AppContext,
885 applier: &mut MemoryApplier,
886 root: NodeId,
887) -> Result<(), NodeError> {
888 for node_id in context.take_modifier_slice_repass_nodes() {
889 if let Ok(node) = applier.get_mut(node_id) {
890 let any = node.as_any_mut();
891 if let Some(layout) = any.downcast_mut::<LayoutNode>() {
892 layout.mark_modifier_slices_dirty();
893 } else if let Some(subcompose) = any.downcast_mut::<SubcomposeLayoutNode>() {
894 subcompose.mark_modifier_slices_dirty();
895 }
896 }
897 }
898 let measure_repass_nodes = context.take_measure_repass_nodes();
899 let repass_nodes = context.take_layout_repass_nodes();
900 if measure_repass_nodes.is_empty() && repass_nodes.is_empty() {
901 return Ok(());
902 }
903 for node_id in measure_repass_nodes {
904 cranpose_core::bubble_measure_dirty(applier as &mut dyn Applier, node_id);
905 }
906 for node_id in repass_nodes {
907 cranpose_core::bubble_layout_dirty(applier as &mut dyn Applier, node_id);
908 }
909 applier.get_mut(root)?.mark_descendant_needs_layout(false);
910 Ok(())
911}
912
913struct LayoutBuilder<'a> {
916 state: Rc<LayoutBuilderState>,
917 context: &'a AppContext,
918}
919
920impl<'a> LayoutBuilder<'a> {
921 fn new_with_epoch(
922 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
923 epoch: u64,
924 context: &'a Rc<AppContext>,
925 ) -> Self {
926 Self {
927 state: Rc::new(LayoutBuilderState::new_with_epoch(
928 applier,
929 epoch,
930 Rc::clone(context),
931 )),
932 context,
933 }
934 }
935}
936
937impl Drop for LayoutBuilder<'_> {
938 fn drop(&mut self) {
939 if Rc::strong_count(&self.state) != 1 {
940 return;
941 }
942 let Ok(mut frame_arena) = self.state.frame_arena.try_borrow_mut() else {
943 return;
944 };
945 self.context
946 .replace_layout_frame_arena(std::mem::take(&mut *frame_arena));
947 }
948}
949
950struct LayoutBuilderState {
951 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
952 runtime_handle: RefCell<Option<RuntimeHandle>>,
953 cache_epoch: u64,
954 cache_floor: u64,
955 frame_arena: RefCell<FrameLayoutArena>,
956 context: Rc<AppContext>,
959 measure_telemetry_ms: Option<f64>,
962}
963
964struct LayoutRuntimeFrameBindingCleanup<'a> {
965 state: &'a RefCell<LayoutRuntimeState>,
966}
967
968impl Drop for LayoutRuntimeFrameBindingCleanup<'_> {
969 fn drop(&mut self) {
970 self.state.borrow().unbind_children();
971 }
972}
973
974enum LayoutNodeVisit<'a> {
975 Cached(Rc<MeasuredNode>),
976 Keep(KeptNode),
979 Measure(LayoutNodeMeasure<'a>),
980}
981
982struct KeptNode {
985 measured: Rc<MeasuredNode>,
986 parts: Rc<LayoutParts>,
987 ignores_child_sizes: bool,
990}
991
992struct KeptChildren {
995 measured: Rc<MeasuredNode>,
996 resized: bool,
997}
998
999#[derive(Clone, Copy)]
1001struct ChangedChild {
1002 constraints: Constraints,
1003 placed: bool,
1004}
1005
1006enum ChildChange {
1008 Unchanged,
1009 Changed(ChangedChild),
1010 Refuse,
1012}
1013
1014struct ChildView {
1017 self_dirty: bool,
1018 parent_data: Option<cranpose_ui_layout::ParentData>,
1021 cached_constraints: Option<Constraints>,
1023 read_intrinsics: bool,
1024 placed: bool,
1025}
1026
1027impl ChildView {
1028 fn of(node: &LiveLayoutNode<'_>) -> Option<Self> {
1030 let cache = node.cache();
1031 let dirt = cache.dirt();
1032 let self_dirty = dirt.measure || dirt.layout;
1033 if !self_dirty && !dirt.descendant.layout {
1034 return None;
1035 }
1036 Some(Self {
1037 self_dirty,
1038 parent_data: self_dirty.then(|| node.parent_data()),
1039 cached_constraints: cache.measured_constraints(),
1040 read_intrinsics: cache.has_intrinsics(),
1041 placed: node.is_placed(),
1042 })
1043 }
1044}
1045
1046struct LayoutNodeMeasure<'a> {
1047 parts: Rc<LayoutParts>,
1048 chain: ModifierChainInputs,
1049 pools: VecPools<'a>,
1050 spare: MeasuredSpare,
1051}
1052
1053#[expect(
1054 clippy::multiple_inherent_impl,
1055 reason = "the bottom-up pass, the chain measure and the subcompose measure in their files"
1056)]
1057impl LayoutBuilderState {
1058 fn new_with_epoch(
1059 applier: Rc<ConcreteApplierHost<MemoryApplier>>,
1060 epoch: u64,
1061 context: Rc<AppContext>,
1062 ) -> Self {
1063 let runtime_handle = applier.borrow_typed().runtime_handle();
1064
1065 Self {
1066 applier,
1067 runtime_handle: RefCell::new(runtime_handle),
1068 cache_epoch: epoch,
1069 cache_floor: context.layout_cache_floor(),
1070 frame_arena: RefCell::new(context.take_layout_frame_arena()),
1071 context,
1072 measure_telemetry_ms: layout_measure_telemetry_threshold_ms(),
1073 }
1074 }
1075
1076 fn with_applier_result<R>(
1077 &self,
1078 f: impl FnOnce(&mut MemoryApplier) -> Result<R, NodeError>,
1079 ) -> Result<R, NodeError> {
1080 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1081 return Err(NodeError::MissingContext {
1082 id: NodeId::default(),
1083 reason: "applier already borrowed",
1084 });
1085 };
1086 f(&mut applier)
1087 }
1088
1089 fn measure_subcompose_node(
1090 self: &Rc<Self>,
1091 node_id: NodeId,
1092 constraints: Constraints,
1093 ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1094 self.clear_subcompose_placed(node_id);
1095 if let Some(cached) = self.subcompose_measurement_to_keep(node_id, constraints)
1096 && let Some(kept) = self.keep_subcompose_measurement(node_id, constraints, &cached)?
1097 {
1098 return Ok(("subcompose kept", kept));
1099 }
1100 Ok(match self.try_measure_subcompose(node_id, constraints)? {
1101 Some(measured) => {
1102 self.store_subcompose_measurement(node_id, constraints, &measured);
1103 ("subcompose", measured)
1104 }
1105 None => (
1106 "fallback",
1107 Rc::new(MeasuredNode::new(
1108 node_id,
1109 Size::default(),
1110 Point { x: 0.0, y: 0.0 },
1111 Point::default(),
1112 Vec::new(),
1113 )),
1114 ),
1115 })
1116 }
1117
1118 fn subcompose_measurement_to_keep(
1122 &self,
1123 node_id: NodeId,
1124 constraints: Constraints,
1125 ) -> Option<Rc<MeasuredNode>> {
1126 let mut applier = self.applier.try_borrow_typed().ok()?;
1127 applier
1128 .with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1129 if node.needs_measure() || Node::needs_layout(node) {
1130 return None;
1131 }
1132 let cache = node.cache();
1133 if cache.epoch() < self.cache_floor {
1134 return None;
1135 }
1136 cache.get_measurement(constraints)
1137 })
1138 .ok()
1139 .flatten()
1140 }
1141
1142 fn keep_subcompose_measurement(
1148 self: &Rc<Self>,
1149 node_id: NodeId,
1150 constraints: Constraints,
1151 cached: &Rc<MeasuredNode>,
1152 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1153 let kept = self.remeasure_changed_children(
1154 cached,
1155 false,
1156 |child_id| self.subcompose_child_change(child_id),
1157 |child_id| self.mark_measured_without_policy(child_id),
1158 )?;
1159 let Some(KeptChildren { measured: kept, .. }) = kept else {
1160 return Ok(None);
1161 };
1162 self.with_applier_result(|applier| {
1163 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1164 node.cache()
1165 .store_measurement(constraints, Rc::clone(&kept));
1166 node.clear_needs_layout();
1167 })
1168 })?;
1169 Ok(Some(kept))
1170 }
1171
1172 fn subcompose_child_change(&self, child_id: NodeId) -> Result<ChildChange, NodeError> {
1176 self.child_change(child_id, |view| {
1177 match view.cached_constraints.filter(|_| !view.self_dirty) {
1178 Some(constraints) if !view.read_intrinsics => ChildChange::Changed(ChangedChild {
1179 constraints,
1180 placed: view.placed,
1181 }),
1182 _ => ChildChange::Refuse,
1183 }
1184 })
1185 }
1186
1187 fn child_change(
1190 &self,
1191 child_id: NodeId,
1192 decide: impl FnOnce(ChildView) -> ChildChange,
1193 ) -> Result<ChildChange, NodeError> {
1194 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1195 return Ok(ChildChange::Refuse);
1196 };
1197 Ok(
1198 match read_live_layout_node(&mut applier, child_id, |node| ChildView::of(&node))? {
1199 None => ChildChange::Refuse,
1200 Some(None) => ChildChange::Unchanged,
1201 Some(Some(view)) => decide(view),
1202 },
1203 )
1204 }
1205
1206 fn mark_measured_without_policy(&self, child_id: NodeId) {
1210 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1211 return;
1212 };
1213 if let Ok(node) = applier.get_mut(child_id) {
1214 node.mark_descendant_needs_layout(true);
1215 }
1216 }
1217
1218 fn clear_subcompose_placed(&self, node_id: NodeId) {
1219 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1220 return;
1221 };
1222 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1223 node.clear_placed(&self.context);
1224 });
1225 }
1226
1227 fn measure_node(
1228 self: &Rc<Self>,
1229 node_id: NodeId,
1230 constraints: Constraints,
1231 ) -> Result<Rc<MeasuredNode>, NodeError> {
1232 let Some(threshold_ms) = self.measure_telemetry_ms else {
1233 return self
1234 .measure_node_untimed(node_id, constraints)
1235 .map(|(_, measured)| measured);
1236 };
1237 self.measure_node_timed(node_id, constraints, threshold_ms)
1238 }
1239
1240 #[inline(never)]
1243 fn measure_node_timed(
1244 self: &Rc<Self>,
1245 node_id: NodeId,
1246 constraints: Constraints,
1247 threshold_ms: f64,
1248 ) -> Result<Rc<MeasuredNode>, NodeError> {
1249 let start = Instant::now();
1250 let (kind, measured) = self.measure_node_untimed(node_id, constraints)?;
1251 log_node_measure_telemetry(kind, node_id, constraints, &measured, (threshold_ms, start));
1252 Ok(measured)
1253 }
1254
1255 fn measure_node_untimed(
1257 self: &Rc<Self>,
1258 node_id: NodeId,
1259 constraints: Constraints,
1260 ) -> Result<(&'static str, Rc<MeasuredNode>), NodeError> {
1261 if let Some(measured) = self.measure_layout_node(node_id, constraints)? {
1262 return Ok(("layout", measured));
1263 }
1264 self.measure_subcompose_node(node_id, constraints)
1265 }
1266
1267 fn try_measure_subcompose(
1268 self: &Rc<Self>,
1269 node_id: NodeId,
1270 constraints: Constraints,
1271 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1272 let (node_handle, resolved_modifiers, spare) = {
1273 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1274 return Ok(None);
1275 };
1276 let node = match applier.get_mut(node_id) {
1277 Ok(node) => node,
1278 Err(NodeError::Missing { .. }) => return Ok(None),
1279 Err(err) => return Err(err),
1280 };
1281 let any = node.as_any_mut();
1282 if let Some(subcompose) = any.downcast_mut::<SubcomposeLayoutNode>() {
1283 let handle = subcompose.handle();
1284 let resolved_modifiers = handle
1285 .resolved_modifiers()
1286 .on_device_grid(subcompose.density().density());
1287 let spare = MeasuredSpare::take(subcompose.cache());
1288 (handle, resolved_modifiers, spare)
1289 } else {
1290 return Ok(None);
1291 }
1292 };
1293
1294 let props = resolved_modifiers.layout_properties();
1295 let padding = resolved_modifiers.padding();
1296 let offset = resolved_modifiers.offset();
1297 let mut inner_constraints = normalize_constraints(subtract_padding(constraints, padding));
1298
1299 if let DimensionConstraint::Points(width) = props.width() {
1300 let constrained_width = width - padding.horizontal_sum();
1301 inner_constraints.max_width = inner_constraints.max_width.min(constrained_width);
1302 inner_constraints.min_width = inner_constraints.min_width.min(constrained_width);
1303 }
1304 if let DimensionConstraint::Points(height) = props.height() {
1305 let constrained_height = height - padding.vertical_sum();
1306 inner_constraints.max_height = inner_constraints.max_height.min(constrained_height);
1307 inner_constraints.min_height = inner_constraints.min_height.min(constrained_height);
1308 }
1309
1310 let measure_error = RefCell::new(None);
1311 let measured_children = node_handle.measured_children_scratch();
1312 let measure_result = self.subcompose_policy_result(
1313 &node_handle,
1314 node_id,
1315 inner_constraints,
1316 &measured_children,
1317 &measure_error,
1318 )?;
1319
1320 if let Some(err) = measure_error.borrow_mut().take() {
1321 return Err(err);
1322 }
1323
1324 let cranpose_ui_layout::MeasureResult {
1325 size: measured_size,
1326 alignment_lines: explicit_lines,
1327 placements,
1328 } = measure_result;
1329
1330 let mut width = measured_size.width + padding.horizontal_sum();
1331 let mut height = measured_size.height + padding.vertical_sum();
1332
1333 width = resolve_dimension(
1334 width,
1335 props.width(),
1336 props.min_width(),
1337 props.max_width(),
1338 constraints.min_width,
1339 constraints.max_width,
1340 );
1341 height = resolve_dimension(
1342 height,
1343 props.height(),
1344 props.min_height(),
1345 props.max_height(),
1346 constraints.min_height,
1347 constraints.max_height,
1348 );
1349
1350 let MeasuredSpare {
1351 node: spare,
1352 mut children,
1353 } = spare;
1354 children.reserve_exact(placements.len());
1355 let mut alignment_lines = AlignmentLines::default();
1356 let mut measured_children_by_id = measured_children.borrow_mut();
1357
1358 let mut applier = self.applier.try_borrow_typed().ok();
1361 if let Some(applier) = applier.as_mut() {
1362 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |parent_node| {
1363 parent_node.set_measured_size(Size { width, height }, &self.context);
1364 parent_node
1365 .layout_parts()
1366 .note_child_placements(&placements);
1367 parent_node.clear_needs_measure();
1368 parent_node.clear_needs_layout();
1369 });
1370 }
1371
1372 let placed_roots = node_handle.take_placed_roots();
1375 let mut carried = PlacedRootMeasurements::new(&placed_roots);
1376 for placement in &placements {
1377 let looked_up;
1378 let child: &MeasuredNode = if let Some(measured) = carried.next_measurement() {
1379 measured
1380 } else {
1381 looked_up =
1382 if let Some(measured) = measured_children_by_id.remove(&placement.node_id) {
1383 measured
1384 } else {
1385 drop(applier.take());
1386 let measured = self.measure_node(placement.node_id, inner_constraints)?;
1387 applier = self.applier.try_borrow_typed().ok();
1388 measured
1389 };
1390 &looked_up
1391 };
1392 let policy_position = Point {
1393 x: padding.left + placement.x,
1394 y: padding.top + placement.y,
1395 };
1396 let retained_position = Point {
1397 x: policy_position.x + child.offset.x,
1398 y: policy_position.y + child.offset.y,
1399 };
1400 let retained = ChildPlacement::new(retained_position, placement.z_index);
1401 alignment_lines.merge(
1402 child
1403 .alignment_lines_for_parent()
1404 .translated(policy_position.y),
1405 );
1406
1407 if let Some(applier) = applier.as_mut() {
1408 let _ = read_live_layout_node(applier, placement.node_id, |node| {
1409 node.place(retained, node_id, &self.context);
1410 });
1411 }
1412
1413 children.push(MeasuredChild::new(child, policy_position));
1414 }
1415 drop(applier);
1416
1417 node_handle.recycle_placed_roots(placed_roots);
1418 node_handle.recycle_placement_scratch(placements);
1419
1420 Ok(Some(
1421 MeasuredNode::new(
1422 node_id,
1423 Size { width, height },
1424 offset,
1425 Point::default(),
1426 children,
1427 )
1428 .with_alignment_lines(
1429 alignment_lines
1430 .with_overrides(explicit_lines.translated(padding.top))
1431 .translated(offset.y),
1432 )
1433 .into_rc(spare),
1434 ))
1435 }
1436
1437 fn measure_layout_node(
1438 self: &Rc<Self>,
1439 node_id: NodeId,
1440 constraints: Constraints,
1441 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1442 let mut keep = true;
1443 let (mut applier, measure) = loop {
1444 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1445 return Ok(None);
1446 };
1447 match applier.with_node::<LayoutNode, _>(node_id, |node| {
1448 self.visit_layout_node(node, constraints, keep)
1449 }) {
1450 Ok(LayoutNodeVisit::Measure(measure)) => break (applier, measure),
1451 Ok(LayoutNodeVisit::Cached(measured)) => return Ok(Some(measured)),
1452 Ok(LayoutNodeVisit::Keep(node)) => {
1453 drop(applier);
1454 if let Some(kept) = self.keep_measurement(constraints, node)? {
1455 return Ok(Some(kept));
1456 }
1457 keep = false;
1458 }
1459 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
1460 Err(err) => return Err(err),
1461 }
1462 };
1463 let LayoutNodeMeasure {
1464 parts,
1465 chain,
1466 mut pools,
1467 spare,
1468 } = measure;
1469
1470 let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
1471 state: parts.runtime_state(),
1472 };
1473 self.bind_layout_children(
1474 &mut applier,
1475 (node_id, &parts),
1476 &pools.child_ids,
1477 ChildPass::Measure,
1478 )?;
1479 drop(applier);
1480
1481 let runtime_state = parts.runtime_state().borrow();
1482 let measurement = self.measure_through_modifier_chain(
1483 node_id,
1484 &runtime_state,
1485 chain,
1486 constraints,
1487 &mut pools.placements,
1488 &mut pools.placement_indices,
1489 );
1490
1491 if let Some(err) = runtime_state.take_child_error() {
1492 for child_state in runtime_state.child_states() {
1493 child_state.measured.take();
1494 }
1495 if runtime_state.take_moved_coordinators() {
1496 let mut layout_state = parts.layout_state().borrow_mut();
1497 layout_state.note_geometry_change(node_id, &self.context);
1498 }
1499 return Err(err);
1500 }
1501
1502 let MeasuredSpare {
1503 node: spare,
1504 mut children,
1505 } = spare;
1506 runtime_state.measured_children(
1507 &pools.placements,
1508 &pools.placement_indices,
1509 measurement.content_offset,
1510 &mut children,
1511 );
1512 let measured = MeasuredNode::new(
1513 node_id,
1514 measurement.size,
1515 measurement.offset,
1516 measurement.content_offset,
1517 children,
1518 )
1519 .with_alignment_lines(measurement.alignment_lines)
1520 .with_window_root(measurement.window_root)
1521 .into_rc(spare);
1522
1523 let cache = parts.cache();
1525 cache.store_held_measurement(
1526 constraints,
1527 Rc::clone(&measured),
1528 measurement.hold,
1529 measurement.ignores_child_sizes,
1530 );
1531 let mut layout_state = parts.layout_state().borrow_mut();
1532 if runtime_state.take_moved_coordinators() {
1533 layout_state.note_geometry_change(node_id, &self.context);
1534 }
1535 cache.clear_needs_measure();
1536 cache.clear_layout_dirt();
1537 layout_state.set_size(measurement.size, &self.context);
1538 layout_state.set_content_offset(measurement.content_offset);
1539
1540 Ok(Some(measured))
1541 }
1542
1543 fn layout_node_intrinsic(
1548 self: &Rc<Self>,
1549 node_id: NodeId,
1550 kind: IntrinsicKind,
1551 ) -> Result<Option<f32>, NodeError> {
1552 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1553 return Ok(None);
1554 };
1555 let mut pools = VecPools::acquire(&self.frame_arena);
1556 let bound = applier.with_node::<LayoutNode, _>(node_id, |node| {
1557 let parts = Rc::clone(node.layout_parts());
1558 let chain = parts.runtime_state().borrow_mut().bind_node(node);
1559 pools.child_ids.extend_from_slice(&node.children);
1560 (parts, chain)
1561 });
1562 let (parts, chain) = match bound {
1563 Ok(bound) => bound,
1564 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => return Ok(None),
1565 Err(err) => return Err(err),
1566 };
1567 let _frame_binding_cleanup = LayoutRuntimeFrameBindingCleanup {
1568 state: parts.runtime_state(),
1569 };
1570 self.bind_layout_children(
1571 &mut applier,
1572 (node_id, &parts),
1573 &pools.child_ids,
1574 ChildPass::Intrinsics,
1575 )?;
1576 drop(applier);
1577
1578 let runtime_state = parts.runtime_state().borrow();
1579 let value = if chain.uses_chain {
1580 let scope = crate::density::DensityMeasureScope::new(chain.density);
1581 let frame = CoordinatorFrame::new(
1582 &runtime_state.measure_policy,
1583 &scope,
1584 runtime_state.child_measurables(),
1585 runtime_state.child_states(),
1586 &mut pools.placements,
1587 &mut pools.placement_indices,
1588 );
1589 runtime_state
1590 .coordinator_chain
1591 .intrinsic_from(0, &frame, kind)
1592 } else {
1593 policy_intrinsic(
1594 runtime_state.measure_policy.as_ref(),
1595 runtime_state.child_measurables(),
1596 kind,
1597 )
1598 };
1599 match runtime_state.take_child_error() {
1600 Some(err) => Err(err),
1601 None => Ok(Some(value)),
1602 }
1603 }
1604
1605 fn measurement_to_keep(
1608 &self,
1609 node: &LayoutNode,
1610 constraints: Constraints,
1611 ) -> Option<Rc<MeasuredNode>> {
1612 if node.needs_measure() || node.needs_layout() || node.is_virtual() {
1613 return None;
1614 }
1615 let cache = node.cache();
1616 if cache.epoch() < self.cache_floor {
1617 return None;
1618 }
1619 cache.get_measurement(constraints)
1620 }
1621
1622 #[inline(never)]
1627 fn keep_measurement(
1628 self: &Rc<Self>,
1629 constraints: Constraints,
1630 node: KeptNode,
1631 ) -> Result<Option<Rc<MeasuredNode>>, NodeError> {
1632 let KeptNode {
1633 measured,
1634 parts,
1635 ignores_child_sizes,
1636 } = node;
1637 let mut next_state = 0;
1638 let kept = self.remeasure_changed_children(
1639 &measured,
1640 ignores_child_sizes,
1641 |child_id| self.layout_child_change(parts.runtime_state(), &mut next_state, child_id),
1642 |_| {},
1643 )?;
1644 let Some(KeptChildren {
1645 measured: kept,
1646 resized,
1647 }) = kept
1648 else {
1649 return Ok(None);
1650 };
1651 let cache = parts.cache();
1652 if resized {
1653 cache.store_resized_measurement(constraints, Rc::clone(&kept));
1654 } else {
1655 cache.store_measurement(constraints, Rc::clone(&kept));
1656 }
1657 cache.clear_layout_dirt();
1658 Ok(Some(kept))
1659 }
1660
1661 fn remeasure_changed_children(
1668 self: &Rc<Self>,
1669 cached: &Rc<MeasuredNode>,
1670 ignores_child_sizes: bool,
1671 mut change: impl FnMut(NodeId) -> Result<ChildChange, NodeError>,
1672 mut remeasured: impl FnMut(NodeId),
1673 ) -> Result<Option<KeptChildren>, NodeError> {
1674 let mut resized = false;
1675 for previous in &cached.children {
1676 let child = match change(previous.node_id)? {
1677 ChildChange::Unchanged => continue,
1678 ChildChange::Refuse => return Ok(None),
1679 ChildChange::Changed(child) => child,
1680 };
1681 let measured = self.measure_node(previous.node_id, child.constraints)?;
1682 remeasured(previous.node_id);
1683 let now = ForParent::of(&measured);
1684 if !previous.for_parent.same_as(&now) {
1685 if !(ignores_child_sizes && previous.for_parent.same_but_size(&now)) {
1686 return Ok(None);
1687 }
1688 resized = true;
1689 }
1690 if child.placed {
1691 self.place_where_it_was(cached.node_id, previous.node_id);
1692 }
1693 }
1694 Ok(Some(KeptChildren {
1695 measured: Rc::clone(cached),
1696 resized,
1697 }))
1698 }
1699
1700 fn layout_child_change(
1705 &self,
1706 runtime_state: &RefCell<LayoutRuntimeState>,
1707 next_state: &mut usize,
1708 child_id: NodeId,
1709 ) -> Result<ChildChange, NodeError> {
1710 let runtime_state = runtime_state.borrow();
1711 let Some((offset, state)) = runtime_state
1712 .child_states()
1713 .get(*next_state..)
1714 .unwrap_or_default()
1715 .iter()
1716 .enumerate()
1717 .find(|(_, state)| state.node_id == child_id)
1718 else {
1719 return Ok(ChildChange::Refuse);
1720 };
1721 *next_state += offset + 1;
1722 if read_cell(&state.parts, |parts| {
1724 parts
1725 .as_ref()
1726 .is_some_and(|parts| !parts.cache().dirt().any())
1727 }) {
1728 return Ok(ChildChange::Unchanged);
1729 }
1730 self.child_change(child_id, |view| {
1731 let parent_data_changed = view.parent_data.as_ref().is_some_and(|data| {
1732 state.reads_parent_data_change(&*runtime_state.measure_policy, data)
1733 });
1734 match state.measured_constraints.get() {
1735 Some(constraints) if !state.read_intrinsics.get() && !parent_data_changed => {
1736 ChildChange::Changed(ChangedChild {
1737 constraints,
1738 placed: view.placed,
1739 })
1740 }
1741 _ => ChildChange::Refuse,
1742 }
1743 })
1744 }
1745
1746 fn store_subcompose_measurement(
1749 &self,
1750 node_id: NodeId,
1751 constraints: Constraints,
1752 measured: &Rc<MeasuredNode>,
1753 ) {
1754 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1755 return;
1756 };
1757 let _ = applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
1758 node.cache()
1759 .store_measurement(constraints, Rc::clone(measured));
1760 });
1761 }
1762
1763 fn place_where_it_was(&self, parent: NodeId, child_id: NodeId) {
1766 let Ok(mut applier) = self.applier.try_borrow_typed() else {
1767 return;
1768 };
1769 let _ = read_live_layout_node(&mut applier, child_id, |child| {
1770 child.stay_placed(parent, &self.context);
1771 });
1772 }
1773
1774 fn visit_layout_node(
1775 &self,
1776 node: &mut LayoutNode,
1777 constraints: Constraints,
1778 keep: bool,
1779 ) -> LayoutNodeVisit<'_> {
1780 node.clear_placed(&self.context);
1781 if keep && let Some(measured) = self.measurement_to_keep(node, constraints) {
1782 return LayoutNodeVisit::Keep(KeptNode {
1783 ignores_child_sizes: node.cache().ignores_child_sizes_of(&measured),
1784 measured,
1785 parts: Rc::clone(node.layout_parts()),
1786 });
1787 }
1788 let cache = node.cache();
1789 cache.activate(self.cache_epoch);
1790 if !Node::layout_dirty(node)
1791 && let Some(cached) = cache.get_measurement(constraints)
1792 {
1793 node.clear_needs_measure();
1794 node.clear_needs_layout();
1795 return LayoutNodeVisit::Cached(cached);
1796 }
1797
1798 let spare = MeasuredSpare::take(cache);
1799 let parts = Rc::clone(node.layout_parts());
1800 let chain = parts.runtime_state().borrow_mut().bind_node(node);
1801 let mut pools = VecPools::acquire(&self.frame_arena);
1802 pools.child_ids.extend_from_slice(&node.children);
1803 LayoutNodeVisit::Measure(LayoutNodeMeasure {
1804 parts,
1805 chain,
1806 pools,
1807 spare,
1808 })
1809 }
1810}
1811
1812#[derive(Debug, Clone)]
1813pub(crate) struct MeasuredNode {
1814 node_id: NodeId,
1815 size: Size,
1816 offset: Point,
1817 content_offset: Point,
1818 alignment_lines: AlignmentLines,
1819 children: Vec<MeasuredChild>,
1820 window_root: bool,
1821}
1822
1823impl MeasuredNode {
1824 fn new(
1825 node_id: NodeId,
1826 size: Size,
1827 offset: Point,
1828 content_offset: Point,
1829 children: Vec<MeasuredChild>,
1830 ) -> Self {
1831 Self {
1832 node_id,
1833 size,
1834 offset,
1835 content_offset,
1836 alignment_lines: AlignmentLines::default(),
1837 children,
1838 window_root: false,
1839 }
1840 }
1841
1842 fn with_window_root(mut self, window_root: bool) -> Self {
1843 self.window_root = window_root;
1844 self
1845 }
1846
1847 fn with_alignment_lines(mut self, alignment_lines: AlignmentLines) -> Self {
1848 self.alignment_lines = alignment_lines;
1849 self
1850 }
1851
1852 pub(crate) fn alignment_lines_for_parent(&self) -> AlignmentLines {
1853 if self.window_root {
1854 AlignmentLines::default()
1855 } else {
1856 self.alignment_lines
1857 }
1858 }
1859
1860 pub(crate) fn size_for_parent(&self) -> Size {
1861 if self.window_root {
1862 Size::new(0.0, 0.0)
1863 } else {
1864 self.size
1865 }
1866 }
1867
1868 pub(crate) fn size(&self) -> Size {
1869 self.size
1870 }
1871
1872 fn into_rc(self, spare: Option<Rc<Self>>) -> Rc<Self> {
1875 let Some(mut spare) = spare else {
1876 return Rc::new(self);
1877 };
1878 match Rc::get_mut(&mut spare) {
1879 Some(slot) => *slot = self,
1880 None => return Rc::new(self),
1881 }
1882 spare
1883 }
1884}
1885
1886#[derive(Debug, Clone, Copy)]
1891struct MeasuredChild {
1892 node_id: NodeId,
1893 offset: Point,
1894 for_parent: ForParent,
1895}
1896
1897impl MeasuredChild {
1898 fn new(measured: &MeasuredNode, offset: Point) -> Self {
1899 Self {
1900 node_id: measured.node_id,
1901 offset,
1902 for_parent: ForParent::of(measured),
1903 }
1904 }
1905}
1906
1907#[derive(Debug, Clone, Copy)]
1910struct ForParent {
1911 size: Size,
1912 offset: Point,
1913 alignment_lines: AlignmentLines,
1914}
1915
1916impl ForParent {
1917 fn of(measured: &MeasuredNode) -> Self {
1918 Self {
1919 size: measured.size_for_parent(),
1920 offset: measured.offset,
1921 alignment_lines: measured.alignment_lines_for_parent(),
1922 }
1923 }
1924
1925 fn same_as(&self, other: &Self) -> bool {
1928 self.size == other.size && self.same_but_size(other)
1929 }
1930
1931 fn same_but_size(&self, other: &Self) -> bool {
1933 self.offset == other.offset
1934 && self.alignment_lines.first_baseline() == other.alignment_lines.first_baseline()
1935 && self.alignment_lines.last_baseline() == other.alignment_lines.last_baseline()
1936 }
1937}
1938
1939fn policy_intrinsic(
1941 policy: &dyn MeasurePolicy,
1942 measurables: &[Box<dyn Measurable>],
1943 kind: IntrinsicKind,
1944) -> f32 {
1945 match kind {
1946 IntrinsicKind::MinWidth(height) => policy.min_intrinsic_width(measurables, height),
1947 IntrinsicKind::MaxWidth(height) => policy.max_intrinsic_width(measurables, height),
1948 IntrinsicKind::MinHeight(width) => policy.min_intrinsic_height(measurables, width),
1949 IntrinsicKind::MaxHeight(width) => policy.max_intrinsic_height(measurables, width),
1950 }
1951}
1952
1953pub(crate) struct LayoutRuntimeState {
1954 children: Option<Box<LayoutChildren>>,
1956 coordinator_chain: CoordinatorChain,
1957 measure_policy: Rc<dyn MeasurePolicy>,
1958}
1959
1960impl LayoutRuntimeState {
1961 pub(crate) fn new(measure_policy: Rc<dyn MeasurePolicy>) -> Self {
1962 Self {
1963 children: None,
1964 coordinator_chain: CoordinatorChain::default(),
1965 measure_policy,
1966 }
1967 }
1968
1969 fn bind_node(&mut self, node: &LayoutNode) -> ModifierChainInputs {
1970 if !Rc::ptr_eq(&self.measure_policy, &node.measure_policy) {
1971 self.measure_policy = Rc::clone(&node.measure_policy);
1972 }
1973 self.coordinator_chain.sync(node)
1974 }
1975
1976 fn take_moved_coordinators(&self) -> bool {
1981 self.coordinator_chain.take_moved()
1982 }
1983
1984 pub(crate) fn placed_coordinator_rect(
1985 &self,
1986 ordinal: usize,
1987 node_size: Size,
1988 ) -> Option<GeometryRect> {
1989 self.coordinator_chain.placed_rect(ordinal, node_size)
1990 }
1991
1992 #[cfg(test)]
1993 pub(crate) fn debug_stats(&self) -> LayoutRuntimeDebugStats {
1994 LayoutRuntimeDebugStats {
1995 child_ids: self
1996 .children
1997 .as_ref()
1998 .map(|children| children.ids.clone())
1999 .unwrap_or_default(),
2000 child_state_ptrs: self
2001 .child_states()
2002 .iter()
2003 .map(|state| Rc::as_ptr(state) as *const () as usize)
2004 .collect(),
2005 child_measurable_ptrs: self
2006 .child_measurables()
2007 .iter()
2008 .map(|measurable| {
2009 measurable.as_ref() as *const dyn Measurable as *const () as usize
2010 })
2011 .collect(),
2012 child_measurable_count: self.child_measurables().len(),
2013 coordinator_node_ptrs: self.coordinator_chain.debug_ptrs(),
2014 coordinator_node_count: self.coordinator_chain.debug_ptrs().len(),
2015 }
2016 }
2017}
2018
2019#[cfg(test)]
2020#[derive(Debug, Clone, PartialEq, Eq)]
2021pub(crate) struct LayoutRuntimeDebugStats {
2022 pub(crate) child_ids: Vec<NodeId>,
2023 pub(crate) child_state_ptrs: Vec<usize>,
2024 pub(crate) child_measurable_ptrs: Vec<usize>,
2025 pub(crate) child_measurable_count: usize,
2026 pub(crate) coordinator_node_ptrs: Vec<usize>,
2027 pub(crate) coordinator_node_count: usize,
2028}
2029
2030impl PlaceTarget for LayoutChildMeasureState {
2031 fn place(&self, x: f32, y: f32) {
2032 let internal_offset = read_cell(&self.measured, |measured| {
2033 measured
2034 .as_ref()
2035 .map(|measured| measured.offset)
2036 .unwrap_or_default()
2037 });
2038 self.place_retained(Point {
2039 x: x + internal_offset.x,
2040 y: y + internal_offset.y,
2041 });
2042 }
2043}
2044
2045fn visit_child<R>(
2049 applier: &mut MemoryApplier,
2050 child: &MeasuredChild,
2051 visit: impl FnOnce(&mut MemoryApplier, &MeasuredNode) -> R,
2052) -> R {
2053 let held = applier.get_mut(child.node_id).ok().and_then(|node| {
2054 let any = node.as_any_mut();
2055 if let Some(layout) = any.downcast_ref::<LayoutNode>() {
2056 return layout.cache().held_measurement();
2057 }
2058 any.downcast_ref::<SubcomposeLayoutNode>()
2059 .and_then(|subcompose| subcompose.cache().held_measurement())
2060 });
2061 match held {
2062 Some(held) => visit(applier, &held),
2063 None => visit(
2064 applier,
2065 &MeasuredNode::new(
2066 child.node_id,
2067 Size::default(),
2068 Point::default(),
2069 Point::default(),
2070 Vec::new(),
2071 ),
2072 ),
2073 }
2074}
2075
2076fn clear_semantics_dirty_flags(
2077 applier: &mut MemoryApplier,
2078 node: &MeasuredNode,
2079) -> Result<(), NodeError> {
2080 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
2081 layout.clear_needs_semantics();
2082 }) {
2083 Ok(()) => {}
2084 Err(NodeError::Missing { .. }) => {}
2085 Err(NodeError::TypeMismatch { .. }) => {
2086 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
2087 subcompose.clear_needs_semantics();
2088 }) {
2089 Ok(()) | Err(NodeError::Missing { .. } | NodeError::TypeMismatch { .. }) => {}
2090 Err(err) => return Err(err),
2091 }
2092 }
2093 Err(err) => return Err(err),
2094 }
2095
2096 for child in &node.children {
2097 visit_child(applier, child, clear_semantics_dirty_flags)?;
2098 }
2099
2100 Ok(())
2101}
2102
2103fn build_semantics_tree_from_live_nodes(
2104 applier: &mut MemoryApplier,
2105 node: &MeasuredNode,
2106) -> Result<SemanticsTree, NodeError> {
2107 Ok(SemanticsTree::new(build_semantics_node_from_live_nodes(
2108 applier, node, None,
2109 )?))
2110}
2111
2112fn semantics_node_from_parts(
2113 node_id: NodeId,
2114 node_generation: u32,
2115 mut role: SemanticsRole,
2116 config: Option<SemanticsConfiguration>,
2117 children: Vec<SemanticsNode>,
2118 held_details: Option<Box<SemanticsDetails>>,
2119 bounds: GeometryRect,
2120) -> SemanticsNode {
2121 let focusable = crate::focus_dispatch::has_focus_target(node_id);
2122 let mut node = SemanticsNode {
2123 node_id,
2124 bounds,
2125 node_generation,
2126 children,
2127 focusable,
2128 focused: focusable && crate::focus_dispatch::active_focus_target() == Some(node_id),
2129 details: held_details,
2130 ..SemanticsNode::default()
2131 };
2132 let details = match config {
2133 Some(mut config) => {
2134 if config.role == Some(SemanticsWidgetRole::Button) {
2135 role = SemanticsRole::Button;
2136 }
2137 if config.is_activatable() {
2138 node.actions.push(SemanticsAction::Click {
2139 handler: SemanticsCallback::new(node_id),
2140 });
2141 }
2142 let on_click_label = config.on_click_label.take().or_else(|| {
2143 config
2144 .on_click
2145 .as_ref()
2146 .and_then(|action| (!action.label.is_empty()).then(|| action.label.clone()))
2147 });
2148 node.widget_role = config.role;
2149 node.description = config.content_description.take();
2150 node.on_click = config.on_click.take();
2151 node.selected = config.selected;
2152 node.toggled = config.toggled;
2153 node.enabled = config.enabled;
2154 node.hidden = config.hidden;
2155 node.merge_descendants = config.merge_descendants;
2156 node.traversal_index = config.traversal_index;
2157 node.text = config.text.take();
2158 let is_modal = config.is_modal
2159 && modal_takes_space(Size {
2160 width: bounds.width,
2161 height: bounds.height,
2162 });
2163 SemanticsDetails::from_configuration(config, on_click_label, is_modal)
2164 }
2165 None => SemanticsDetails::NONE,
2166 };
2167 node.set_details(details);
2168 node.role = role;
2169 node
2170}
2171
2172#[inline(never)]
2173fn build_semantics_node_from_live_nodes(
2174 applier: &mut MemoryApplier,
2175 node: &MeasuredNode,
2176 origin: Option<Point>,
2177) -> Result<SemanticsNode, NodeError> {
2178 let (role, config, (bounds, content), placement) =
2179 match applier.with_node::<LayoutNode, _>(node.node_id, |layout| {
2180 let role = role_from_modifier_slices(&layout.modifier_slices_snapshot());
2181 let config = layout.semantics_configuration();
2182 layout.clear_needs_semantics();
2183 let state = layout.layout_state();
2184 (
2185 role,
2186 config,
2187 semantics_placement(&state, origin),
2188 SemanticsPlacement::of(&state),
2189 )
2190 }) {
2191 Ok(data) => data,
2192 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
2193 match applier.with_node::<SubcomposeLayoutNode, _>(node.node_id, |subcompose| {
2194 subcompose.clear_needs_semantics();
2195 let state = subcompose.layout_state();
2196 (
2197 SemanticsRole::Subcompose,
2198 subcompose.semantics_configuration(),
2199 semantics_placement(&state, origin),
2200 SemanticsPlacement::of(&state),
2201 )
2202 }) {
2203 Ok(data) => data,
2204 Err(NodeError::TypeMismatch { .. } | NodeError::Missing { .. }) => {
2205 let top_left = origin.unwrap_or_default();
2206 (
2207 SemanticsRole::Unknown,
2208 None,
2209 (
2210 GeometryRect::from_origin_size(top_left, node.size),
2211 top_left,
2212 ),
2213 SemanticsPlacement::default(),
2214 )
2215 }
2216 Err(err) => return Err(err),
2217 }
2218 }
2219 Err(err) => return Err(err),
2220 };
2221
2222 let mut children = Vec::with_capacity(node.children.len());
2223 for child in &node.children {
2224 children.push(visit_child(applier, child, |applier, child| {
2225 build_semantics_node_from_live_nodes(applier, child, Some(content))
2226 })?);
2227 }
2228
2229 Ok(SemanticsNode {
2230 placement,
2231 ..semantics_node_from_parts(
2232 node.node_id,
2233 applier.node_generation(node.node_id),
2234 role,
2235 config,
2236 children,
2237 None,
2238 bounds,
2239 )
2240 })
2241}
2242
2243fn record_semantics_allocation_stats(node: &SemanticsNode, stats: &mut LayoutAllocationDebugStats) {
2244 stats.semantics_node_count += 1;
2245 stats.semantics_action_count += node.actions.len();
2246 stats.semantics_action_capacity += node.actions.capacity();
2247 stats.semantics_child_count += node.children.len();
2248 stats.semantics_child_capacity += node.children.capacity();
2249 stats.semantics_heap_bytes += node.actions.capacity() * size_of::<SemanticsAction>();
2250 stats.semantics_heap_bytes += node.children.capacity() * size_of::<SemanticsNode>();
2251 if node.details.is_some() {
2252 stats.semantics_heap_bytes += size_of::<SemanticsDetails>();
2253 }
2254
2255 if let Some(description) = &node.description {
2256 stats.semantics_description_count += 1;
2257 stats.semantics_description_bytes += description.capacity();
2258 stats.semantics_heap_bytes += description.capacity();
2259 }
2260
2261 for child in &node.children {
2262 record_semantics_allocation_stats(child, stats);
2263 }
2264}
2265
2266fn record_layout_box_allocation_stats(
2267 layout_box: &LayoutBox,
2268 stats: &mut LayoutAllocationDebugStats,
2269) {
2270 stats.layout_box_count += 1;
2271 stats.layout_box_child_count += layout_box.children.len();
2272 stats.layout_box_child_capacity += layout_box.children.capacity();
2273 stats.layout_box_heap_bytes += layout_box.children.capacity() * size_of::<LayoutBox>();
2274 stats.add_modifier_slice(layout_box.node_data.modifier_slices().debug_stats());
2275
2276 for child in &layout_box.children {
2277 record_layout_box_allocation_stats(child, stats);
2278 }
2279}
2280
2281fn build_layout_tree(
2282 applier: &mut MemoryApplier,
2283 node: &MeasuredNode,
2284) -> Result<LayoutTree, NodeError> {
2285 fn place(
2286 applier: &mut MemoryApplier,
2287 node: &MeasuredNode,
2288 origin: Point,
2289 parent_transform: ProjectiveTransform,
2290 ) -> Result<LayoutBox, NodeError> {
2291 let top_left = Point {
2292 x: origin.x + node.offset.x,
2293 y: origin.y + node.offset.y,
2294 };
2295 let rect = GeometryRect {
2296 x: top_left.x,
2297 y: top_left.y,
2298 width: node.size.width,
2299 height: node.size.height,
2300 };
2301 let (data, window_transform) =
2302 snapshot_node_data(applier, node.node_id, top_left, node.size, parent_transform)?;
2303 let mut children = Vec::with_capacity(node.children.len());
2304 for child in &node.children {
2305 if crate::modifier::is_window_root(applier, child.node_id) {
2306 continue;
2307 }
2308 let child_origin = Point {
2309 x: top_left.x + child.offset.x,
2310 y: top_left.y + child.offset.y,
2311 };
2312 children.push(visit_child(applier, child, |applier, child| {
2313 place(applier, child, child_origin, window_transform)
2314 })?);
2315 }
2316 Ok(LayoutBox {
2317 node_generation: applier.node_generation(node.node_id),
2318 ..LayoutBox::new(node.node_id, rect, node.content_offset, data, children)
2319 })
2320 }
2321
2322 Ok(LayoutTree::new(place(
2323 applier,
2324 node,
2325 Point { x: 0.0, y: 0.0 },
2326 ProjectiveTransform::identity(),
2327 )?))
2328}
2329
2330fn semantics_role_from_layout_box(layout_box: &LayoutBox) -> SemanticsRole {
2331 match &layout_box.node_data.kind {
2332 LayoutNodeKind::Subcompose => SemanticsRole::Subcompose,
2333 LayoutNodeKind::Spacer => SemanticsRole::Spacer,
2334 LayoutNodeKind::Unknown => SemanticsRole::Unknown,
2335 LayoutNodeKind::Button { .. } => SemanticsRole::Button,
2336 LayoutNodeKind::Layout => role_from_modifier_slices(layout_box.node_data.modifier_slices()),
2337 }
2338}
2339
2340#[inline(never)]
2341fn build_semantics_node_from_layout_box(layout_box: &LayoutBox, origin: Point) -> SemanticsNode {
2342 let rect = layout_box.rect;
2343 let content = Point {
2344 x: rect.x + layout_box.content_offset.x,
2345 y: rect.y + layout_box.content_offset.y,
2346 };
2347 let children = layout_box
2348 .children
2349 .iter()
2350 .map(|child| build_semantics_node_from_layout_box(child, content))
2351 .collect();
2352
2353 SemanticsNode {
2354 placement: SemanticsPlacement {
2355 position: Point {
2356 x: rect.x - origin.x,
2357 y: rect.y - origin.y,
2358 },
2359 content_offset: layout_box.content_offset,
2360 },
2361 ..semantics_node_from_parts(
2362 layout_box.node_id,
2363 layout_box.node_generation,
2364 semantics_role_from_layout_box(layout_box),
2365 layout_box.node_data.semantics().cloned(),
2366 children,
2367 None,
2368 rect,
2369 )
2370 }
2371}
2372
2373fn subtract_padding(constraints: Constraints, padding: EdgeInsets) -> Constraints {
2374 let horizontal = padding.horizontal_sum();
2375 let vertical = padding.vertical_sum();
2376 let min_width = (constraints.min_width - horizontal).max(0.0);
2377 let mut max_width = constraints.max_width;
2378 if max_width.is_finite() {
2379 max_width = (max_width - horizontal).max(0.0);
2380 }
2381 let min_height = (constraints.min_height - vertical).max(0.0);
2382 let mut max_height = constraints.max_height;
2383 if max_height.is_finite() {
2384 max_height = (max_height - vertical).max(0.0);
2385 }
2386 normalize_constraints(Constraints {
2387 min_width,
2388 max_width,
2389 min_height,
2390 max_height,
2391 })
2392}
2393
2394fn resolve_dimension(
2395 base: f32,
2396 explicit: DimensionConstraint,
2397 min_override: Option<f32>,
2398 max_override: Option<f32>,
2399 min_limit: f32,
2400 max_limit: f32,
2401) -> f32 {
2402 let mut min_bound = min_limit;
2403 if let Some(min_value) = min_override {
2404 min_bound = min_bound.max(min_value);
2405 }
2406
2407 let mut max_bound = if max_limit.is_finite() {
2408 max_limit
2409 } else {
2410 max_override.unwrap_or(max_limit)
2411 };
2412 if let Some(max_value) = max_override {
2413 if max_bound.is_finite() {
2414 max_bound = max_bound.min(max_value);
2415 } else {
2416 max_bound = max_value;
2417 }
2418 }
2419 if max_bound < min_bound {
2420 max_bound = min_bound;
2421 }
2422
2423 let mut size = match explicit {
2424 DimensionConstraint::Points(points) => points,
2425 DimensionConstraint::Fraction(fraction) => {
2426 if max_limit.is_finite() {
2427 max_limit * fraction.clamp(0.0, 1.0)
2428 } else {
2429 base
2430 }
2431 }
2432 DimensionConstraint::Unspecified => base,
2433 DimensionConstraint::Intrinsic(_) => base,
2434 };
2435
2436 size = clamp_dimension(size, min_bound, max_bound);
2437 size = clamp_dimension(size, min_limit, max_limit);
2438 size.max(0.0)
2439}
2440
2441fn clamp_dimension(value: f32, min: f32, max: f32) -> f32 {
2442 let mut result = value.max(min);
2443 if max.is_finite() {
2444 result = result.min(max);
2445 }
2446 result
2447}
2448
2449fn normalize_constraints(mut constraints: Constraints) -> Constraints {
2450 if constraints.max_width < constraints.min_width {
2451 constraints.max_width = constraints.min_width;
2452 }
2453 if constraints.max_height < constraints.min_height {
2454 constraints.max_height = constraints.min_height;
2455 }
2456 constraints
2457}
2458
2459#[cfg(test)]
2460#[path = "tests/layout_tests.rs"]
2461mod tests;
2462
2463#[cfg(test)]
2464#[path = "tests/semantics_update_tests.rs"]
2465mod semantics_update_tests;
2466
2467#[cfg(test)]
2468#[path = "tests/coordinator_geometry_tests.rs"]
2469mod coordinator_geometry_tests;