1use std::{
2 any::TypeId,
3 cell::{Cell, RefCell},
4 collections::HashMap,
5 hash::{Hash, Hasher},
6 rc::Rc,
7};
8
9use cranpose_core::{Node, NodeId};
10use cranpose_foundation::{
11 InvalidationKind, ModifierInvalidation, NodeCapabilities, 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
68struct PlacementPass {
72 id: Cell<u64>,
73 unplaced: Cell<usize>,
74}
75
76thread_local! {
77 static PLACEMENT_PASS: PlacementPass = const {
78 PlacementPass {
79 id: Cell::new(1),
80 unplaced: Cell::new(0),
81 }
82 };
83}
84
85pub(crate) fn begin_placement_pass() {
87 PLACEMENT_PASS.with(|pass| {
88 pass.id.set(pass.id.get() + 1);
89 pass.unplaced.set(0);
90 });
91}
92
93pub(crate) fn placement_pass_with_unplaced_nodes() -> Option<u64> {
95 PLACEMENT_PASS.with(|pass| (pass.unplaced.get() > 0).then(|| pass.id.get()))
96}
97
98#[derive(Clone, Debug, Default)]
103pub struct LayoutState {
104 size: Size,
105 position: Point,
106 is_placed: bool,
107 node_id: Option<NodeId>,
108 cleared_in_pass: u64,
110 content_offset: Point,
111 semantics_stamp: u64,
112}
113
114impl LayoutState {
115 pub fn size(&self) -> Size {
116 self.size
117 }
118
119 pub fn position(&self) -> Point {
120 self.position
121 }
122
123 pub fn content_offset(&self) -> Point {
126 self.content_offset
127 }
128
129 pub fn set_content_offset(&mut self, offset: Point) {
132 if self.content_offset != offset {
133 self.content_offset = offset;
134 self.note_semantics_change();
135 }
136 }
137
138 pub fn at_origin(mut self) -> Self {
142 self.position = Point::default();
143 self
144 }
145
146 pub fn is_placed(&self) -> bool {
147 self.is_placed
148 }
149
150 pub(crate) fn set_node_id(&mut self, node_id: NodeId) {
151 self.node_id = Some(node_id);
152 }
153
154 pub fn set_size(&mut self, size: Size) {
157 if self.size != size {
158 if let Some(id) = self.node_id {
159 crate::render_state::record_geometry_scene_node(id);
160 }
161 self.size = size;
162 self.note_semantics_change();
163 }
164 }
165
166 pub fn place(&mut self, position: Point) {
170 let newly_placed = !self.is_placed && !self.restore_placement();
171 let moved = self.position != position;
172 if (newly_placed || moved)
173 && let Some(id) = self.node_id
174 {
175 crate::render_state::record_geometry_scene_node(id);
176 }
177 self.position = position;
178 if moved || !self.is_placed {
179 self.is_placed = true;
180 self.note_semantics_change();
181 }
182 }
183
184 fn restore_placement(&self) -> bool {
187 PLACEMENT_PASS.with(|pass| {
188 let restored = self.cleared_in_pass == pass.id.get();
189 if restored {
190 pass.unplaced.set(pass.unplaced.get().saturating_sub(1));
191 }
192 restored
193 })
194 }
195
196 pub fn clear_placed(&mut self) {
200 if !self.is_placed {
201 return;
202 }
203 self.is_placed = false;
204 self.cleared_in_pass = PLACEMENT_PASS.with(|pass| {
205 pass.unplaced.set(pass.unplaced.get() + 1);
206 pass.id.get()
207 });
208 self.note_semantics_change();
209 }
210
211 pub(crate) fn unplaced_in(&self, pass: u64) -> bool {
213 !self.is_placed && self.cleared_in_pass == pass
214 }
215
216 pub(crate) fn note_semantics_change(&mut self) {
217 if let Some(id) = self.node_id {
218 crate::semantics_layout_log::record_layout_change(id, &mut self.semantics_stamp);
219 }
220 }
221}
222
223#[derive(Clone)]
224struct MeasurementCacheEntry {
225 constraints: Constraints,
226 measured: Rc<MeasuredNode>,
227}
228
229#[derive(Clone, Copy, Debug)]
230pub enum IntrinsicKind {
231 MinWidth(f32),
232 MaxWidth(f32),
233 MinHeight(f32),
234 MaxHeight(f32),
235}
236
237impl IntrinsicKind {
238 fn discriminant(&self) -> u8 {
239 match self {
240 IntrinsicKind::MinWidth(_) => 0,
241 IntrinsicKind::MaxWidth(_) => 1,
242 IntrinsicKind::MinHeight(_) => 2,
243 IntrinsicKind::MaxHeight(_) => 3,
244 }
245 }
246
247 fn value_bits(&self) -> u32 {
248 match self {
249 IntrinsicKind::MinWidth(value)
250 | IntrinsicKind::MaxWidth(value)
251 | IntrinsicKind::MinHeight(value)
252 | IntrinsicKind::MaxHeight(value) => value.to_bits(),
253 }
254 }
255}
256
257impl PartialEq for IntrinsicKind {
258 fn eq(&self, other: &Self) -> bool {
259 self.discriminant() == other.discriminant() && self.value_bits() == other.value_bits()
260 }
261}
262
263impl Eq for IntrinsicKind {}
264
265impl Hash for IntrinsicKind {
266 fn hash<H: Hasher>(&self, state: &mut H) {
267 self.discriminant().hash(state);
268 self.value_bits().hash(state);
269 }
270}
271
272#[derive(Default)]
273struct NodeCacheState {
274 epoch: u64,
275 measurement: Option<MeasurementCacheEntry>,
276 intrinsics: Vec<(IntrinsicKind, f32)>,
277}
278
279#[derive(Default)]
280pub(crate) struct LayoutNodeCacheHandles {
281 state: Rc<RefCell<NodeCacheState>>,
282}
283
284impl Clone for LayoutNodeCacheHandles {
285 fn clone(&self) -> Self {
286 Self {
287 state: Rc::clone(&self.state),
288 }
289 }
290
291 fn clone_from(&mut self, source: &Self) {
292 if !Rc::ptr_eq(&self.state, &source.state) {
293 self.state = Rc::clone(&source.state);
294 }
295 }
296}
297
298impl LayoutNodeCacheHandles {
299 pub(crate) fn clear(&self) {
300 let mut state = self.state.borrow_mut();
301 state.measurement = None;
302 state.intrinsics.clear();
303 state.epoch = 0;
304 }
305
306 pub(crate) fn activate(&self, epoch: u64) {
307 let mut state = self.state.borrow_mut();
308 if state.epoch != epoch {
309 state.measurement = None;
310 state.intrinsics.clear();
311 state.epoch = epoch;
312 }
313 }
314
315 pub(crate) fn epoch(&self) -> u64 {
316 self.state.borrow().epoch
317 }
318
319 pub(crate) fn get_measurement(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
320 let state = self.state.borrow();
321 state
322 .measurement
323 .as_ref()
324 .filter(|entry| entry.constraints == constraints)
325 .map(|entry| Rc::clone(&entry.measured))
326 }
327
328 pub(crate) fn store_measurement(&self, constraints: Constraints, measured: Rc<MeasuredNode>) {
329 self.state.borrow_mut().measurement = Some(MeasurementCacheEntry {
330 constraints,
331 measured,
332 });
333 }
334
335 pub(crate) fn get_intrinsic(&self, kind: &IntrinsicKind) -> Option<f32> {
336 let state = self.state.borrow();
337 state
338 .intrinsics
339 .iter()
340 .find(|(stored_kind, _)| stored_kind == kind)
341 .map(|(_, value)| *value)
342 }
343
344 pub(crate) fn store_intrinsic(&self, kind: IntrinsicKind, value: f32) {
345 let mut state = self.state.borrow_mut();
346 if let Some((_, existing)) = state
347 .intrinsics
348 .iter_mut()
349 .find(|(stored_kind, _)| stored_kind == &kind)
350 {
351 *existing = value;
352 } else {
353 state.intrinsics.push((kind, value));
354 }
355 }
356}
357
358pub struct LayoutNode {
359 #[cfg(feature = "inspection")]
361 pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
362 pub modifier: Modifier,
363 modifier_chain: ModifierChainHandle,
364 pub measure_policy: Rc<dyn MeasurePolicy>,
365 density: crate::density::Density,
366 pub children: Vec<NodeId>,
368 cache: LayoutNodeCacheHandles,
369 needs_measure: Cell<bool>,
370 needs_layout: Cell<bool>,
371 needs_semantics: Cell<bool>,
372 descendant_needs_semantics: Cell<bool>,
376 semantics_reach: Cell<Option<cranpose_foundation::SemanticsReach>>,
380 needs_redraw: Cell<bool>,
381 needs_pointer_pass: Cell<bool>,
382 needs_focus_sync: Cell<bool>,
383 parent: Cell<Option<NodeId>>,
384 folded_parent: Cell<Option<NodeId>>,
385 id: Cell<Option<NodeId>>,
386 owner_context_id: Cell<Option<crate::render_state::AppContextId>>,
387 debug_modifiers: Cell<bool>,
388 is_virtual: bool,
389 virtual_children_count: Cell<usize>,
390
391 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
392 modifier_slices_dirty: Cell<bool>,
393
394 layout_state: Rc<RefCell<LayoutState>>,
395 layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
396 coordinator_geometry: Rc<crate::modifier::CoordinatorGeometry>,
399}
400
401pub(crate) const RECYCLED_LAYOUT_NODE_POOL_LIMIT: usize = 128;
402
403thread_local! {
404 static EMPTY_MEASURE_POLICY: Rc<dyn MeasurePolicy> =
405 Rc::new(crate::layout::policies::EmptyMeasurePolicy);
406}
407
408fn empty_measure_policy() -> Rc<dyn MeasurePolicy> {
409 EMPTY_MEASURE_POLICY.with(Rc::clone)
410}
411
412impl LayoutNode {
413 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
414 Self::new_with_virtual(modifier, measure_policy, false)
415 }
416
417 pub fn new_virtual() -> Self {
420 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), true)
421 }
422
423 fn new_recycled_shell(is_virtual: bool) -> Self {
424 let mut shell =
425 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), is_virtual);
426 shell.needs_measure.set(false);
427 shell.needs_layout.set(false);
428 shell.needs_semantics.set(false);
429 shell.descendant_needs_semantics.set(false);
430 shell.needs_redraw.set(false);
431 shell.needs_pointer_pass.set(false);
432 shell.needs_focus_sync.set(false);
433 shell.parent.set(None);
434 shell.folded_parent.set(None);
435 shell.id.set(None);
436 shell.owner_context_id.set(None);
437 shell.debug_modifiers.set(false);
438 shell.virtual_children_count.set(0);
439 shell.cache = LayoutNodeCacheHandles::default();
440 shell.modifier_slices_snapshot = RefCell::new(Rc::default());
441 shell.modifier_slices_dirty = Cell::new(true);
442 shell.layout_state = Rc::new(RefCell::new(LayoutState::default()));
443 shell.layout_runtime_state = Rc::new(RefCell::new(LayoutRuntimeState::new(Rc::clone(
444 &shell.measure_policy,
445 ))));
446 shell.coordinator_geometry = Rc::default();
447 shell
448 }
449
450 fn new_with_virtual(
451 modifier: Modifier,
452 measure_policy: Rc<dyn MeasurePolicy>,
453 is_virtual: bool,
454 ) -> Self {
455 let layout_runtime_state = Rc::new(RefCell::new(LayoutRuntimeState::new(Rc::clone(
456 &measure_policy,
457 ))));
458 let mut node = Self {
459 #[cfg(feature = "inspection")]
460 source_trace: cranpose_core::source_trace::current_source_trace(),
461 modifier,
462 modifier_chain: ModifierChainHandle::new(),
463 measure_policy,
464 density: crate::density::Density::default(),
465 children: Vec::new(),
466 cache: LayoutNodeCacheHandles::default(),
467 needs_measure: Cell::new(true),
468 needs_layout: Cell::new(true),
469 needs_semantics: Cell::new(true),
470 descendant_needs_semantics: Cell::new(false),
471 semantics_reach: Cell::new(None),
472 needs_redraw: Cell::new(true),
473 needs_pointer_pass: Cell::new(false),
474 needs_focus_sync: Cell::new(false),
475 parent: Cell::new(None),
476 folded_parent: Cell::new(None),
477 id: Cell::new(None),
478 owner_context_id: Cell::new(None),
479 debug_modifiers: Cell::new(false),
480 is_virtual,
481 virtual_children_count: Cell::new(0),
482 modifier_slices_snapshot: RefCell::new(Rc::default()),
483 modifier_slices_dirty: Cell::new(true),
484 layout_state: Rc::new(RefCell::new(LayoutState::default())),
485 layout_runtime_state,
486 coordinator_geometry: Rc::default(),
487 };
488 node.sync_modifier_chain();
489 node
490 }
491
492 pub fn set_modifier(&mut self, modifier: Modifier) {
493 #[cfg(feature = "inspection")]
494 {
495 let trace = cranpose_core::source_trace::current_source_trace();
496 if !trace.is_empty() {
497 self.source_trace = trace;
498 }
499 }
500 if self.modifier == modifier
504 && !self
505 .modifier_capabilities()
506 .contains(NodeCapabilities::MODIFIER_LOCALS)
507 {
508 return;
509 }
510 let modifier_changed = !self.modifier.structural_eq(&modifier);
511 self.modifier = modifier;
512 self.sync_modifier_chain();
513 if modifier_changed {
514 self.cache.clear();
515 self.request_semantics_update();
516 }
517 }
518
519 fn sync_modifier_chain(&mut self) {
520 let prev_caps = self.modifier_capabilities();
521 let start_parent = self.parent();
522 let mut resolver = move |token: &ModifierLocalToken| {
523 resolve_modifier_local_from_parent_chain(start_parent, token)
524 };
525 self.modifier_chain
526 .set_debug_logging(self.debug_modifiers.get());
527 self.modifier_chain.set_node_id(self.id.get());
528 let modifier_local_invalidations = self
529 .modifier_chain
530 .update_with_resolver(&self.modifier, &mut resolver);
531 if prev_caps.contains(NodeCapabilities::WINDOW_ROOT)
532 != self
533 .modifier_capabilities()
534 .contains(NodeCapabilities::WINDOW_ROOT)
535 {
536 self.note_semantics_layout_change();
537 }
538 self.forget_semantics_reach();
539 self.modifier_slices_dirty.set(true);
540
541 let mut invalidations = self.modifier_chain.take_invalidations();
542 invalidations.extend(modifier_local_invalidations);
543 self.dispatch_modifier_invalidations_with_prev(&invalidations, prev_caps);
544 self.refresh_registry_state();
545 }
546
547 fn update_modifier_slices_cache(&self) {
548 let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
549 crate::modifier::collect_modifier_slices_into_shared(
550 self.modifier_chain.chain(),
551 &mut snapshot,
552 &self.coordinator_geometry,
553 self.density.density(),
554 );
555 self.modifier_slices_dirty.set(false);
556 }
557
558 #[cfg(test)]
560 pub(crate) fn modifier_slices_ready(&self) -> bool {
561 !self.modifier_slices_dirty.get()
562 }
563
564 pub(crate) fn mark_modifier_slices_dirty(&self) {
565 self.modifier_slices_dirty.set(true);
566 }
567
568 #[cfg(test)]
569 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
570 self.dispatch_modifier_invalidations_with_prev(invalidations, NodeCapabilities::empty());
571 }
572
573 fn dispatch_modifier_invalidations_with_prev(
574 &self,
575 invalidations: &[ModifierInvalidation],
576 prev_caps: NodeCapabilities,
577 ) {
578 let curr_caps = self.modifier_capabilities();
579 for invalidation in invalidations {
580 self.modifier_slices_dirty.set(true);
581 let has_capability =
582 |capability| curr_caps.contains(capability) || prev_caps.contains(capability);
583 match invalidation.kind() {
584 InvalidationKind::Layout => {
585 if has_capability(NodeCapabilities::LAYOUT) {
586 self.mark_needs_measure();
587 if let Some(id) = self.id.get() {
588 log_layout_invalidation_dispatch(
589 id,
590 invalidation,
591 curr_caps,
592 prev_caps,
593 &self.modifier,
594 );
595 let inside_composition =
596 cranpose_core::composer_context::try_with_composer(|_| ())
597 .is_some();
598 if inside_composition {
599 cranpose_core::bubble_measure_dirty_in_composer(id);
600 } else {
601 crate::schedule_layout_repass(id);
602 }
603 }
604 }
605 }
606 InvalidationKind::Draw => {
607 if has_capability(NodeCapabilities::DRAW)
608 || invalidation.capabilities().contains(NodeCapabilities::DRAW)
609 {
610 self.mark_needs_redraw();
611 }
612 }
613 InvalidationKind::PointerInput => {
614 if has_capability(NodeCapabilities::POINTER_INPUT) {
615 self.mark_needs_pointer_pass();
616 crate::request_pointer_invalidation();
617 if let Some(id) = self.id.get() {
618 crate::schedule_pointer_repass(id);
619 }
620 }
621 }
622 InvalidationKind::Semantics => {
623 self.request_semantics_update();
624 }
625 InvalidationKind::Focus => {
626 if has_capability(NodeCapabilities::FOCUS) {
627 self.mark_needs_focus_sync();
628 crate::request_focus_invalidation();
629 if let Some(id) = self.id.get() {
630 crate::schedule_focus_invalidation(id);
631 }
632 }
633 }
634 }
635 }
636 }
637
638 pub fn density(&self) -> crate::density::Density {
640 self.density
641 }
642
643 pub fn set_density(&mut self, density: crate::density::Density) {
645 if self.density != density {
646 self.density = density;
647 self.cache.clear();
648 self.modifier_slices_dirty.set(true);
649 self.mark_needs_measure();
650 }
651 }
652
653 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
654 if !Rc::ptr_eq(&self.measure_policy, &policy) {
655 self.measure_policy = policy;
656 self.cache.clear();
657 self.mark_needs_measure();
658 if let Some(id) = self.id.get() {
659 cranpose_core::bubble_measure_dirty_in_composer(id);
660 }
661 }
662 }
663
664 pub fn mark_needs_measure(&self) {
666 self.needs_measure.set(true);
667 self.needs_layout.set(true);
668 }
669
670 pub fn mark_needs_layout(&self) {
672 self.needs_layout.set(true);
673 }
674
675 pub fn mark_needs_redraw(&self) {
677 self.needs_redraw.set(true);
678 if let Some(id) = self.id.get() {
679 crate::schedule_draw_repass(id);
680 }
681 crate::request_render_invalidation();
682 }
683
684 pub fn needs_measure(&self) -> bool {
686 self.needs_measure.get()
687 }
688
689 pub fn needs_layout(&self) -> bool {
691 self.needs_layout.get()
692 }
693
694 pub fn mark_needs_semantics(&self) {
696 self.needs_semantics.set(true);
697 self.forget_semantics_reach();
698 }
699
700 pub(crate) fn clear_needs_semantics(&self) {
701 self.needs_semantics.set(false);
702 self.descendant_needs_semantics.set(false);
703 }
704
705 pub fn needs_semantics(&self) -> bool {
708 self.needs_semantics.get() || self.descendant_needs_semantics.get()
709 }
710
711 pub(crate) fn semantics_changed(&self) -> bool {
714 self.needs_semantics.get()
715 }
716
717 pub fn needs_redraw(&self) -> bool {
719 self.needs_redraw.get()
720 }
721
722 pub fn clear_needs_redraw(&self) {
723 self.needs_redraw.set(false);
724 }
725
726 fn request_semantics_update(&self) {
727 let already_dirty = self.needs_semantics.replace(true);
728 if already_dirty {
729 return;
730 }
731
732 if let Some(id) = self.id.get() {
733 cranpose_core::queue_semantics_invalidation(id);
734 }
735 }
736
737 pub(crate) fn clear_needs_measure(&self) {
738 self.needs_measure.set(false);
739 }
740
741 pub(crate) fn clear_needs_layout(&self) {
742 self.needs_layout.set(false);
743 }
744
745 pub fn mark_needs_pointer_pass(&self) {
747 self.needs_pointer_pass.set(true);
748 }
749
750 pub fn needs_pointer_pass(&self) -> bool {
752 self.needs_pointer_pass.get()
753 }
754
755 pub fn clear_needs_pointer_pass(&self) {
757 self.needs_pointer_pass.set(false);
758 }
759
760 pub fn mark_needs_focus_sync(&self) {
762 self.needs_focus_sync.set(true);
763 }
764
765 pub fn needs_focus_sync(&self) -> bool {
767 self.needs_focus_sync.get()
768 }
769
770 pub fn clear_needs_focus_sync(&self) {
772 self.needs_focus_sync.set(false);
773 }
774
775 pub fn set_node_id(&mut self, id: NodeId) {
777 if let Some(existing) = self.id.replace(Some(id))
778 && let Some(owner_context_id) = self.owner_context_id.take()
779 {
780 unregister_layout_node(owner_context_id, existing);
781 }
782 self.layout_state.borrow_mut().set_node_id(id);
783 let owner_context_id = register_layout_node(id, self);
784 self.owner_context_id.set(Some(owner_context_id));
785 self.refresh_registry_state();
786
787 self.modifier_chain.set_node_id(Some(id));
788 let invalidations = self.modifier_chain.take_invalidations();
789 self.dispatch_modifier_invalidations_with_prev(&invalidations, NodeCapabilities::empty());
790 self.modifier_slices_dirty.set(true);
792 }
793
794 pub fn node_id(&self) -> Option<NodeId> {
796 self.id.get()
797 }
798
799 pub fn set_parent(&self, parent: NodeId) {
802 self.folded_parent.set(Some(parent));
803 self.parent.set(Some(parent));
804 self.refresh_registry_state();
805 }
806
807 pub fn clear_parent(&self) {
809 self.folded_parent.set(None);
810 self.parent.set(None);
811 self.refresh_registry_state();
812 }
813
814 pub fn parent(&self) -> Option<NodeId> {
816 self.parent.get()
817 }
818
819 pub fn folded_parent(&self) -> Option<NodeId> {
821 self.folded_parent.get()
822 }
823
824 pub fn is_virtual(&self) -> bool {
826 self.is_virtual
827 }
828
829 pub(crate) fn cache_handles(&self) -> &LayoutNodeCacheHandles {
830 &self.cache
831 }
832
833 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
834 self.modifier_chain.resolved_modifiers()
835 }
836
837 pub fn modifier_capabilities(&self) -> NodeCapabilities {
838 self.modifier_chain.capabilities()
839 }
840
841 pub fn is_window_root(&self) -> bool {
845 self.modifier_capabilities()
846 .contains(NodeCapabilities::WINDOW_ROOT)
847 }
848
849 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
850 self.modifier_chain.aggregate_child_capabilities()
851 }
852
853 pub fn set_debug_modifiers(&mut self, enabled: bool) {
854 self.debug_modifiers.set(enabled);
855 self.modifier_chain.set_debug_logging(enabled);
856 }
857
858 pub fn debug_modifiers_enabled(&self) -> bool {
859 self.debug_modifiers.get()
860 }
861
862 pub fn modifier_locals_handle(&self) -> Option<ModifierLocalsHandle> {
865 self.modifier_chain.modifier_locals_handle()
866 }
867
868 pub fn has_layout_modifier_nodes(&self) -> bool {
869 self.modifier_capabilities()
870 .contains(NodeCapabilities::LAYOUT)
871 }
872
873 pub fn has_draw_modifier_nodes(&self) -> bool {
874 self.modifier_capabilities()
875 .contains(NodeCapabilities::DRAW)
876 }
877
878 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
879 self.modifier_capabilities()
880 .contains(NodeCapabilities::POINTER_INPUT)
881 }
882
883 pub fn has_semantics_modifier_nodes(&self) -> bool {
884 self.modifier_capabilities()
885 .contains(NodeCapabilities::SEMANTICS)
886 }
887
888 pub fn has_focus_modifier_nodes(&self) -> bool {
889 self.modifier_capabilities()
890 .contains(NodeCapabilities::FOCUS)
891 }
892
893 fn refresh_registry_state(&self) {
894 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
895 let parent = self.parent();
896 let capabilities = self.modifier_child_capabilities();
897 let modifier_locals = self.modifier_locals_handle();
898 let _ = crate::render_state::with_layout_node_registry_by_app_context(
899 owner_context_id,
900 |registry| {
901 registry.update_entry(id, parent, capabilities, modifier_locals);
902 },
903 );
904 }
905 }
906
907 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
908 if self.modifier_slices_dirty.get() {
909 self.update_modifier_slices_cache();
910 }
911 self.modifier_slices_snapshot.borrow().clone()
912 }
913
914 pub fn layout_state(&self) -> LayoutState {
916 self.layout_state.borrow().clone()
917 }
918
919 pub fn measured_size(&self) -> Size {
921 self.layout_state.borrow().size
922 }
923
924 pub fn position(&self) -> Point {
926 self.layout_state.borrow().position
927 }
928
929 pub fn is_placed(&self) -> bool {
931 self.layout_state.borrow().is_placed
932 }
933
934 pub fn set_measured_size(&self, size: Size) {
938 self.layout_state.borrow_mut().set_size(size);
939 }
940
941 pub fn set_position(&self, position: Point) {
944 self.layout_state.borrow_mut().place(position);
945 }
946
947 pub fn set_content_offset(&self, offset: Point) {
949 self.layout_state.borrow_mut().set_content_offset(offset);
950 }
951
952 pub fn clear_placed(&self) {
954 self.layout_state.borrow_mut().clear_placed();
955 }
956
957 fn note_semantics_layout_change(&self) {
958 if let Ok(mut state) = self.layout_state.try_borrow_mut() {
959 state.note_semantics_change();
960 }
961 }
962
963 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
964 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
965 }
966
967 fn forget_semantics_reach(&self) {
969 self.semantics_reach.set(None);
970 crate::modal_nodes::reach_changed(self.id.get());
971 }
972
973 pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
975 if !self
977 .modifier_capabilities()
978 .contains(NodeCapabilities::SEMANTICS)
979 {
980 return cranpose_foundation::SemanticsReach::default();
981 }
982 if let Some(reach) = self.semantics_reach.get() {
983 return reach;
984 }
985 let reach = crate::modifier::semantics_reach_of_chain(self.modifier_chain.chain());
986 self.semantics_reach.set(Some(reach));
987 reach
988 }
989
990 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
991 &self.modifier_chain
992 }
993
994 pub fn with_text_field_modifier_mut<R>(
999 &mut self,
1000 f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
1001 ) -> Option<R> {
1002 self.modifier_chain.with_text_field_modifier_mut(f)
1003 }
1004
1005 pub fn layout_state_handle(&self) -> &Rc<RefCell<LayoutState>> {
1008 &self.layout_state
1009 }
1010
1011 pub(crate) fn coordinator_geometry(&self) -> &crate::modifier::CoordinatorGeometry {
1012 &self.coordinator_geometry
1013 }
1014
1015 pub(crate) fn layout_runtime_state_handle(&self) -> Rc<RefCell<LayoutRuntimeState>> {
1016 self.layout_runtime_state.clone()
1017 }
1018
1019 #[cfg(test)]
1020 pub(crate) fn layout_runtime_debug_stats(&self) -> LayoutRuntimeDebugStats {
1021 self.layout_runtime_state.borrow().debug_stats()
1022 }
1023}
1024impl Clone for LayoutNode {
1025 fn clone(&self) -> Self {
1026 let mut node = Self {
1027 #[cfg(feature = "inspection")]
1028 source_trace: self.source_trace.clone(),
1029 modifier: self.modifier.clone(),
1030 modifier_chain: ModifierChainHandle::new(),
1031 measure_policy: self.measure_policy.clone(),
1032 density: self.density,
1033 children: self.children.clone(),
1034 cache: self.cache.clone(),
1035 needs_measure: Cell::new(self.needs_measure.get()),
1036 needs_layout: Cell::new(self.needs_layout.get()),
1037 needs_semantics: Cell::new(self.needs_semantics.get()),
1038 descendant_needs_semantics: Cell::new(self.descendant_needs_semantics.get()),
1039 semantics_reach: Cell::new(None),
1040 needs_redraw: Cell::new(self.needs_redraw.get()),
1041 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
1042 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
1043 parent: Cell::new(self.parent.get()),
1044 folded_parent: Cell::new(self.folded_parent.get()),
1045 id: Cell::new(None),
1046 owner_context_id: Cell::new(None),
1047 debug_modifiers: Cell::new(self.debug_modifiers.get()),
1048 is_virtual: self.is_virtual,
1049 virtual_children_count: Cell::new(self.virtual_children_count.get()),
1050 modifier_slices_snapshot: RefCell::new(Rc::default()),
1051 modifier_slices_dirty: Cell::new(true),
1052 layout_state: self.layout_state.clone(),
1053 layout_runtime_state: self.layout_runtime_state.clone(),
1054 coordinator_geometry: Rc::clone(&self.coordinator_geometry),
1055 };
1056 node.sync_modifier_chain();
1057 node
1058 }
1059}
1060
1061impl Node for LayoutNode {
1062 fn mount(&mut self) {
1063 let (chain, mut context) = self.modifier_chain.chain_and_context_mut();
1064 chain.repair_chain();
1065 chain.attach_nodes(&mut *context);
1066 crate::modal_nodes::reach_changed(self.id.get());
1067 }
1068
1069 fn unmount(&mut self) {
1070 self.modifier_chain.chain_mut().detach_nodes();
1071 }
1072
1073 fn set_node_id(&mut self, id: NodeId) {
1074 LayoutNode::set_node_id(self, id);
1075 }
1076
1077 fn insert_child(&mut self, child: NodeId) -> bool {
1078 if self.children.contains(&child) {
1079 return false;
1080 }
1081 if is_virtual_node(child) {
1082 let count = self.virtual_children_count.get();
1083 self.virtual_children_count.set(count + 1);
1084 }
1085 self.children.push(child);
1086 self.cache.clear();
1087 self.mark_needs_measure();
1088 self.note_semantics_layout_change();
1089 true
1090 }
1091
1092 fn remove_child(&mut self, child: NodeId) -> bool {
1093 let before = self.children.len();
1094 self.children.retain(|&id| id != child);
1095 let removed = self.children.len() < before;
1096 if removed {
1097 if is_virtual_node(child) {
1098 let count = self.virtual_children_count.get();
1099 if count > 0 {
1100 self.virtual_children_count.set(count - 1);
1101 }
1102 }
1103 self.cache.clear();
1104 self.mark_needs_measure();
1105 self.note_semantics_layout_change();
1106 }
1107 removed
1108 }
1109
1110 fn move_child(&mut self, from: usize, to: usize) {
1111 if from == to || from >= self.children.len() {
1112 return;
1113 }
1114 let child = self.children.remove(from);
1115 let target = to.min(self.children.len());
1116 self.children.insert(target, child);
1117 self.cache.clear();
1118 self.mark_needs_measure();
1119 self.note_semantics_layout_change();
1120 }
1121
1122 fn update_children(&mut self, children: &[NodeId]) {
1123 self.children.clear();
1124 self.children.extend_from_slice(children);
1125 self.cache.clear();
1126 self.mark_needs_measure();
1127 self.note_semantics_layout_change();
1128 }
1129
1130 fn collect_children_into(&self, out: &mut smallvec::SmallVec<[NodeId; 8]>) {
1131 out.clear();
1132 out.extend_from_slice(&self.children);
1133 }
1134
1135 fn on_attached_to_parent(&mut self, parent: NodeId) {
1136 self.set_parent(parent);
1137 }
1138
1139 fn on_removed_from_parent(&mut self) {
1140 self.clear_parent();
1141 }
1142
1143 fn parent(&self) -> Option<NodeId> {
1144 self.parent.get()
1145 }
1146
1147 fn mark_needs_layout(&self) {
1148 self.needs_layout.set(true);
1149 }
1150
1151 fn needs_layout(&self) -> bool {
1152 self.needs_layout.get()
1153 }
1154
1155 fn mark_needs_measure(&self) {
1156 self.needs_measure.set(true);
1157 self.needs_layout.set(true);
1158 }
1159
1160 fn needs_measure(&self) -> bool {
1161 self.needs_measure.get()
1162 }
1163
1164 fn mark_needs_semantics(&self) {
1165 self.needs_semantics.set(true);
1166 self.forget_semantics_reach();
1167 }
1168
1169 fn mark_descendant_needs_semantics(&self) {
1170 self.descendant_needs_semantics.set(true);
1171 }
1172
1173 fn needs_semantics(&self) -> bool {
1174 LayoutNode::needs_semantics(self)
1175 }
1176
1177 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1178 if self.parent.get().is_none() {
1179 self.parent.set(Some(parent));
1180 }
1181 }
1182
1183 fn recycle_key(&self) -> Option<TypeId> {
1184 Some(TypeId::of::<Self>())
1185 }
1186
1187 fn recycle_pool_limit(&self) -> Option<usize> {
1188 Some(RECYCLED_LAYOUT_NODE_POOL_LIMIT)
1189 }
1190
1191 fn prepare_for_recycle(&mut self) {
1192 *self = Self::new_recycled_shell(self.is_virtual);
1193 }
1194
1195 fn rehouse_for_recycle(&self) -> Option<Box<dyn cranpose_core::Node>> {
1196 Some(Box::new(Self::new_recycled_shell(self.is_virtual)))
1197 }
1198
1199 fn rehouse_for_live_compaction(&mut self) -> Option<Box<dyn cranpose_core::Node>> {
1200 let mut previous = std::mem::replace(self, Self::new_recycled_shell(self.is_virtual));
1201 let node_id = previous.id.replace(None);
1202 let parent = previous.parent.get();
1203 let folded_parent = previous.folded_parent.get();
1204 let debug_modifiers = previous.debug_modifiers.get();
1205 let needs_measure = previous.needs_measure.get();
1206 let needs_layout = previous.needs_layout.get();
1207 let needs_semantics = previous.needs_semantics.get();
1208 let descendant_needs_semantics = previous.descendant_needs_semantics.get();
1209 let needs_redraw = previous.needs_redraw.get();
1210 let needs_pointer_pass = previous.needs_pointer_pass.get();
1211 let needs_focus_sync = previous.needs_focus_sync.get();
1212 let virtual_children_count = previous.virtual_children_count.get();
1213 let children = previous.children.to_vec();
1214 let modifier = previous.modifier.rehouse_for_live_compaction();
1215 let measure_policy = previous.measure_policy.clone();
1216 let layout_state = previous.layout_state.clone();
1217 let layout_runtime_state = previous.layout_runtime_state.clone();
1218 let coordinator_geometry = Rc::clone(&previous.coordinator_geometry);
1219
1220 previous.modifier_chain.chain_mut().detach_nodes();
1221
1222 let mut compact = Self::new_with_virtual(modifier, measure_policy, previous.is_virtual);
1223 compact.children = children;
1224 #[cfg(feature = "inspection")]
1225 {
1226 compact.source_trace = previous.source_trace.clone();
1227 }
1228 compact.parent.set(parent);
1229 compact.folded_parent.set(folded_parent);
1230 compact.id.set(node_id);
1231 compact.debug_modifiers.set(debug_modifiers);
1232 compact.needs_measure.set(needs_measure);
1233 compact.needs_layout.set(needs_layout);
1234 compact.needs_semantics.set(needs_semantics);
1235 compact
1236 .descendant_needs_semantics
1237 .set(descendant_needs_semantics);
1238 compact.needs_redraw.set(needs_redraw);
1239 compact.needs_pointer_pass.set(needs_pointer_pass);
1240 compact.needs_focus_sync.set(needs_focus_sync);
1241 compact.virtual_children_count.set(virtual_children_count);
1242 compact.layout_state = layout_state;
1243 compact.layout_runtime_state = layout_runtime_state;
1244 compact.coordinator_geometry = coordinator_geometry;
1245 compact.sync_modifier_chain();
1246 if let Some(id) = node_id {
1247 let owner_context_id = register_layout_node(id, &compact);
1248 compact.owner_context_id.set(Some(owner_context_id));
1249 }
1250
1251 Some(Box::new(compact))
1252 }
1253}
1254
1255impl Drop for LayoutNode {
1256 fn drop(&mut self) {
1257 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
1258 unregister_layout_node(owner_context_id, id);
1259 }
1260 }
1261}
1262
1263const MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY: usize = 128;
1264const VIRTUAL_NODE_ID_START: NodeId = 0xC0000000;
1265
1266#[cfg(test)]
1267#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1268struct LayoutNodeRegistryDebugStats {
1269 len: usize,
1270 capacity: usize,
1271}
1272
1273struct LayoutNodeRegistryEntry {
1274 parent: Option<NodeId>,
1275 modifier_child_capabilities: NodeCapabilities,
1276 modifier_locals: Option<ModifierLocalsHandle>,
1277 is_virtual: bool,
1278}
1279
1280pub(crate) struct LayoutNodeRegistryState {
1281 entries: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>>,
1282 virtual_node_id_counter: Cell<NodeId>,
1283}
1284
1285impl LayoutNodeRegistryState {
1286 pub(crate) fn new() -> Self {
1287 Self {
1288 entries: RefCell::new(HashMap::new()),
1289 virtual_node_id_counter: Cell::new(VIRTUAL_NODE_ID_START),
1290 }
1291 }
1292
1293 fn register(&self, id: NodeId, node: &LayoutNode) {
1294 self.entries.borrow_mut().insert(
1295 id,
1296 LayoutNodeRegistryEntry {
1297 parent: node.parent(),
1298 modifier_child_capabilities: node.modifier_child_capabilities(),
1299 modifier_locals: node.modifier_locals_handle(),
1300 is_virtual: node.is_virtual(),
1301 },
1302 );
1303 }
1304
1305 fn unregister(&self, id: NodeId) {
1306 let mut entries = self.entries.borrow_mut();
1307 entries.remove(&id);
1308 let should_shrink = (entries.len() <= MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
1309 && entries.capacity() > MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1310 || entries.capacity()
1311 > entries
1312 .len()
1313 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1314 .saturating_mul(4);
1315 if should_shrink {
1316 let retained = entries
1317 .len()
1318 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY);
1319 let mut rebuilt = HashMap::new();
1320 rebuilt.reserve(retained);
1321 rebuilt.extend(entries.drain());
1322 *entries = rebuilt;
1323 }
1324 }
1325
1326 fn update_entry(
1327 &self,
1328 id: NodeId,
1329 parent: Option<NodeId>,
1330 modifier_child_capabilities: NodeCapabilities,
1331 modifier_locals: Option<ModifierLocalsHandle>,
1332 ) {
1333 if let Some(entry) = self.entries.borrow_mut().get_mut(&id) {
1334 entry.parent = parent;
1335 entry.modifier_child_capabilities = modifier_child_capabilities;
1336 entry.modifier_locals = modifier_locals;
1337 }
1338 }
1339
1340 #[cfg(test)]
1341 fn stats(&self) -> LayoutNodeRegistryDebugStats {
1342 let entries = self.entries.borrow();
1343 LayoutNodeRegistryDebugStats {
1344 len: entries.len(),
1345 capacity: entries.capacity(),
1346 }
1347 }
1348
1349 fn is_virtual_node(&self, id: NodeId) -> bool {
1350 self.entries
1351 .borrow()
1352 .get(&id)
1353 .is_some_and(|entry| entry.is_virtual)
1354 }
1355
1356 fn allocate_virtual_node_id(&self) -> NodeId {
1357 let id = self.virtual_node_id_counter.get();
1358 self.virtual_node_id_counter.set(id.wrapping_add(1));
1359 id
1360 }
1361
1362 fn resolve_modifier_local_from_parent_chain(
1363 &self,
1364 start: Option<NodeId>,
1365 token: &ModifierLocalToken,
1366 ) -> Option<ResolvedModifierLocal> {
1367 let mut current = start;
1368 while let Some(parent_id) = current {
1369 let (next_parent, resolved) = {
1370 let entries = self.entries.borrow();
1371 if let Some(entry) = entries.get(&parent_id) {
1372 let resolved = entry
1373 .modifier_child_capabilities
1374 .contains(NodeCapabilities::MODIFIER_LOCALS)
1375 .then_some(entry.modifier_locals.as_ref())
1376 .flatten()
1377 .and_then(|locals| locals.borrow().resolve(token))
1378 .map(|value| value.with_source(ModifierLocalSource::Ancestor));
1379 (entry.parent, resolved)
1380 } else {
1381 (None, None)
1382 }
1383 };
1384 if let Some(value) = resolved {
1385 return Some(value);
1386 }
1387 current = next_parent;
1388 }
1389 None
1390 }
1391}
1392
1393pub(crate) fn register_layout_node(
1394 id: NodeId,
1395 node: &LayoutNode,
1396) -> crate::render_state::AppContextId {
1397 let owner_context_id = crate::render_state::current_app_context_id();
1398 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1399 owner_context_id,
1400 |registry| {
1401 registry.register(id, node);
1402 },
1403 );
1404 owner_context_id
1405}
1406
1407pub(crate) fn unregister_layout_node(
1408 owner_context_id: crate::render_state::AppContextId,
1409 id: NodeId,
1410) {
1411 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1412 owner_context_id,
1413 |registry| {
1414 registry.unregister(id);
1415 },
1416 );
1417}
1418
1419#[cfg(test)]
1420fn layout_node_registry_stats() -> LayoutNodeRegistryDebugStats {
1421 crate::render_state::with_layout_node_registry(LayoutNodeRegistryState::stats)
1422}
1423
1424pub(crate) fn is_virtual_node(id: NodeId) -> bool {
1425 crate::render_state::with_layout_node_registry(|registry| registry.is_virtual_node(id))
1426}
1427
1428pub(crate) fn allocate_virtual_node_id() -> NodeId {
1429 crate::render_state::with_layout_node_registry(
1430 LayoutNodeRegistryState::allocate_virtual_node_id,
1431 )
1432}
1433
1434fn resolve_modifier_local_from_parent_chain(
1435 start: Option<NodeId>,
1436 token: &ModifierLocalToken,
1437) -> Option<ResolvedModifierLocal> {
1438 crate::render_state::with_layout_node_registry(|registry| {
1439 registry.resolve_modifier_local_from_parent_chain(start, token)
1440 })
1441}
1442
1443#[cfg(test)]
1444#[path = "tests/layout_node_tests.rs"]
1445mod tests;