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