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