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)]
108pub(crate) struct DescendantDirt {
109 pub(crate) layout: bool,
110 pub(crate) measure: bool,
111}
112
113impl DescendantDirt {
114 pub(crate) fn marked(self, measure: bool) -> Self {
115 Self {
116 layout: true,
117 measure: self.measure || measure,
118 }
119 }
120}
121
122#[derive(Clone, Debug, Default)]
123pub struct LayoutState {
124 size: Size,
125 position: Point,
126 is_placed: bool,
127 node_id: Option<NodeId>,
128 cleared_in_pass: u64,
130 content_offset: Point,
131 semantics_stamp: u64,
132}
133
134impl LayoutState {
135 pub fn size(&self) -> Size {
136 self.size
137 }
138
139 pub fn position(&self) -> Point {
140 self.position
141 }
142
143 pub fn content_offset(&self) -> Point {
146 self.content_offset
147 }
148
149 pub fn set_content_offset(&mut self, offset: Point) {
152 if self.content_offset != offset {
153 self.content_offset = offset;
154 self.note_semantics_change();
155 }
156 }
157
158 pub fn at_origin(mut self) -> Self {
162 self.position = Point::default();
163 self
164 }
165
166 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)]
257pub enum IntrinsicKind {
258 MinWidth(f32),
259 MaxWidth(f32),
260 MinHeight(f32),
261 MaxHeight(f32),
262}
263
264impl IntrinsicKind {
265 fn discriminant(&self) -> u8 {
266 match self {
267 IntrinsicKind::MinWidth(_) => 0,
268 IntrinsicKind::MaxWidth(_) => 1,
269 IntrinsicKind::MinHeight(_) => 2,
270 IntrinsicKind::MaxHeight(_) => 3,
271 }
272 }
273
274 fn value_bits(&self) -> u32 {
275 match self {
276 IntrinsicKind::MinWidth(value)
277 | IntrinsicKind::MaxWidth(value)
278 | IntrinsicKind::MinHeight(value)
279 | IntrinsicKind::MaxHeight(value) => value.to_bits(),
280 }
281 }
282}
283
284impl PartialEq for IntrinsicKind {
285 fn eq(&self, other: &Self) -> bool {
286 self.discriminant() == other.discriminant() && self.value_bits() == other.value_bits()
287 }
288}
289
290impl Eq for IntrinsicKind {}
291
292impl Hash for IntrinsicKind {
293 fn hash<H: Hasher>(&self, state: &mut H) {
294 self.discriminant().hash(state);
295 self.value_bits().hash(state);
296 }
297}
298
299#[derive(Default)]
300struct NodeCacheState {
301 epoch: u64,
302 measurement: Option<MeasurementCacheEntry>,
303 intrinsics: Vec<(IntrinsicKind, f32)>,
304 intrinsics_read: bool,
306}
307
308#[derive(Default)]
309pub(crate) struct LayoutNodeCacheHandles {
310 state: Rc<RefCell<NodeCacheState>>,
311}
312
313impl Clone for LayoutNodeCacheHandles {
314 fn clone(&self) -> Self {
315 Self {
316 state: Rc::clone(&self.state),
317 }
318 }
319
320 fn clone_from(&mut self, source: &Self) {
321 if !Rc::ptr_eq(&self.state, &source.state) {
322 self.state = Rc::clone(&source.state);
323 }
324 }
325}
326
327impl LayoutNodeCacheHandles {
328 pub(crate) fn clear(&self) {
329 let mut state = self.state.borrow_mut();
330 state.measurement = None;
331 state.intrinsics.clear();
332 state.intrinsics_read = false;
333 state.epoch = 0;
334 }
335
336 pub(crate) fn activate(&self, epoch: u64) {
337 let mut state = self.state.borrow_mut();
338 if state.epoch != epoch {
339 state.measurement = None;
340 state.intrinsics.clear();
341 state.intrinsics_read = false;
342 state.epoch = epoch;
343 }
344 }
345
346 pub(crate) fn forget_intrinsics(&self) {
350 self.state.borrow_mut().intrinsics.clear();
351 }
352
353 pub(crate) fn epoch(&self) -> u64 {
354 self.state.borrow().epoch
355 }
356
357 pub(crate) fn get_measurement(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
361 let state = self.state.borrow();
362 state
363 .measurement
364 .as_ref()
365 .filter(|entry| {
366 entry.constraints == constraints
367 || entry.hold.is_some_and(|hold| hold.contains(constraints))
368 })
369 .map(|entry| Rc::clone(&entry.measured))
370 }
371
372 pub(crate) fn measured_constraints(&self) -> Option<Constraints> {
374 self.state
375 .borrow()
376 .measurement
377 .as_ref()
378 .map(|entry| entry.constraints)
379 }
380
381 pub(crate) fn has_intrinsics(&self) -> bool {
383 self.state.borrow().intrinsics_read
384 }
385
386 pub(crate) fn hold_of(
388 &self,
389 measured: &Rc<MeasuredNode>,
390 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
391 self.state
392 .borrow()
393 .measurement
394 .as_ref()
395 .filter(|entry| Rc::ptr_eq(&entry.measured, measured))
396 .and_then(|entry| entry.hold)
397 }
398
399 pub(crate) fn store_measurement(&self, constraints: Constraints, measured: Rc<MeasuredNode>) {
402 let mut state = self.state.borrow_mut();
403 let hold = state
404 .measurement
405 .as_ref()
406 .filter(|entry| Rc::ptr_eq(&entry.measured, &measured))
407 .and_then(|entry| entry.hold);
408 state.measurement = Some(MeasurementCacheEntry {
409 constraints,
410 measured,
411 hold,
412 });
413 }
414
415 pub(crate) fn store_held_measurement(
418 &self,
419 constraints: Constraints,
420 measured: Rc<MeasuredNode>,
421 hold: Option<cranpose_ui_layout::ConstraintsHold>,
422 ) {
423 self.state.borrow_mut().measurement = Some(MeasurementCacheEntry {
424 constraints,
425 measured,
426 hold,
427 });
428 }
429
430 pub(crate) fn get_intrinsic(&self, kind: &IntrinsicKind) -> Option<f32> {
431 let state = self.state.borrow();
432 state
433 .intrinsics
434 .iter()
435 .find(|(stored_kind, _)| stored_kind == kind)
436 .map(|(_, value)| *value)
437 }
438
439 pub(crate) fn store_intrinsic(&self, kind: IntrinsicKind, value: f32) {
440 let mut state = self.state.borrow_mut();
441 state.intrinsics_read = true;
442 if let Some((_, existing)) = state
443 .intrinsics
444 .iter_mut()
445 .find(|(stored_kind, _)| stored_kind == &kind)
446 {
447 *existing = value;
448 } else {
449 state.intrinsics.push((kind, value));
450 }
451 }
452}
453
454struct NodeIdCell(Cell<NodeId>);
458
459const NO_NODE_ID: NodeId = NodeId::MAX;
462
463impl NodeIdCell {
464 fn new(id: Option<NodeId>) -> Self {
465 Self(Cell::new(id.unwrap_or(NO_NODE_ID)))
466 }
467
468 fn get(&self) -> Option<NodeId> {
469 let id = self.0.get();
470 (id != NO_NODE_ID).then_some(id)
471 }
472
473 fn set(&self, id: Option<NodeId>) {
474 debug_assert_ne!(id, Some(NO_NODE_ID), "no node takes the none id");
475 self.0.set(id.unwrap_or(NO_NODE_ID));
476 }
477
478 fn replace(&self, id: Option<NodeId>) -> Option<NodeId> {
479 let previous = self.get();
480 self.set(id);
481 previous
482 }
483}
484
485#[repr(C)]
492pub struct LayoutNode {
493 cache: LayoutNodeCacheHandles,
494 layout_state: Rc<RefCell<LayoutState>>,
495 layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
496 pub children: Vec<NodeId>,
498 parent_data: Cell<cranpose_ui_layout::ParentData>,
501 needs_measure: Cell<bool>,
502 needs_layout: Cell<bool>,
503 descendant_dirt: Cell<DescendantDirt>,
505
506 pub measure_policy: Rc<dyn MeasurePolicy>,
507 coordinator_geometry: Rc<crate::modifier::CoordinatorGeometry>,
510 density: crate::density::Density,
511 is_virtual: bool,
512 needs_semantics: Cell<bool>,
513 descendant_needs_semantics: Cell<bool>,
517 needs_redraw: Cell<bool>,
518 needs_pointer_pass: Cell<bool>,
519 needs_focus_sync: Cell<bool>,
520 modifier_slices_dirty: Cell<bool>,
521 debug_modifiers: Cell<bool>,
522
523 id: NodeIdCell,
524 parent: NodeIdCell,
525 folded_parent: NodeIdCell,
526 modifier_slices_snapshot: crate::modifier::SlicesSnapshot,
527
528 semantics_reach: Cell<Option<cranpose_foundation::SemanticsReach>>,
532 owner_context_id: Cell<Option<crate::render_state::AppContextId>>,
533 modifier_chain: ModifierChainHandle,
534 pub modifier: Modifier,
535 #[cfg(feature = "inspection")]
537 pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
538}
539
540pub(crate) const RECYCLED_LAYOUT_NODE_POOL_LIMIT: usize = 128;
541
542pub(crate) fn empty_measure_policy() -> Rc<dyn MeasurePolicy> {
543 crate::thread_state::with(|state| {
544 Rc::clone(
545 state
546 .empty_measure_policy
547 .get_or_init(|| Rc::new(crate::layout::policies::EmptyMeasurePolicy)),
548 )
549 })
550}
551
552impl LayoutNode {
553 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
554 Self::new_with_virtual(modifier, measure_policy, false)
555 }
556
557 pub fn new_virtual() -> Self {
560 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), true)
561 }
562
563 fn new_recycled_shell(is_virtual: bool) -> Self {
564 let mut shell =
565 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), is_virtual);
566 shell.needs_measure.set(false);
567 shell.needs_layout.set(false);
568 shell.needs_semantics.set(false);
569 shell.descendant_needs_semantics.set(false);
570 shell.needs_redraw.set(false);
571 shell.needs_pointer_pass.set(false);
572 shell.needs_focus_sync.set(false);
573 shell.parent.set(None);
574 shell.folded_parent.set(None);
575 shell.id.set(None);
576 shell.owner_context_id.set(None);
577 shell.debug_modifiers.set(false);
578 shell.modifier_slices_dirty = Cell::new(true);
579 shell
580 }
581
582 fn new_with_virtual(
583 modifier: Modifier,
584 measure_policy: Rc<dyn MeasurePolicy>,
585 is_virtual: bool,
586 ) -> Self {
587 let layout_runtime_state = Rc::new(RefCell::new(LayoutRuntimeState::new(Rc::clone(
588 &measure_policy,
589 ))));
590 let mut node = Self {
591 #[cfg(feature = "inspection")]
592 source_trace: cranpose_core::source_trace::current_source_trace(),
593 modifier,
594 modifier_chain: ModifierChainHandle::detached(),
595 measure_policy,
596 density: crate::density::Density::default(),
597 children: Vec::new(),
598 cache: LayoutNodeCacheHandles::default(),
599 needs_measure: Cell::new(true),
600 needs_layout: Cell::new(true),
601 descendant_dirt: Cell::default(),
602 parent_data: Cell::default(),
603 needs_semantics: Cell::new(true),
604 descendant_needs_semantics: Cell::new(false),
605 semantics_reach: Cell::new(None),
606 needs_redraw: Cell::new(true),
607 needs_pointer_pass: Cell::new(false),
608 needs_focus_sync: Cell::new(false),
609 parent: NodeIdCell::new(None),
610 folded_parent: NodeIdCell::new(None),
611 id: NodeIdCell::new(None),
612 owner_context_id: Cell::new(None),
613 debug_modifiers: Cell::new(false),
614 is_virtual,
615 modifier_slices_snapshot: crate::modifier::SlicesSnapshot::default(),
616 modifier_slices_dirty: Cell::new(true),
617 layout_state: Rc::new(RefCell::new(LayoutState::default())),
618 layout_runtime_state,
619 coordinator_geometry: Rc::default(),
620 };
621 node.sync_modifier_chain(false);
622 node
623 }
624
625 pub fn set_modifier(&mut self, modifier: Modifier) {
626 #[cfg(feature = "inspection")]
627 {
628 let trace = cranpose_core::source_trace::current_source_trace();
629 if !trace.is_empty() {
630 self.source_trace = trace;
631 }
632 }
633 if self.modifier == modifier
637 && !self
638 .modifier_capabilities()
639 .contains(NodeCapabilities::MODIFIER_LOCALS)
640 {
641 return;
642 }
643 let text_only = self
647 .modifier
648 .differs_only_in::<crate::text_modifier_node::TextModifierElement>(&modifier);
649 let modifier_changed = text_only || !self.modifier.structural_eq(&modifier);
650 self.modifier = modifier;
651 self.sync_modifier_chain(text_only);
652 if modifier_changed {
653 self.cache.clear();
654 self.request_semantics_update();
655 }
656 }
657
658 fn sync_modifier_chain(&mut self, text_only: bool) {
662 let prev_caps = self.modifier_capabilities();
663 let start_parent = self.parent();
664 let mut resolver = move |token: &ModifierLocalToken| {
665 resolve_modifier_local_from_parent_chain(start_parent, token)
666 };
667 self.modifier_chain
668 .set_debug_logging(self.debug_modifiers.get());
669 let mut context = self.modifier_chain.context(self.id.get());
670 let in_place = text_only
671 && self
672 .modifier_chain
673 .update_elements_in_place::<crate::text_modifier_node::TextModifierElement>(
674 &self.modifier,
675 &mut context,
676 );
677 let modifier_local_invalidations = if in_place {
678 ModifierInvalidations::new()
679 } else {
680 let invalidations = self.modifier_chain.update_with_resolver(
681 &self.modifier,
682 &mut resolver,
683 &mut context,
684 );
685 self.refresh_parent_data();
686 invalidations
687 };
688 if prev_caps.contains(NodeCapabilities::WINDOW_ROOT)
689 != self
690 .modifier_capabilities()
691 .contains(NodeCapabilities::WINDOW_ROOT)
692 {
693 self.note_semantics_layout_change();
694 }
695 if !in_place {
698 self.forget_semantics_reach();
699 }
700 let keep_slices = in_place && !self.modifier_slices_dirty.get();
701 if !keep_slices {
702 self.modifier_slices_dirty.set(true);
703 }
704
705 let mut invalidations = context.take_invalidations();
706 invalidations.extend(modifier_local_invalidations);
707 self.dispatch_modifier_invalidations_with_prev(&invalidations, prev_caps, keep_slices);
708 if !in_place {
711 self.refresh_registry_state();
712 }
713 }
714
715 fn update_modifier_slices_cache(&self) {
716 self.modifier_slices_snapshot.collect(
717 self.modifier_chain.chain(),
718 &self.coordinator_geometry,
719 self.density.density(),
720 );
721 self.modifier_slices_dirty.set(false);
722 }
723
724 #[cfg(test)]
726 pub(crate) fn modifier_slices_ready(&self) -> bool {
727 !self.modifier_slices_dirty.get()
728 }
729
730 pub(crate) fn mark_modifier_slices_dirty(&self) {
731 self.modifier_slices_dirty.set(true);
732 }
733
734 #[cfg(test)]
735 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
736 self.dispatch_modifier_invalidations_with_prev(
737 invalidations,
738 NodeCapabilities::empty(),
739 false,
740 );
741 }
742
743 fn dispatch_modifier_invalidations_with_prev(
746 &self,
747 invalidations: &[ModifierInvalidation],
748 prev_caps: NodeCapabilities,
749 keep_slices: bool,
750 ) {
751 let curr_caps = self.modifier_capabilities();
752 for invalidation in invalidations {
753 if !keep_slices {
754 self.modifier_slices_dirty.set(true);
755 }
756 let has_capability =
757 |capability| curr_caps.contains(capability) || prev_caps.contains(capability);
758 match invalidation.kind() {
759 InvalidationKind::Layout => {
760 if has_capability(NodeCapabilities::LAYOUT) {
761 self.mark_needs_measure();
762 if let Some(id) = self.id.get() {
763 log_layout_invalidation_dispatch(
764 id,
765 invalidation,
766 curr_caps,
767 prev_caps,
768 &self.modifier,
769 );
770 let inside_composition =
771 cranpose_core::composer_context::try_with_composer(|_| ())
772 .is_some();
773 if inside_composition {
774 cranpose_core::bubble_measure_dirty_in_composer(id);
775 } else {
776 crate::schedule_layout_repass(id);
777 }
778 }
779 }
780 }
781 InvalidationKind::Draw => {
782 if has_capability(NodeCapabilities::DRAW)
783 || invalidation.capabilities().contains(NodeCapabilities::DRAW)
784 {
785 self.mark_needs_redraw();
786 }
787 }
788 InvalidationKind::PointerInput => {
789 if has_capability(NodeCapabilities::POINTER_INPUT) {
790 self.mark_needs_pointer_pass();
791 crate::request_pointer_invalidation();
792 if let Some(id) = self.id.get() {
793 crate::schedule_pointer_repass(id);
794 }
795 }
796 }
797 InvalidationKind::Semantics => {
798 self.request_semantics_update();
799 }
800 InvalidationKind::Focus => {
801 if has_capability(NodeCapabilities::FOCUS) {
802 self.mark_needs_focus_sync();
803 crate::request_focus_invalidation();
804 if let Some(id) = self.id.get() {
805 crate::schedule_focus_invalidation(id);
806 }
807 }
808 }
809 }
810 }
811 }
812
813 pub fn density(&self) -> crate::density::Density {
815 self.density
816 }
817
818 pub fn set_density(&mut self, density: crate::density::Density) {
820 if self.density != density {
821 self.density = density;
822 self.cache.clear();
823 self.modifier_slices_dirty.set(true);
824 self.mark_needs_measure();
825 }
826 }
827
828 pub fn set_layout_direction(&mut self, direction: crate::LayoutDirection) {
830 if self.modifier_chain.set_layout_direction(direction) {
831 self.refresh_parent_data();
832 self.cache.clear();
833 self.modifier_slices_dirty.set(true);
834 self.mark_needs_measure();
835 }
836 }
837
838 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
839 if !Rc::ptr_eq(&self.measure_policy, &policy) {
840 self.measure_policy = policy;
841 self.cache.clear();
842 self.mark_needs_measure();
843 if let Some(id) = self.id.get() {
844 cranpose_core::bubble_measure_dirty_in_composer(id);
845 }
846 }
847 }
848
849 pub fn mark_needs_measure(&self) {
851 self.needs_measure.set(true);
852 self.needs_layout.set(true);
853 }
854
855 pub fn mark_needs_layout(&self) {
857 self.needs_layout.set(true);
858 }
859
860 pub fn mark_needs_redraw(&self) {
862 self.needs_redraw.set(true);
863 if let Some(id) = self.id.get() {
864 crate::schedule_draw_repass(id);
865 }
866 crate::request_render_invalidation();
867 }
868
869 pub fn needs_measure(&self) -> bool {
871 self.needs_measure.get()
872 }
873
874 pub fn needs_layout(&self) -> bool {
876 self.needs_layout.get()
877 }
878
879 pub fn mark_needs_semantics(&self) {
881 self.needs_semantics.set(true);
882 self.forget_semantics_reach();
883 }
884
885 pub(crate) fn clear_needs_semantics(&self) {
886 self.needs_semantics.set(false);
887 self.descendant_needs_semantics.set(false);
888 }
889
890 pub fn needs_semantics(&self) -> bool {
893 self.needs_semantics.get() || self.descendant_needs_semantics.get()
894 }
895
896 pub(crate) fn semantics_changed(&self) -> bool {
899 self.needs_semantics.get()
900 }
901
902 pub fn needs_redraw(&self) -> bool {
904 self.needs_redraw.get()
905 }
906
907 pub fn clear_needs_redraw(&self) {
908 self.needs_redraw.set(false);
909 }
910
911 fn request_semantics_update(&self) {
912 let already_dirty = self.needs_semantics.replace(true);
913 if already_dirty {
914 return;
915 }
916
917 if let Some(id) = self.id.get() {
918 cranpose_core::queue_semantics_invalidation(id);
919 }
920 }
921
922 pub(crate) fn clear_needs_measure(&self) {
923 self.needs_measure.set(false);
924 }
925
926 pub(crate) fn clear_needs_layout(&self) {
929 self.needs_layout.set(false);
930 self.descendant_dirt.set(DescendantDirt::default());
931 }
932
933 pub fn mark_needs_pointer_pass(&self) {
935 self.needs_pointer_pass.set(true);
936 }
937
938 pub fn needs_pointer_pass(&self) -> bool {
940 self.needs_pointer_pass.get()
941 }
942
943 pub fn clear_needs_pointer_pass(&self) {
945 self.needs_pointer_pass.set(false);
946 }
947
948 pub fn mark_needs_focus_sync(&self) {
950 self.needs_focus_sync.set(true);
951 }
952
953 pub fn needs_focus_sync(&self) -> bool {
955 self.needs_focus_sync.get()
956 }
957
958 pub fn clear_needs_focus_sync(&self) {
960 self.needs_focus_sync.set(false);
961 }
962
963 pub fn set_node_id(&mut self, id: NodeId) {
965 let previous = self.id.replace(Some(id));
966 if let Some(existing) = previous
967 && let Some(owner_context_id) = self.owner_context_id.take()
968 {
969 unregister_layout_node(owner_context_id, existing);
970 }
971 self.layout_state.borrow_mut().set_node_id(id);
972 let owner_context_id = register_layout_node(id, self);
975 self.owner_context_id.set(Some(owner_context_id));
976
977 let mut context = self.modifier_chain.context(Some(id));
978 self.modifier_chain.move_to_node(previous, &mut context);
979 let invalidations = context.take_invalidations();
980 self.dispatch_modifier_invalidations_with_prev(
981 &invalidations,
982 NodeCapabilities::empty(),
983 false,
984 );
985 self.modifier_slices_dirty.set(true);
987 }
988
989 pub fn node_id(&self) -> Option<NodeId> {
991 self.id.get()
992 }
993
994 pub fn set_parent(&self, parent: NodeId) {
997 self.folded_parent.set(Some(parent));
998 self.parent.set(Some(parent));
999 self.refresh_registry_state();
1000 }
1001
1002 pub fn clear_parent(&self) {
1004 self.folded_parent.set(None);
1005 self.parent.set(None);
1006 self.refresh_registry_state();
1007 }
1008
1009 pub fn parent(&self) -> Option<NodeId> {
1011 self.parent.get()
1012 }
1013
1014 pub fn folded_parent(&self) -> Option<NodeId> {
1016 self.folded_parent.get()
1017 }
1018
1019 pub(crate) fn cache_handles(&self) -> &LayoutNodeCacheHandles {
1020 &self.cache
1021 }
1022
1023 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
1024 self.modifier_chain.resolved_modifiers()
1025 }
1026
1027 pub(crate) fn parent_data(&self) -> cranpose_ui_layout::ParentData {
1029 self.parent_data.get()
1030 }
1031
1032 fn refresh_parent_data(&self) {
1035 self.parent_data.set(crate::layout::parent_data_of(
1036 self.modifier_chain.resolved_modifiers().layout_properties(),
1037 ));
1038 }
1039
1040 pub fn modifier_capabilities(&self) -> NodeCapabilities {
1041 self.modifier_chain.capabilities()
1042 }
1043
1044 pub fn is_window_root(&self) -> bool {
1048 self.modifier_capabilities()
1049 .contains(NodeCapabilities::WINDOW_ROOT)
1050 }
1051
1052 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
1053 self.modifier_chain.aggregate_child_capabilities()
1054 }
1055
1056 pub fn set_debug_modifiers(&mut self, enabled: bool) {
1057 self.debug_modifiers.set(enabled);
1058 self.modifier_chain.set_debug_logging(enabled);
1059 }
1060
1061 pub fn debug_modifiers_enabled(&self) -> bool {
1062 self.debug_modifiers.get()
1063 }
1064
1065 pub fn modifier_locals_handle(&self) -> Option<ModifierLocalsHandle> {
1068 self.modifier_chain.modifier_locals_handle()
1069 }
1070
1071 pub fn has_layout_modifier_nodes(&self) -> bool {
1072 self.modifier_capabilities()
1073 .contains(NodeCapabilities::LAYOUT)
1074 }
1075
1076 pub fn has_draw_modifier_nodes(&self) -> bool {
1077 self.modifier_capabilities()
1078 .contains(NodeCapabilities::DRAW)
1079 }
1080
1081 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
1082 self.modifier_capabilities()
1083 .contains(NodeCapabilities::POINTER_INPUT)
1084 }
1085
1086 pub fn has_semantics_modifier_nodes(&self) -> bool {
1087 self.modifier_capabilities()
1088 .contains(NodeCapabilities::SEMANTICS)
1089 }
1090
1091 pub fn has_focus_modifier_nodes(&self) -> bool {
1092 self.modifier_capabilities()
1093 .contains(NodeCapabilities::FOCUS)
1094 }
1095
1096 fn refresh_registry_state(&self) {
1097 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
1098 let parent = self.parent();
1099 let capabilities = self.modifier_child_capabilities();
1100 let modifier_locals = self.modifier_locals_handle();
1101 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1102 owner_context_id,
1103 |registry| {
1104 registry.update_entry(id, parent, capabilities, modifier_locals);
1105 },
1106 );
1107 }
1108 }
1109
1110 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
1111 if self.modifier_slices_dirty.get() {
1112 self.update_modifier_slices_cache();
1113 }
1114 self.modifier_slices_snapshot.get()
1115 }
1116
1117 pub fn layout_state(&self) -> LayoutState {
1119 self.layout_state.borrow().clone()
1120 }
1121
1122 pub fn measured_size(&self) -> Size {
1124 self.layout_state.borrow().size
1125 }
1126
1127 pub fn position(&self) -> Point {
1129 self.layout_state.borrow().position
1130 }
1131
1132 pub fn is_placed(&self) -> bool {
1134 self.layout_state.borrow().is_placed
1135 }
1136
1137 pub fn set_measured_size(&self, size: Size) {
1141 self.layout_state.borrow_mut().set_size(size);
1142 }
1143
1144 pub fn set_position(&self, position: Point) {
1147 self.layout_state.borrow_mut().place(position);
1148 }
1149
1150 pub fn set_content_offset(&self, offset: Point) {
1152 self.layout_state.borrow_mut().set_content_offset(offset);
1153 }
1154
1155 pub fn clear_placed(&self) {
1157 self.layout_state.borrow_mut().clear_placed();
1158 }
1159
1160 fn note_semantics_layout_change(&self) {
1161 if let Ok(mut state) = self.layout_state.try_borrow_mut() {
1162 state.note_semantics_change();
1163 }
1164 }
1165
1166 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
1167 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
1168 }
1169
1170 fn forget_semantics_reach(&self) {
1172 self.semantics_reach.set(None);
1173 crate::modal_nodes::reach_changed(self.id.get());
1174 }
1175
1176 pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
1178 if !self
1180 .modifier_capabilities()
1181 .contains(NodeCapabilities::SEMANTICS)
1182 {
1183 return cranpose_foundation::SemanticsReach::default();
1184 }
1185 if let Some(reach) = self.semantics_reach.get() {
1186 return reach;
1187 }
1188 let reach = crate::modifier::semantics_reach_of_chain(self.modifier_chain.chain());
1189 self.semantics_reach.set(Some(reach));
1190 reach
1191 }
1192
1193 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
1194 &self.modifier_chain
1195 }
1196
1197 pub fn with_text_field_modifier_mut<R>(
1202 &mut self,
1203 f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
1204 ) -> Option<R> {
1205 self.modifier_chain.with_text_field_modifier_mut(f)
1206 }
1207
1208 pub fn layout_state_handle(&self) -> &Rc<RefCell<LayoutState>> {
1211 &self.layout_state
1212 }
1213
1214 pub(crate) fn coordinator_geometry(&self) -> &crate::modifier::CoordinatorGeometry {
1215 &self.coordinator_geometry
1216 }
1217
1218 pub(crate) fn layout_runtime_state_handle(&self) -> Rc<RefCell<LayoutRuntimeState>> {
1219 self.layout_runtime_state.clone()
1220 }
1221
1222 #[cfg(test)]
1223 pub(crate) fn layout_runtime_debug_stats(&self) -> LayoutRuntimeDebugStats {
1224 self.layout_runtime_state.borrow().debug_stats()
1225 }
1226}
1227impl Clone for LayoutNode {
1228 fn clone(&self) -> Self {
1229 let mut node = Self {
1230 #[cfg(feature = "inspection")]
1231 source_trace: self.source_trace.clone(),
1232 modifier: self.modifier.clone(),
1233 modifier_chain: ModifierChainHandle::for_layout_direction(
1234 self.modifier_chain.layout_direction(),
1235 ),
1236 measure_policy: self.measure_policy.clone(),
1237 density: self.density,
1238 children: self.children.clone(),
1239 cache: self.cache.clone(),
1240 needs_measure: Cell::new(self.needs_measure.get()),
1241 needs_layout: Cell::new(self.needs_layout.get()),
1242 descendant_dirt: Cell::new(self.descendant_dirt.get()),
1243 parent_data: Cell::new(self.parent_data.get()),
1244 needs_semantics: Cell::new(self.needs_semantics.get()),
1245 descendant_needs_semantics: Cell::new(self.descendant_needs_semantics.get()),
1246 semantics_reach: Cell::new(None),
1247 needs_redraw: Cell::new(self.needs_redraw.get()),
1248 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
1249 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
1250 parent: NodeIdCell::new(self.parent.get()),
1251 folded_parent: NodeIdCell::new(self.folded_parent.get()),
1252 id: NodeIdCell::new(None),
1253 owner_context_id: Cell::new(None),
1254 debug_modifiers: Cell::new(self.debug_modifiers.get()),
1255 is_virtual: self.is_virtual,
1256 modifier_slices_snapshot: crate::modifier::SlicesSnapshot::default(),
1257 modifier_slices_dirty: Cell::new(true),
1258 layout_state: self.layout_state.clone(),
1259 layout_runtime_state: self.layout_runtime_state.clone(),
1260 coordinator_geometry: Rc::clone(&self.coordinator_geometry),
1261 };
1262 node.sync_modifier_chain(false);
1263 node
1264 }
1265}
1266
1267impl Node for LayoutNode {
1268 fn mount(&mut self) {
1269 self.modifier_chain.remount(self.id.get());
1270 crate::modal_nodes::reach_changed(self.id.get());
1271 }
1272
1273 fn unmount(&mut self) {
1274 self.modifier_chain.chain_mut().detach_nodes();
1275 }
1276
1277 fn set_node_id(&mut self, id: NodeId) {
1278 LayoutNode::set_node_id(self, id);
1279 }
1280
1281 fn insert_child(&mut self, child: NodeId) -> bool {
1282 if self.children.contains(&child) {
1283 return false;
1284 }
1285 self.children.push(child);
1286 self.cache.clear();
1287 self.mark_needs_measure();
1288 self.note_semantics_layout_change();
1289 true
1290 }
1291
1292 fn remove_child(&mut self, child: NodeId) -> bool {
1293 let before = self.children.len();
1294 self.children.retain(|&id| id != child);
1295 let removed = self.children.len() < before;
1296 if removed {
1297 self.cache.clear();
1298 self.mark_needs_measure();
1299 self.note_semantics_layout_change();
1300 }
1301 removed
1302 }
1303
1304 fn move_child(&mut self, from: usize, to: usize) {
1305 if from == to || from >= self.children.len() {
1306 return;
1307 }
1308 let child = self.children.remove(from);
1309 let target = to.min(self.children.len());
1310 self.children.insert(target, child);
1311 self.cache.clear();
1312 self.mark_needs_measure();
1313 self.note_semantics_layout_change();
1314 }
1315
1316 fn update_children(&mut self, children: &[NodeId]) {
1317 self.children.clear();
1318 self.children.extend_from_slice(children);
1319 self.cache.clear();
1320 self.mark_needs_measure();
1321 self.note_semantics_layout_change();
1322 }
1323
1324 fn collect_children_into(&self, out: &mut smallvec::SmallVec<[NodeId; 8]>) {
1325 out.clear();
1326 out.extend_from_slice(&self.children);
1327 }
1328
1329 fn owned_child_index(&self, child: NodeId) -> Option<usize> {
1330 self.children.iter().position(|&id| id == child)
1331 }
1332
1333 fn on_attached_to_parent(&mut self, parent: NodeId) {
1334 self.set_parent(parent);
1335 }
1336
1337 fn on_removed_from_parent(&mut self) {
1338 self.clear_parent();
1339 }
1340
1341 fn parent(&self) -> Option<NodeId> {
1342 self.parent.get()
1343 }
1344
1345 fn mark_needs_layout(&self) {
1346 self.needs_layout.set(true);
1347 }
1348
1349 fn needs_layout(&self) -> bool {
1350 self.needs_layout.get()
1351 }
1352
1353 fn mark_needs_measure(&self) {
1354 self.needs_measure.set(true);
1355 self.needs_layout.set(true);
1356 }
1357
1358 fn needs_measure(&self) -> bool {
1359 self.needs_measure.get()
1360 }
1361
1362 fn mark_descendant_needs_layout(&self, measure: bool) {
1363 self.descendant_dirt
1364 .set(self.descendant_dirt.get().marked(measure));
1365 if measure {
1366 self.cache.forget_intrinsics();
1367 }
1368 }
1369
1370 fn descendant_needs_layout(&self) -> bool {
1371 self.descendant_dirt.get().layout
1372 }
1373
1374 fn is_virtual(&self) -> bool {
1375 self.is_virtual
1376 }
1377
1378 fn descendant_needs_measure(&self) -> bool {
1379 self.descendant_dirt.get().measure
1380 }
1381
1382 fn mark_needs_semantics(&self) {
1383 self.needs_semantics.set(true);
1384 self.forget_semantics_reach();
1385 }
1386
1387 fn mark_descendant_needs_semantics(&self) {
1388 self.descendant_needs_semantics.set(true);
1389 }
1390
1391 fn needs_semantics(&self) -> bool {
1392 LayoutNode::needs_semantics(self)
1393 }
1394
1395 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1396 if self.parent.get().is_none() {
1397 self.parent.set(Some(parent));
1398 }
1399 }
1400
1401 fn recycle_key(&self) -> Option<TypeId> {
1402 Some(TypeId::of::<Self>())
1403 }
1404
1405 fn recycle_pool_limit(&self) -> Option<usize> {
1406 Some(RECYCLED_LAYOUT_NODE_POOL_LIMIT)
1407 }
1408
1409 fn prepare_for_recycle(&mut self) {
1410 *self = Self::new_recycled_shell(self.is_virtual);
1411 }
1412
1413 fn rehouse_for_recycle(&self) -> Option<Box<dyn cranpose_core::Node>> {
1414 Some(Box::new(Self::new_recycled_shell(self.is_virtual)))
1415 }
1416
1417 fn rehouse_for_live_compaction(&mut self) -> Option<Box<dyn cranpose_core::Node>> {
1418 let mut node = std::mem::replace(self, Self::new_recycled_shell(self.is_virtual));
1424 node.children.shrink_to_fit();
1425 Some(Box::new(node))
1426 }
1427}
1428
1429impl Drop for LayoutNode {
1430 fn drop(&mut self) {
1431 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
1432 unregister_layout_node(owner_context_id, id);
1433 }
1434 }
1435}
1436
1437const MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY: usize = 128;
1438const VIRTUAL_NODE_ID_START: NodeId = 0xC0000000;
1439
1440#[cfg(test)]
1441#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1442struct LayoutNodeRegistryDebugStats {
1443 len: usize,
1444 capacity: usize,
1445}
1446
1447struct LayoutNodeRegistryEntry {
1448 parent: Option<NodeId>,
1449 modifier_child_capabilities: NodeCapabilities,
1450 modifier_locals: Option<ModifierLocalsHandle>,
1451}
1452
1453pub(crate) struct LayoutNodeRegistryState {
1454 entries: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>>,
1455 virtual_node_id_counter: Cell<NodeId>,
1456}
1457
1458impl LayoutNodeRegistryState {
1459 pub(crate) fn new() -> Self {
1460 Self {
1461 entries: RefCell::new(HashMap::default()),
1462 virtual_node_id_counter: Cell::new(VIRTUAL_NODE_ID_START),
1463 }
1464 }
1465
1466 fn register(&self, id: NodeId, node: &LayoutNode) {
1467 self.entries.borrow_mut().insert(
1468 id,
1469 LayoutNodeRegistryEntry {
1470 parent: node.parent(),
1471 modifier_child_capabilities: node.modifier_child_capabilities(),
1472 modifier_locals: node.modifier_locals_handle(),
1473 },
1474 );
1475 }
1476
1477 fn unregister(&self, id: NodeId) {
1478 let mut entries = self.entries.borrow_mut();
1479 entries.remove(&id);
1480 let should_shrink = (entries.len() <= MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
1481 && entries.capacity() > MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1482 || entries.capacity()
1483 > entries
1484 .len()
1485 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1486 .saturating_mul(4);
1487 if should_shrink {
1488 let retained = entries
1489 .len()
1490 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY);
1491 let mut rebuilt = HashMap::default();
1492 rebuilt.reserve(retained);
1493 rebuilt.extend(entries.drain());
1494 *entries = rebuilt;
1495 }
1496 }
1497
1498 fn update_entry(
1499 &self,
1500 id: NodeId,
1501 parent: Option<NodeId>,
1502 modifier_child_capabilities: NodeCapabilities,
1503 modifier_locals: Option<ModifierLocalsHandle>,
1504 ) {
1505 if let Some(entry) = self.entries.borrow_mut().get_mut(&id) {
1506 entry.parent = parent;
1507 entry.modifier_child_capabilities = modifier_child_capabilities;
1508 entry.modifier_locals = modifier_locals;
1509 }
1510 }
1511
1512 #[cfg(test)]
1513 fn stats(&self) -> LayoutNodeRegistryDebugStats {
1514 let entries = self.entries.borrow();
1515 LayoutNodeRegistryDebugStats {
1516 len: entries.len(),
1517 capacity: entries.capacity(),
1518 }
1519 }
1520
1521 fn allocate_virtual_node_id(&self) -> NodeId {
1522 let id = self.virtual_node_id_counter.get();
1523 self.virtual_node_id_counter.set(id.wrapping_add(1));
1524 id
1525 }
1526
1527 fn resolve_modifier_local_from_parent_chain(
1528 &self,
1529 start: Option<NodeId>,
1530 token: &ModifierLocalToken,
1531 ) -> Option<ResolvedModifierLocal> {
1532 let mut current = start;
1533 while let Some(parent_id) = current {
1534 let (next_parent, resolved) = {
1535 let entries = self.entries.borrow();
1536 if let Some(entry) = entries.get(&parent_id) {
1537 let resolved = entry
1538 .modifier_child_capabilities
1539 .contains(NodeCapabilities::MODIFIER_LOCALS)
1540 .then_some(entry.modifier_locals.as_ref())
1541 .flatten()
1542 .and_then(|locals| locals.borrow().resolve(token))
1543 .map(|value| value.with_source(ModifierLocalSource::Ancestor));
1544 (entry.parent, resolved)
1545 } else {
1546 (None, None)
1547 }
1548 };
1549 if let Some(value) = resolved {
1550 return Some(value);
1551 }
1552 current = next_parent;
1553 }
1554 None
1555 }
1556}
1557
1558pub(crate) fn register_layout_node(
1559 id: NodeId,
1560 node: &LayoutNode,
1561) -> crate::render_state::AppContextId {
1562 crate::render_state::with_current_layout_node_registry(|registry| registry.register(id, node))
1563}
1564
1565pub(crate) fn unregister_layout_node(
1566 owner_context_id: crate::render_state::AppContextId,
1567 id: NodeId,
1568) {
1569 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1570 owner_context_id,
1571 |registry| {
1572 registry.unregister(id);
1573 },
1574 );
1575}
1576
1577#[cfg(test)]
1578fn layout_node_registry_stats() -> LayoutNodeRegistryDebugStats {
1579 crate::render_state::with_layout_node_registry(LayoutNodeRegistryState::stats)
1580}
1581
1582pub(crate) fn allocate_virtual_node_id() -> NodeId {
1583 crate::render_state::with_layout_node_registry(
1584 LayoutNodeRegistryState::allocate_virtual_node_id,
1585 )
1586}
1587
1588fn resolve_modifier_local_from_parent_chain(
1589 start: Option<NodeId>,
1590 token: &ModifierLocalToken,
1591) -> Option<ResolvedModifierLocal> {
1592 crate::render_state::with_layout_node_registry(|registry| {
1593 registry.resolve_modifier_local_from_parent_chain(start, token)
1594 })
1595}
1596
1597#[cfg(test)]
1598#[path = "tests/layout_node_tests.rs"]
1599mod tests;