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