1use std::{
2 any::TypeId,
3 cell::{Cell, RefCell},
4 hash::{Hash, Hasher},
5 rc::Rc,
6};
7
8use cranpose_core::{Node, NodeId, collections::map::HashMap};
9use cranpose_foundation::{
10 InvalidationKind, ModifierInvalidation, ModifierInvalidations, NodeCapabilities,
11 SemanticsConfiguration,
12};
13use cranpose_ui_layout::{Constraints, MeasurePolicy};
14
15#[cfg(test)]
16use crate::layout::LayoutRuntimeDebugStats;
17use crate::{
18 layout::{LayoutRuntimeState, MeasuredNode},
19 modifier::{
20 Modifier, ModifierChainHandle, ModifierLocalSource, ModifierLocalToken,
21 ModifierLocalsHandle, ModifierNodeSlices, Point, ResolvedModifierLocal, ResolvedModifiers,
22 Size,
23 },
24};
25
26#[derive(Clone, Copy)]
27enum LayoutInvalidationDispatchDiag {
28 Disabled,
29 All,
30 Node(NodeId),
31}
32
33fn layout_invalidation_dispatch_diag() -> LayoutInvalidationDispatchDiag {
34 static MODE: std::sync::OnceLock<LayoutInvalidationDispatchDiag> = std::sync::OnceLock::new();
35 *MODE.get_or_init(|| {
36 let Some(value) = std::env::var_os("CRANPOSE_LAYOUT_INVALIDATION_DISPATCH_DIAG") else {
37 return LayoutInvalidationDispatchDiag::Disabled;
38 };
39 if value == "all" {
40 return LayoutInvalidationDispatchDiag::All;
41 }
42 value.to_string_lossy().parse::<NodeId>().map_or(
43 LayoutInvalidationDispatchDiag::Disabled,
44 LayoutInvalidationDispatchDiag::Node,
45 )
46 })
47}
48
49fn log_layout_invalidation_dispatch(
50 id: NodeId,
51 invalidation: &ModifierInvalidation,
52 curr_caps: NodeCapabilities,
53 prev_caps: NodeCapabilities,
54 modifier: &Modifier,
55) {
56 let enabled = match layout_invalidation_dispatch_diag() {
57 LayoutInvalidationDispatchDiag::Disabled => false,
58 LayoutInvalidationDispatchDiag::All => true,
59 LayoutInvalidationDispatchDiag::Node(target) => target == id,
60 };
61 if enabled {
62 log::warn!(
63 "[layout-invalidation-dispatch] node={id} invalidation={invalidation:?} curr_caps={curr_caps:?} prev_caps={prev_caps:?} modifier={modifier}"
64 );
65 }
66}
67
68pub(crate) struct PlacementPass {
72 id: Cell<u64>,
73 unplaced: Cell<usize>,
74}
75
76impl PlacementPass {
77 pub(crate) const fn new() -> Self {
78 Self {
79 id: Cell::new(1),
80 unplaced: Cell::new(0),
81 }
82 }
83}
84
85pub(crate) fn begin_placement_pass() {
87 crate::thread_state::cells(|cells| {
88 let pass = &cells.placement_pass;
89 pass.id.set(pass.id.get() + 1);
90 pass.unplaced.set(0);
91 });
92}
93
94pub(crate) fn placement_pass_with_unplaced_nodes() -> Option<u64> {
96 crate::thread_state::cells(|cells| {
97 let pass = &cells.placement_pass;
98 (pass.unplaced.get() > 0).then(|| pass.id.get())
99 })
100}
101
102#[derive(Clone, Copy, Default)]
104pub(crate) struct DescendantDirt {
105 pub(crate) layout: bool,
106 pub(crate) measure: bool,
107}
108
109impl DescendantDirt {
110 pub(crate) fn marked(self, measure: bool) -> Self {
111 Self {
112 layout: true,
113 measure: self.measure || measure,
114 }
115 }
116}
117
118#[derive(Clone, Debug, Default)]
120pub struct LayoutState {
121 size: Size,
122 position: Point,
123 is_placed: bool,
124 node_id: Option<NodeId>,
125 cleared_in_pass: u64,
127 content_offset: Point,
128 semantics_stamp: u64,
129}
130
131impl LayoutState {
132 pub fn size(&self) -> Size {
134 self.size
135 }
136
137 pub fn position(&self) -> Point {
139 self.position
140 }
141
142 pub fn content_offset(&self) -> Point {
145 self.content_offset
146 }
147
148 pub fn set_content_offset(&mut self, offset: Point) {
151 if self.content_offset != offset {
152 self.content_offset = offset;
153 self.note_semantics_change();
154 }
155 }
156
157 pub fn at_origin(mut self) -> Self {
161 self.position = Point::default();
162 self
163 }
164
165 pub fn is_placed(&self) -> bool {
167 self.is_placed
168 }
169
170 pub(crate) fn set_node_id(&mut self, node_id: NodeId) {
171 self.node_id = Some(node_id);
172 }
173
174 pub fn set_size(&mut self, size: Size) {
177 if self.size != size {
178 if let Some(id) = self.node_id {
179 crate::render_state::record_geometry_scene_node(id);
180 }
181 self.size = size;
182 self.note_semantics_change();
183 }
184 }
185
186 pub fn place(&mut self, position: Point) {
191 let newly_placed = !self.is_placed && !self.restore_placement();
192 let moved = self.position != position;
193 if let Some(id) = self.node_id {
194 if newly_placed {
195 crate::render_state::record_geometry_scene_node(id);
196 } else if moved {
197 crate::render_state::record_moved_scene_node(id);
198 }
199 }
200 self.position = position;
201 if moved || !self.is_placed {
202 self.is_placed = true;
203 self.note_semantics_change();
204 }
205 }
206
207 fn restore_placement(&self) -> bool {
210 crate::thread_state::cells(|cells| {
211 let pass = &cells.placement_pass;
212 let restored = self.cleared_in_pass == pass.id.get();
213 if restored {
214 pass.unplaced.set(pass.unplaced.get().saturating_sub(1));
215 }
216 restored
217 })
218 }
219
220 pub fn clear_placed(&mut self) {
224 if !self.is_placed {
225 return;
226 }
227 self.is_placed = false;
228 self.cleared_in_pass = crate::thread_state::cells(|cells| {
229 let pass = &cells.placement_pass;
230 pass.unplaced.set(pass.unplaced.get() + 1);
231 pass.id.get()
232 });
233 self.note_semantics_change();
234 }
235
236 pub(crate) fn unplaced_in(&self, pass: u64) -> bool {
238 !self.is_placed && self.cleared_in_pass == pass
239 }
240
241 pub(crate) fn note_semantics_change(&mut self) {
242 if let Some(id) = self.node_id {
243 crate::semantics_layout_log::record_layout_change(id, &mut self.semantics_stamp);
244 }
245 }
246}
247
248#[derive(Clone)]
249struct MeasurementCacheEntry {
250 constraints: Constraints,
251 measured: Rc<MeasuredNode>,
252 hold: Option<cranpose_ui_layout::ConstraintsHold>,
254}
255
256#[derive(Clone, Copy, Debug)]
258pub enum IntrinsicKind {
259 MinWidth(f32),
261 MaxWidth(f32),
263 MinHeight(f32),
265 MaxHeight(f32),
267}
268
269impl IntrinsicKind {
270 fn discriminant(&self) -> u8 {
271 match self {
272 IntrinsicKind::MinWidth(_) => 0,
273 IntrinsicKind::MaxWidth(_) => 1,
274 IntrinsicKind::MinHeight(_) => 2,
275 IntrinsicKind::MaxHeight(_) => 3,
276 }
277 }
278
279 fn value_bits(&self) -> u32 {
280 match self {
281 IntrinsicKind::MinWidth(value)
282 | IntrinsicKind::MaxWidth(value)
283 | IntrinsicKind::MinHeight(value)
284 | IntrinsicKind::MaxHeight(value) => value.to_bits(),
285 }
286 }
287}
288
289impl PartialEq for IntrinsicKind {
290 fn eq(&self, other: &Self) -> bool {
291 self.discriminant() == other.discriminant() && self.value_bits() == other.value_bits()
292 }
293}
294
295impl Eq for IntrinsicKind {}
296
297impl Hash for IntrinsicKind {
298 fn hash<H: Hasher>(&self, state: &mut H) {
299 self.discriminant().hash(state);
300 self.value_bits().hash(state);
301 }
302}
303
304#[derive(Default)]
305struct NodeCacheState {
306 epoch: u64,
307 measurement: Option<MeasurementCacheEntry>,
308 intrinsics: Vec<(IntrinsicKind, f32)>,
309 intrinsics_read: bool,
311}
312
313#[derive(Default)]
314pub(crate) struct LayoutNodeCacheHandles {
315 state: Rc<RefCell<NodeCacheState>>,
316}
317
318impl Clone for LayoutNodeCacheHandles {
319 fn clone(&self) -> Self {
320 Self {
321 state: Rc::clone(&self.state),
322 }
323 }
324
325 fn clone_from(&mut self, source: &Self) {
326 if !Rc::ptr_eq(&self.state, &source.state) {
327 self.state = Rc::clone(&source.state);
328 }
329 }
330}
331
332impl LayoutNodeCacheHandles {
333 pub(crate) fn clear(&self) {
334 let mut state = self.state.borrow_mut();
335 state.measurement = None;
336 state.intrinsics.clear();
337 state.intrinsics_read = false;
338 state.epoch = 0;
339 }
340
341 pub(crate) fn activate(&self, epoch: u64) {
342 let mut state = self.state.borrow_mut();
343 if state.epoch != epoch {
344 state.measurement = None;
345 state.intrinsics.clear();
346 state.intrinsics_read = false;
347 state.epoch = epoch;
348 }
349 }
350
351 pub(crate) fn forget_intrinsics(&self) {
355 self.state.borrow_mut().intrinsics.clear();
356 }
357
358 pub(crate) fn epoch(&self) -> u64 {
359 self.state.borrow().epoch
360 }
361
362 pub(crate) fn get_measurement(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
366 let state = self.state.borrow();
367 state
368 .measurement
369 .as_ref()
370 .filter(|entry| {
371 entry.constraints == constraints
372 || entry.hold.is_some_and(|hold| hold.contains(constraints))
373 })
374 .map(|entry| Rc::clone(&entry.measured))
375 }
376
377 pub(crate) fn measured_constraints(&self) -> Option<Constraints> {
379 self.state
380 .borrow()
381 .measurement
382 .as_ref()
383 .map(|entry| entry.constraints)
384 }
385
386 pub(crate) fn has_intrinsics(&self) -> bool {
388 self.state.borrow().intrinsics_read
389 }
390
391 pub(crate) fn hold_of(
393 &self,
394 measured: &Rc<MeasuredNode>,
395 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
396 self.state
397 .borrow()
398 .measurement
399 .as_ref()
400 .filter(|entry| Rc::ptr_eq(&entry.measured, measured))
401 .and_then(|entry| entry.hold)
402 }
403
404 pub(crate) fn store_measurement(&self, constraints: Constraints, measured: Rc<MeasuredNode>) {
407 let mut state = self.state.borrow_mut();
408 let hold = state
409 .measurement
410 .as_ref()
411 .filter(|entry| Rc::ptr_eq(&entry.measured, &measured))
412 .and_then(|entry| entry.hold);
413 state.measurement = Some(MeasurementCacheEntry {
414 constraints,
415 measured,
416 hold,
417 });
418 }
419
420 pub(crate) fn store_held_measurement(
423 &self,
424 constraints: Constraints,
425 measured: Rc<MeasuredNode>,
426 hold: Option<cranpose_ui_layout::ConstraintsHold>,
427 ) {
428 self.state.borrow_mut().measurement = Some(MeasurementCacheEntry {
429 constraints,
430 measured,
431 hold,
432 });
433 }
434
435 pub(crate) fn get_intrinsic(&self, kind: &IntrinsicKind) -> Option<f32> {
436 let state = self.state.borrow();
437 state
438 .intrinsics
439 .iter()
440 .find(|(stored_kind, _)| stored_kind == kind)
441 .map(|(_, value)| *value)
442 }
443
444 pub(crate) fn store_intrinsic(&self, kind: IntrinsicKind, value: f32) {
445 let mut state = self.state.borrow_mut();
446 state.intrinsics_read = true;
447 if let Some((_, existing)) = state
448 .intrinsics
449 .iter_mut()
450 .find(|(stored_kind, _)| stored_kind == &kind)
451 {
452 *existing = value;
453 } else {
454 state.intrinsics.push((kind, value));
455 }
456 }
457}
458
459struct NodeIdCell(Cell<NodeId>);
463
464const NO_NODE_ID: NodeId = NodeId::MAX;
467
468impl NodeIdCell {
469 fn new(id: Option<NodeId>) -> Self {
470 Self(Cell::new(id.unwrap_or(NO_NODE_ID)))
471 }
472
473 fn get(&self) -> Option<NodeId> {
474 let id = self.0.get();
475 (id != NO_NODE_ID).then_some(id)
476 }
477
478 fn set(&self, id: Option<NodeId>) {
479 debug_assert_ne!(id, Some(NO_NODE_ID), "no node takes the none id");
480 self.0.set(id.unwrap_or(NO_NODE_ID));
481 }
482
483 fn replace(&self, id: Option<NodeId>) -> Option<NodeId> {
484 let previous = self.get();
485 self.set(id);
486 previous
487 }
488}
489
490#[repr(C)]
497pub struct LayoutNode {
498 cache: LayoutNodeCacheHandles,
499 layout_state: Rc<RefCell<LayoutState>>,
500 layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
501 pub children: Vec<NodeId>,
503 parent_data: Cell<cranpose_ui_layout::ParentData>,
506 needs_measure: Cell<bool>,
507 needs_layout: Cell<bool>,
508 descendant_dirt: Cell<DescendantDirt>,
510
511 pub measure_policy: Rc<dyn MeasurePolicy>,
514 coordinator_geometry: Rc<crate::modifier::CoordinatorGeometry>,
517 density: crate::density::Density,
518 is_virtual: bool,
519 needs_semantics: Cell<bool>,
520 descendant_needs_semantics: Cell<bool>,
524 needs_redraw: Cell<bool>,
525 needs_pointer_pass: Cell<bool>,
526 needs_focus_sync: Cell<bool>,
527 modifier_slices_dirty: Cell<bool>,
528 debug_modifiers: Cell<bool>,
529
530 id: NodeIdCell,
531 parent: NodeIdCell,
532 folded_parent: NodeIdCell,
533 modifier_slices_snapshot: crate::modifier::SlicesSnapshot,
534
535 semantics_reach: Cell<Option<cranpose_foundation::SemanticsReach>>,
539 owner_context_id: Cell<Option<crate::render_state::AppContextId>>,
540 modifier_chain: ModifierChainHandle,
541 pub modifier: Modifier,
544 #[cfg(feature = "inspection")]
546 pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
547}
548
549pub(crate) const RECYCLED_LAYOUT_NODE_POOL_LIMIT: usize = 128;
550
551pub(crate) fn empty_measure_policy() -> Rc<dyn MeasurePolicy> {
552 crate::thread_state::with(|state| {
553 Rc::clone(
554 state
555 .empty_measure_policy
556 .get_or_init(|| Rc::new(crate::layout::policies::EmptyMeasurePolicy)),
557 )
558 })
559}
560
561impl LayoutNode {
562 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
564 Self::new_with_virtual(modifier, measure_policy, false)
565 }
566
567 pub fn new_virtual() -> Self {
570 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), true)
571 }
572
573 fn new_recycled_shell(is_virtual: bool) -> Self {
574 let mut shell =
575 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), is_virtual);
576 shell.needs_measure.set(false);
577 shell.needs_layout.set(false);
578 shell.needs_semantics.set(false);
579 shell.descendant_needs_semantics.set(false);
580 shell.needs_redraw.set(false);
581 shell.needs_pointer_pass.set(false);
582 shell.needs_focus_sync.set(false);
583 shell.parent.set(None);
584 shell.folded_parent.set(None);
585 shell.id.set(None);
586 shell.owner_context_id.set(None);
587 shell.debug_modifiers.set(false);
588 shell.modifier_slices_dirty = Cell::new(true);
589 shell
590 }
591
592 fn new_with_virtual(
593 modifier: Modifier,
594 measure_policy: Rc<dyn MeasurePolicy>,
595 is_virtual: bool,
596 ) -> Self {
597 let layout_runtime_state = Rc::new(RefCell::new(LayoutRuntimeState::new(Rc::clone(
598 &measure_policy,
599 ))));
600 let mut node = Self {
601 #[cfg(feature = "inspection")]
602 source_trace: cranpose_core::source_trace::current_source_trace(),
603 modifier,
604 modifier_chain: ModifierChainHandle::detached(),
605 measure_policy,
606 density: crate::density::Density::default(),
607 children: Vec::new(),
608 cache: LayoutNodeCacheHandles::default(),
609 needs_measure: Cell::new(true),
610 needs_layout: Cell::new(true),
611 descendant_dirt: Cell::default(),
612 parent_data: Cell::default(),
613 needs_semantics: Cell::new(true),
614 descendant_needs_semantics: Cell::new(false),
615 semantics_reach: Cell::new(None),
616 needs_redraw: Cell::new(true),
617 needs_pointer_pass: Cell::new(false),
618 needs_focus_sync: Cell::new(false),
619 parent: NodeIdCell::new(None),
620 folded_parent: NodeIdCell::new(None),
621 id: NodeIdCell::new(None),
622 owner_context_id: Cell::new(None),
623 debug_modifiers: Cell::new(false),
624 is_virtual,
625 modifier_slices_snapshot: crate::modifier::SlicesSnapshot::default(),
626 modifier_slices_dirty: Cell::new(true),
627 layout_state: Rc::new(RefCell::new(LayoutState::default())),
628 layout_runtime_state,
629 coordinator_geometry: Rc::default(),
630 };
631 node.sync_modifier_chain(false);
632 node
633 }
634
635 pub fn set_modifier(&mut self, modifier: Modifier) {
638 #[cfg(feature = "inspection")]
639 {
640 let trace = cranpose_core::source_trace::current_source_trace();
641 if !trace.is_empty() {
642 self.source_trace = trace;
643 }
644 }
645 if self.modifier == modifier
649 && !self
650 .modifier_capabilities()
651 .contains(NodeCapabilities::MODIFIER_LOCALS)
652 {
653 return;
654 }
655 let text_only = self
659 .modifier
660 .differs_only_in::<crate::text_modifier_node::TextModifierElement>(&modifier);
661 let modifier_changed = text_only || !self.modifier.structural_eq(&modifier);
662 self.modifier = modifier;
663 self.sync_modifier_chain(text_only);
664 if modifier_changed {
665 self.cache.clear();
666 self.request_semantics_update();
667 }
668 }
669
670 fn sync_modifier_chain(&mut self, text_only: bool) {
674 let prev_caps = self.modifier_capabilities();
675 let start_parent = self.parent();
676 let mut resolver = move |token: &ModifierLocalToken| {
677 resolve_modifier_local_from_parent_chain(start_parent, token)
678 };
679 self.modifier_chain
680 .set_debug_logging(self.debug_modifiers.get());
681 let mut context = self.modifier_chain.context(self.id.get());
682 let in_place = text_only
683 && self
684 .modifier_chain
685 .update_elements_in_place::<crate::text_modifier_node::TextModifierElement>(
686 &self.modifier,
687 &mut context,
688 );
689 let modifier_local_invalidations = if in_place {
690 ModifierInvalidations::new()
691 } else {
692 let invalidations = self.modifier_chain.update_with_resolver(
693 &self.modifier,
694 &mut resolver,
695 &mut context,
696 );
697 self.refresh_parent_data();
698 invalidations
699 };
700 if prev_caps.contains(NodeCapabilities::WINDOW_ROOT)
701 != self
702 .modifier_capabilities()
703 .contains(NodeCapabilities::WINDOW_ROOT)
704 {
705 self.note_semantics_layout_change();
706 }
707 if !in_place {
710 self.forget_semantics_reach();
711 }
712 let keep_slices = in_place && !self.modifier_slices_dirty.get();
713 if !keep_slices {
714 self.modifier_slices_dirty.set(true);
715 }
716
717 let mut invalidations = context.take_invalidations();
718 invalidations.extend(modifier_local_invalidations);
719 self.dispatch_modifier_invalidations_with_prev(&invalidations, prev_caps, keep_slices);
720 if !in_place {
723 self.refresh_registry_state();
724 }
725 }
726
727 fn update_modifier_slices_cache(&self) {
728 self.modifier_slices_snapshot.collect(
729 self.modifier_chain.chain(),
730 &self.coordinator_geometry,
731 self.density.density(),
732 );
733 self.modifier_slices_dirty.set(false);
734 }
735
736 #[cfg(test)]
738 pub(crate) fn modifier_slices_ready(&self) -> bool {
739 !self.modifier_slices_dirty.get()
740 }
741
742 pub(crate) fn mark_modifier_slices_dirty(&self) {
743 self.modifier_slices_dirty.set(true);
744 }
745
746 #[cfg(test)]
747 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
748 self.dispatch_modifier_invalidations_with_prev(
749 invalidations,
750 NodeCapabilities::empty(),
751 false,
752 );
753 }
754
755 fn dispatch_modifier_invalidations_with_prev(
758 &self,
759 invalidations: &[ModifierInvalidation],
760 prev_caps: NodeCapabilities,
761 keep_slices: bool,
762 ) {
763 let curr_caps = self.modifier_capabilities();
764 for invalidation in invalidations {
765 if !keep_slices {
766 self.modifier_slices_dirty.set(true);
767 }
768 let has_capability =
769 |capability| curr_caps.contains(capability) || prev_caps.contains(capability);
770 match invalidation.kind() {
771 InvalidationKind::Layout => {
772 if has_capability(NodeCapabilities::LAYOUT) {
773 self.mark_needs_measure();
774 if let Some(id) = self.id.get() {
775 log_layout_invalidation_dispatch(
776 id,
777 invalidation,
778 curr_caps,
779 prev_caps,
780 &self.modifier,
781 );
782 let inside_composition =
783 cranpose_core::composer_context::try_with_composer(|_| ())
784 .is_some();
785 if inside_composition {
786 cranpose_core::bubble_measure_dirty_in_composer(id);
787 } else {
788 crate::schedule_layout_repass(id);
789 }
790 }
791 }
792 }
793 InvalidationKind::Draw => {
794 if has_capability(NodeCapabilities::DRAW)
795 || invalidation.capabilities().contains(NodeCapabilities::DRAW)
796 {
797 self.mark_needs_redraw();
798 }
799 }
800 InvalidationKind::PointerInput => {
801 if has_capability(NodeCapabilities::POINTER_INPUT) {
802 self.mark_needs_pointer_pass();
803 crate::request_pointer_invalidation();
804 if let Some(id) = self.id.get() {
805 crate::schedule_pointer_repass(id);
806 }
807 }
808 }
809 InvalidationKind::Semantics => {
810 self.request_semantics_update();
811 }
812 InvalidationKind::Focus => {
813 if has_capability(NodeCapabilities::FOCUS) {
814 self.mark_needs_focus_sync();
815 crate::request_focus_invalidation();
816 if let Some(id) = self.id.get() {
817 crate::schedule_focus_invalidation(id);
818 }
819 }
820 }
821 }
822 }
823 }
824
825 pub fn density(&self) -> crate::density::Density {
827 self.density
828 }
829
830 pub fn set_density(&mut self, density: crate::density::Density) {
832 if self.density != density {
833 self.density = density;
834 self.cache.clear();
835 self.modifier_slices_dirty.set(true);
836 self.mark_needs_measure();
837 }
838 }
839
840 pub fn set_layout_direction(&mut self, direction: crate::LayoutDirection) {
842 if self.modifier_chain.set_layout_direction(direction) {
843 self.refresh_parent_data();
844 self.cache.clear();
845 self.modifier_slices_dirty.set(true);
846 self.mark_needs_measure();
847 }
848 }
849
850 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
853 if !Rc::ptr_eq(&self.measure_policy, &policy) {
854 self.measure_policy = policy;
855 self.cache.clear();
856 self.mark_needs_measure();
857 if let Some(id) = self.id.get() {
858 cranpose_core::bubble_measure_dirty_in_composer(id);
859 }
860 }
861 }
862
863 pub fn mark_needs_measure(&self) {
865 self.needs_measure.set(true);
866 self.needs_layout.set(true);
867 }
868
869 pub fn mark_needs_layout(&self) {
871 self.needs_layout.set(true);
872 }
873
874 pub fn mark_needs_redraw(&self) {
876 self.needs_redraw.set(true);
877 if let Some(id) = self.id.get() {
878 crate::schedule_draw_repass(id);
879 }
880 crate::request_render_invalidation();
881 }
882
883 pub fn needs_measure(&self) -> bool {
885 self.needs_measure.get()
886 }
887
888 pub fn needs_layout(&self) -> bool {
890 self.needs_layout.get()
891 }
892
893 pub fn mark_needs_semantics(&self) {
895 self.needs_semantics.set(true);
896 self.forget_semantics_reach();
897 }
898
899 pub(crate) fn clear_needs_semantics(&self) {
900 self.needs_semantics.set(false);
901 self.descendant_needs_semantics.set(false);
902 }
903
904 pub fn needs_semantics(&self) -> bool {
907 self.needs_semantics.get() || self.descendant_needs_semantics.get()
908 }
909
910 pub(crate) fn semantics_changed(&self) -> bool {
913 self.needs_semantics.get()
914 }
915
916 pub fn needs_redraw(&self) -> bool {
918 self.needs_redraw.get()
919 }
920
921 pub fn clear_needs_redraw(&self) {
923 self.needs_redraw.set(false);
924 }
925
926 fn request_semantics_update(&self) {
927 let already_dirty = self.needs_semantics.replace(true);
928 if already_dirty {
929 return;
930 }
931
932 if let Some(id) = self.id.get() {
933 cranpose_core::queue_semantics_invalidation(id);
934 }
935 }
936
937 pub(crate) fn clear_needs_measure(&self) {
938 self.needs_measure.set(false);
939 }
940
941 pub(crate) fn clear_needs_layout(&self) {
944 self.needs_layout.set(false);
945 self.descendant_dirt.set(DescendantDirt::default());
946 }
947
948 pub fn mark_needs_pointer_pass(&self) {
950 self.needs_pointer_pass.set(true);
951 }
952
953 pub fn needs_pointer_pass(&self) -> bool {
955 self.needs_pointer_pass.get()
956 }
957
958 pub fn clear_needs_pointer_pass(&self) {
960 self.needs_pointer_pass.set(false);
961 }
962
963 pub fn mark_needs_focus_sync(&self) {
965 self.needs_focus_sync.set(true);
966 }
967
968 pub fn needs_focus_sync(&self) -> bool {
970 self.needs_focus_sync.get()
971 }
972
973 pub fn clear_needs_focus_sync(&self) {
975 self.needs_focus_sync.set(false);
976 }
977
978 pub fn set_node_id(&mut self, id: NodeId) {
980 let previous = self.id.replace(Some(id));
981 if let Some(existing) = previous
982 && let Some(owner_context_id) = self.owner_context_id.take()
983 {
984 unregister_layout_node(owner_context_id, existing);
985 }
986 self.layout_state.borrow_mut().set_node_id(id);
987 let owner_context_id = register_layout_node(id, self);
990 self.owner_context_id.set(Some(owner_context_id));
991
992 let mut context = self.modifier_chain.context(Some(id));
993 self.modifier_chain.move_to_node(previous, &mut context);
994 let invalidations = context.take_invalidations();
995 self.dispatch_modifier_invalidations_with_prev(
996 &invalidations,
997 NodeCapabilities::empty(),
998 false,
999 );
1000 self.modifier_slices_dirty.set(true);
1002 }
1003
1004 pub fn node_id(&self) -> Option<NodeId> {
1006 self.id.get()
1007 }
1008
1009 pub fn set_parent(&self, parent: NodeId) {
1012 self.folded_parent.set(Some(parent));
1013 self.parent.set(Some(parent));
1014 self.refresh_registry_state();
1015 }
1016
1017 pub fn clear_parent(&self) {
1019 self.folded_parent.set(None);
1020 self.parent.set(None);
1021 self.refresh_registry_state();
1022 }
1023
1024 pub fn parent(&self) -> Option<NodeId> {
1026 self.parent.get()
1027 }
1028
1029 pub fn folded_parent(&self) -> Option<NodeId> {
1031 self.folded_parent.get()
1032 }
1033
1034 pub(crate) fn cache_handles(&self) -> &LayoutNodeCacheHandles {
1035 &self.cache
1036 }
1037
1038 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1040 self.modifier_chain.resolved_modifiers()
1041 }
1042
1043 pub(crate) fn parent_data(&self) -> cranpose_ui_layout::ParentData {
1045 self.parent_data.get()
1046 }
1047
1048 fn refresh_parent_data(&self) {
1051 self.parent_data.set(crate::layout::parent_data_of(
1052 self.modifier_chain.resolved_modifiers().layout_properties(),
1053 ));
1054 }
1055
1056 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1058 self.modifier_chain.capabilities()
1059 }
1060
1061 pub fn is_window_root(&self) -> bool {
1065 self.modifier_capabilities()
1066 .contains(NodeCapabilities::WINDOW_ROOT)
1067 }
1068
1069 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
1072 self.modifier_chain.aggregate_child_capabilities()
1073 }
1074
1075 pub fn set_debug_modifiers(&mut self, enabled: bool) {
1077 self.debug_modifiers.set(enabled);
1078 self.modifier_chain.set_debug_logging(enabled);
1079 }
1080
1081 pub fn debug_modifiers_enabled(&self) -> bool {
1083 self.debug_modifiers.get()
1084 }
1085
1086 pub fn modifier_locals_handle(&self) -> Option<ModifierLocalsHandle> {
1089 self.modifier_chain.modifier_locals_handle()
1090 }
1091
1092 pub fn has_layout_modifier_nodes(&self) -> bool {
1094 self.modifier_capabilities()
1095 .contains(NodeCapabilities::LAYOUT)
1096 }
1097
1098 pub fn has_draw_modifier_nodes(&self) -> bool {
1100 self.modifier_capabilities()
1101 .contains(NodeCapabilities::DRAW)
1102 }
1103
1104 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1106 self.modifier_capabilities()
1107 .contains(NodeCapabilities::POINTER_INPUT)
1108 }
1109
1110 pub fn has_semantics_modifier_nodes(&self) -> bool {
1112 self.modifier_capabilities()
1113 .contains(NodeCapabilities::SEMANTICS)
1114 }
1115
1116 pub fn has_focus_modifier_nodes(&self) -> bool {
1118 self.modifier_capabilities()
1119 .contains(NodeCapabilities::FOCUS)
1120 }
1121
1122 fn refresh_registry_state(&self) {
1123 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
1124 let parent = self.parent();
1125 let capabilities = self.modifier_child_capabilities();
1126 let modifier_locals = self.modifier_locals_handle();
1127 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1128 owner_context_id,
1129 |registry| {
1130 registry.update_entry(id, parent, capabilities, modifier_locals);
1131 },
1132 );
1133 }
1134 }
1135
1136 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
1139 if self.modifier_slices_dirty.get() {
1140 self.update_modifier_slices_cache();
1141 }
1142 self.modifier_slices_snapshot.get()
1143 }
1144
1145 pub fn layout_state(&self) -> LayoutState {
1147 self.layout_state.borrow().clone()
1148 }
1149
1150 pub fn measured_size(&self) -> Size {
1152 self.layout_state.borrow().size
1153 }
1154
1155 pub fn position(&self) -> Point {
1157 self.layout_state.borrow().position
1158 }
1159
1160 pub fn is_placed(&self) -> bool {
1162 self.layout_state.borrow().is_placed
1163 }
1164
1165 pub fn set_measured_size(&self, size: Size) {
1169 self.layout_state.borrow_mut().set_size(size);
1170 }
1171
1172 pub fn set_position(&self, position: Point) {
1175 self.layout_state.borrow_mut().place(position);
1176 }
1177
1178 pub fn set_content_offset(&self, offset: Point) {
1180 self.layout_state.borrow_mut().set_content_offset(offset);
1181 }
1182
1183 pub fn clear_placed(&self) {
1185 self.layout_state.borrow_mut().clear_placed();
1186 }
1187
1188 fn note_semantics_layout_change(&self) {
1189 if let Ok(mut state) = self.layout_state.try_borrow_mut() {
1190 state.note_semantics_change();
1191 }
1192 }
1193
1194 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
1196 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
1197 }
1198
1199 fn forget_semantics_reach(&self) {
1201 self.semantics_reach.set(None);
1202 crate::modal_nodes::reach_changed(self.id.get());
1203 }
1204
1205 pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
1207 if !self
1209 .modifier_capabilities()
1210 .contains(NodeCapabilities::SEMANTICS)
1211 {
1212 return cranpose_foundation::SemanticsReach::default();
1213 }
1214 if let Some(reach) = self.semantics_reach.get() {
1215 return reach;
1216 }
1217 let reach = crate::modifier::semantics_reach_of_chain(self.modifier_chain.chain());
1218 self.semantics_reach.set(Some(reach));
1219 reach
1220 }
1221
1222 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
1223 &self.modifier_chain
1224 }
1225
1226 pub fn layout_state_handle(&self) -> &Rc<RefCell<LayoutState>> {
1229 &self.layout_state
1230 }
1231
1232 pub(crate) fn coordinator_geometry(&self) -> &crate::modifier::CoordinatorGeometry {
1233 &self.coordinator_geometry
1234 }
1235
1236 pub(crate) fn layout_runtime_state_handle(&self) -> Rc<RefCell<LayoutRuntimeState>> {
1237 self.layout_runtime_state.clone()
1238 }
1239
1240 #[cfg(test)]
1241 pub(crate) fn layout_runtime_debug_stats(&self) -> LayoutRuntimeDebugStats {
1242 self.layout_runtime_state.borrow().debug_stats()
1243 }
1244}
1245impl Clone for LayoutNode {
1246 fn clone(&self) -> Self {
1247 let mut node = Self {
1248 #[cfg(feature = "inspection")]
1249 source_trace: self.source_trace.clone(),
1250 modifier: self.modifier.clone(),
1251 modifier_chain: ModifierChainHandle::for_layout_direction(
1252 self.modifier_chain.layout_direction(),
1253 ),
1254 measure_policy: self.measure_policy.clone(),
1255 density: self.density,
1256 children: self.children.clone(),
1257 cache: self.cache.clone(),
1258 needs_measure: Cell::new(self.needs_measure.get()),
1259 needs_layout: Cell::new(self.needs_layout.get()),
1260 descendant_dirt: Cell::new(self.descendant_dirt.get()),
1261 parent_data: Cell::new(self.parent_data.get()),
1262 needs_semantics: Cell::new(self.needs_semantics.get()),
1263 descendant_needs_semantics: Cell::new(self.descendant_needs_semantics.get()),
1264 semantics_reach: Cell::new(None),
1265 needs_redraw: Cell::new(self.needs_redraw.get()),
1266 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
1267 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
1268 parent: NodeIdCell::new(self.parent.get()),
1269 folded_parent: NodeIdCell::new(self.folded_parent.get()),
1270 id: NodeIdCell::new(None),
1271 owner_context_id: Cell::new(None),
1272 debug_modifiers: Cell::new(self.debug_modifiers.get()),
1273 is_virtual: self.is_virtual,
1274 modifier_slices_snapshot: crate::modifier::SlicesSnapshot::default(),
1275 modifier_slices_dirty: Cell::new(true),
1276 layout_state: self.layout_state.clone(),
1277 layout_runtime_state: self.layout_runtime_state.clone(),
1278 coordinator_geometry: Rc::clone(&self.coordinator_geometry),
1279 };
1280 node.sync_modifier_chain(false);
1281 node
1282 }
1283}
1284
1285impl Node for LayoutNode {
1286 fn mount(&mut self) {
1287 self.modifier_chain.remount(self.id.get());
1288 crate::modal_nodes::reach_changed(self.id.get());
1289 }
1290
1291 fn unmount(&mut self) {
1292 self.modifier_chain.chain_mut().detach_nodes();
1293 }
1294
1295 fn set_node_id(&mut self, id: NodeId) {
1296 LayoutNode::set_node_id(self, id);
1297 }
1298
1299 fn insert_child(&mut self, child: NodeId) -> bool {
1300 if self.children.contains(&child) {
1301 return false;
1302 }
1303 self.children.push(child);
1304 self.cache.clear();
1305 self.mark_needs_measure();
1306 self.note_semantics_layout_change();
1307 true
1308 }
1309
1310 fn remove_child(&mut self, child: NodeId) -> bool {
1311 let before = self.children.len();
1312 self.children.retain(|&id| id != child);
1313 let removed = self.children.len() < before;
1314 if removed {
1315 self.cache.clear();
1316 self.mark_needs_measure();
1317 self.note_semantics_layout_change();
1318 }
1319 removed
1320 }
1321
1322 fn move_child(&mut self, from: usize, to: usize) {
1323 if from == to || from >= self.children.len() {
1324 return;
1325 }
1326 let child = self.children.remove(from);
1327 let target = to.min(self.children.len());
1328 self.children.insert(target, child);
1329 self.cache.clear();
1330 self.mark_needs_measure();
1331 self.note_semantics_layout_change();
1332 }
1333
1334 fn update_children(&mut self, children: &[NodeId]) {
1335 self.children.clear();
1336 self.children.extend_from_slice(children);
1337 self.cache.clear();
1338 self.mark_needs_measure();
1339 self.note_semantics_layout_change();
1340 }
1341
1342 fn collect_children_into(&self, out: &mut smallvec::SmallVec<[NodeId; 8]>) {
1343 out.clear();
1344 out.extend_from_slice(&self.children);
1345 }
1346
1347 fn owned_child_index(&self, child: NodeId) -> Option<usize> {
1348 self.children.iter().position(|&id| id == child)
1349 }
1350
1351 fn on_attached_to_parent(&mut self, parent: NodeId) {
1352 self.set_parent(parent);
1353 }
1354
1355 fn on_removed_from_parent(&mut self) {
1356 self.clear_parent();
1357 }
1358
1359 fn parent(&self) -> Option<NodeId> {
1360 self.parent.get()
1361 }
1362
1363 fn mark_needs_layout(&self) {
1364 self.needs_layout.set(true);
1365 }
1366
1367 fn needs_layout(&self) -> bool {
1368 self.needs_layout.get()
1369 }
1370
1371 fn mark_needs_measure(&self) {
1372 self.needs_measure.set(true);
1373 self.needs_layout.set(true);
1374 }
1375
1376 fn needs_measure(&self) -> bool {
1377 self.needs_measure.get()
1378 }
1379
1380 fn mark_descendant_needs_layout(&self, measure: bool) {
1381 self.descendant_dirt
1382 .set(self.descendant_dirt.get().marked(measure));
1383 if measure {
1384 self.cache.forget_intrinsics();
1385 }
1386 }
1387
1388 fn descendant_needs_layout(&self) -> bool {
1389 self.descendant_dirt.get().layout
1390 }
1391
1392 fn is_virtual(&self) -> bool {
1393 self.is_virtual
1394 }
1395
1396 fn descendant_needs_measure(&self) -> bool {
1397 self.descendant_dirt.get().measure
1398 }
1399
1400 fn mark_needs_semantics(&self) {
1401 self.needs_semantics.set(true);
1402 self.forget_semantics_reach();
1403 }
1404
1405 fn mark_descendant_needs_semantics(&self) {
1406 self.descendant_needs_semantics.set(true);
1407 }
1408
1409 fn needs_semantics(&self) -> bool {
1410 LayoutNode::needs_semantics(self)
1411 }
1412
1413 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1414 if self.parent.get().is_none() {
1415 self.parent.set(Some(parent));
1416 }
1417 }
1418
1419 fn recycle_key(&self) -> Option<TypeId> {
1420 Some(TypeId::of::<Self>())
1421 }
1422
1423 fn recycle_pool_limit(&self) -> Option<usize> {
1424 Some(RECYCLED_LAYOUT_NODE_POOL_LIMIT)
1425 }
1426
1427 fn prepare_for_recycle(&mut self) {
1428 *self = Self::new_recycled_shell(self.is_virtual);
1429 }
1430
1431 fn rehouse_for_recycle(&self) -> Option<Box<dyn cranpose_core::Node>> {
1432 Some(Box::new(Self::new_recycled_shell(self.is_virtual)))
1433 }
1434
1435 fn rehouse_for_live_compaction(&mut self) -> Option<Box<dyn cranpose_core::Node>> {
1436 let mut node = std::mem::replace(self, Self::new_recycled_shell(self.is_virtual));
1442 node.children.shrink_to_fit();
1443 Some(Box::new(node))
1444 }
1445}
1446
1447impl Drop for LayoutNode {
1448 fn drop(&mut self) {
1449 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
1450 unregister_layout_node(owner_context_id, id);
1451 }
1452 }
1453}
1454
1455const MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY: usize = 128;
1456const VIRTUAL_NODE_ID_START: NodeId = 0xC0000000;
1457
1458#[cfg(test)]
1459#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1460struct LayoutNodeRegistryDebugStats {
1461 len: usize,
1462 capacity: usize,
1463}
1464
1465struct LayoutNodeRegistryEntry {
1466 parent: Option<NodeId>,
1467 modifier_child_capabilities: NodeCapabilities,
1468 modifier_locals: Option<ModifierLocalsHandle>,
1469}
1470
1471pub(crate) struct LayoutNodeRegistryState {
1472 entries: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>>,
1473 virtual_node_id_counter: Cell<NodeId>,
1474}
1475
1476impl LayoutNodeRegistryState {
1477 pub(crate) fn new() -> Self {
1478 Self {
1479 entries: RefCell::new(HashMap::default()),
1480 virtual_node_id_counter: Cell::new(VIRTUAL_NODE_ID_START),
1481 }
1482 }
1483
1484 fn register(&self, id: NodeId, node: &LayoutNode) {
1485 self.entries.borrow_mut().insert(
1486 id,
1487 LayoutNodeRegistryEntry {
1488 parent: node.parent(),
1489 modifier_child_capabilities: node.modifier_child_capabilities(),
1490 modifier_locals: node.modifier_locals_handle(),
1491 },
1492 );
1493 }
1494
1495 fn unregister(&self, id: NodeId) {
1496 let mut entries = self.entries.borrow_mut();
1497 entries.remove(&id);
1498 let should_shrink = (entries.len() <= MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
1499 && entries.capacity() > MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1500 || entries.capacity()
1501 > entries
1502 .len()
1503 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1504 .saturating_mul(4);
1505 if should_shrink {
1506 let retained = entries
1507 .len()
1508 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY);
1509 let mut rebuilt = HashMap::default();
1510 rebuilt.reserve(retained);
1511 rebuilt.extend(entries.drain());
1512 *entries = rebuilt;
1513 }
1514 }
1515
1516 fn update_entry(
1517 &self,
1518 id: NodeId,
1519 parent: Option<NodeId>,
1520 modifier_child_capabilities: NodeCapabilities,
1521 modifier_locals: Option<ModifierLocalsHandle>,
1522 ) {
1523 if let Some(entry) = self.entries.borrow_mut().get_mut(&id) {
1524 entry.parent = parent;
1525 entry.modifier_child_capabilities = modifier_child_capabilities;
1526 entry.modifier_locals = modifier_locals;
1527 }
1528 }
1529
1530 #[cfg(test)]
1531 fn stats(&self) -> LayoutNodeRegistryDebugStats {
1532 let entries = self.entries.borrow();
1533 LayoutNodeRegistryDebugStats {
1534 len: entries.len(),
1535 capacity: entries.capacity(),
1536 }
1537 }
1538
1539 fn allocate_virtual_node_id(&self) -> NodeId {
1540 let id = self.virtual_node_id_counter.get();
1541 self.virtual_node_id_counter.set(id.wrapping_add(1));
1542 id
1543 }
1544
1545 fn resolve_modifier_local_from_parent_chain(
1546 &self,
1547 start: Option<NodeId>,
1548 token: &ModifierLocalToken,
1549 ) -> Option<ResolvedModifierLocal> {
1550 let mut current = start;
1551 while let Some(parent_id) = current {
1552 let (next_parent, resolved) = {
1553 let entries = self.entries.borrow();
1554 if let Some(entry) = entries.get(&parent_id) {
1555 let resolved = entry
1556 .modifier_child_capabilities
1557 .contains(NodeCapabilities::MODIFIER_LOCALS)
1558 .then_some(entry.modifier_locals.as_ref())
1559 .flatten()
1560 .and_then(|locals| locals.borrow().resolve(token))
1561 .map(|value| value.with_source(ModifierLocalSource::Ancestor));
1562 (entry.parent, resolved)
1563 } else {
1564 (None, None)
1565 }
1566 };
1567 if let Some(value) = resolved {
1568 return Some(value);
1569 }
1570 current = next_parent;
1571 }
1572 None
1573 }
1574}
1575
1576pub(crate) fn register_layout_node(
1577 id: NodeId,
1578 node: &LayoutNode,
1579) -> crate::render_state::AppContextId {
1580 crate::render_state::with_current_layout_node_registry(|registry| registry.register(id, node))
1581}
1582
1583pub(crate) fn unregister_layout_node(
1584 owner_context_id: crate::render_state::AppContextId,
1585 id: NodeId,
1586) {
1587 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1588 owner_context_id,
1589 |registry| {
1590 registry.unregister(id);
1591 },
1592 );
1593}
1594
1595#[cfg(test)]
1596fn layout_node_registry_stats() -> LayoutNodeRegistryDebugStats {
1597 crate::render_state::with_layout_node_registry(LayoutNodeRegistryState::stats)
1598}
1599
1600pub(crate) fn allocate_virtual_node_id() -> NodeId {
1601 crate::render_state::with_layout_node_registry(
1602 LayoutNodeRegistryState::allocate_virtual_node_id,
1603 )
1604}
1605
1606fn resolve_modifier_local_from_parent_chain(
1607 start: Option<NodeId>,
1608 token: &ModifierLocalToken,
1609) -> Option<ResolvedModifierLocal> {
1610 crate::render_state::with_layout_node_registry(|registry| {
1611 registry.resolve_modifier_local_from_parent_chain(start, token)
1612 })
1613}
1614
1615#[cfg(test)]
1616#[path = "tests/layout_node_tests.rs"]
1617mod tests;