1use crate::{
2 layout::MeasuredNode,
3 modifier::{
4 Modifier, ModifierChainHandle, ModifierLocalSource, ModifierLocalToken,
5 ModifierLocalsHandle, ModifierNodeSlices, Point, ResolvedModifierLocal, ResolvedModifiers,
6 Size,
7 },
8};
9use cranpose_core::{Node, NodeId};
10use cranpose_foundation::{
11 InvalidationKind, ModifierInvalidation, NodeCapabilities, SemanticsConfiguration,
12};
13use cranpose_ui_layout::{Constraints, MeasurePolicy};
14use std::any::TypeId;
15use std::cell::{Cell, RefCell};
16use std::collections::HashMap;
17use std::hash::{Hash, Hasher};
18use std::rc::Rc;
19
20#[derive(Clone, Debug)]
25pub struct LayoutState {
26 pub size: Size,
28 pub position: Point,
30 pub is_placed: bool,
32 pub measurement_constraints: Constraints,
34 pub content_offset: Point,
36}
37
38impl Default for LayoutState {
39 fn default() -> Self {
40 Self {
41 size: Size::default(),
42 position: Point::default(),
43 is_placed: false,
44 measurement_constraints: Constraints {
45 min_width: 0.0,
46 max_width: f32::INFINITY,
47 min_height: 0.0,
48 max_height: f32::INFINITY,
49 },
50 content_offset: Point::default(),
51 }
52 }
53}
54
55#[derive(Clone)]
56struct MeasurementCacheEntry {
57 constraints: Constraints,
58 measured: Rc<MeasuredNode>,
59}
60
61#[derive(Clone, Copy, Debug)]
62pub enum IntrinsicKind {
63 MinWidth(f32),
64 MaxWidth(f32),
65 MinHeight(f32),
66 MaxHeight(f32),
67}
68
69impl IntrinsicKind {
70 fn discriminant(&self) -> u8 {
71 match self {
72 IntrinsicKind::MinWidth(_) => 0,
73 IntrinsicKind::MaxWidth(_) => 1,
74 IntrinsicKind::MinHeight(_) => 2,
75 IntrinsicKind::MaxHeight(_) => 3,
76 }
77 }
78
79 fn value_bits(&self) -> u32 {
80 match self {
81 IntrinsicKind::MinWidth(value)
82 | IntrinsicKind::MaxWidth(value)
83 | IntrinsicKind::MinHeight(value)
84 | IntrinsicKind::MaxHeight(value) => value.to_bits(),
85 }
86 }
87}
88
89impl PartialEq for IntrinsicKind {
90 fn eq(&self, other: &Self) -> bool {
91 self.discriminant() == other.discriminant() && self.value_bits() == other.value_bits()
92 }
93}
94
95impl Eq for IntrinsicKind {}
96
97impl Hash for IntrinsicKind {
98 fn hash<H: Hasher>(&self, state: &mut H) {
99 self.discriminant().hash(state);
100 self.value_bits().hash(state);
101 }
102}
103
104#[derive(Default)]
105struct NodeCacheState {
106 epoch: u64,
107 measurements: Vec<MeasurementCacheEntry>,
108 intrinsics: Vec<(IntrinsicKind, f32)>,
109}
110
111#[derive(Clone, Default)]
112pub(crate) struct LayoutNodeCacheHandles {
113 state: Rc<RefCell<NodeCacheState>>,
114}
115
116impl LayoutNodeCacheHandles {
117 pub(crate) fn clear(&self) {
118 let mut state = self.state.borrow_mut();
119 state.measurements.clear();
120 state.intrinsics.clear();
121 state.epoch = 0;
122 }
123
124 pub(crate) fn activate(&self, epoch: u64) {
125 let mut state = self.state.borrow_mut();
126 if state.epoch != epoch {
127 state.measurements.clear();
128 state.intrinsics.clear();
129 state.epoch = epoch;
130 }
131 }
132
133 pub(crate) fn epoch(&self) -> u64 {
134 self.state.borrow().epoch
135 }
136
137 pub(crate) fn get_measurement(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
138 let state = self.state.borrow();
139 state
140 .measurements
141 .iter()
142 .find(|entry| entry.constraints == constraints)
143 .map(|entry| Rc::clone(&entry.measured))
144 }
145
146 pub(crate) fn store_measurement(&self, constraints: Constraints, measured: Rc<MeasuredNode>) {
147 let mut state = self.state.borrow_mut();
148 if let Some(entry) = state
149 .measurements
150 .iter_mut()
151 .find(|entry| entry.constraints == constraints)
152 {
153 entry.measured = measured;
154 } else {
155 state.measurements.push(MeasurementCacheEntry {
156 constraints,
157 measured,
158 });
159 }
160 }
161
162 pub(crate) fn get_intrinsic(&self, kind: &IntrinsicKind) -> Option<f32> {
163 let state = self.state.borrow();
164 state
165 .intrinsics
166 .iter()
167 .find(|(stored_kind, _)| stored_kind == kind)
168 .map(|(_, value)| *value)
169 }
170
171 pub(crate) fn store_intrinsic(&self, kind: IntrinsicKind, value: f32) {
172 let mut state = self.state.borrow_mut();
173 if let Some((_, existing)) = state
174 .intrinsics
175 .iter_mut()
176 .find(|(stored_kind, _)| stored_kind == &kind)
177 {
178 *existing = value;
179 } else {
180 state.intrinsics.push((kind, value));
181 }
182 }
183}
184
185pub struct LayoutNode {
186 pub modifier: Modifier,
187 modifier_chain: ModifierChainHandle,
188 resolved_modifiers: ResolvedModifiers,
189 modifier_capabilities: NodeCapabilities,
190 modifier_child_capabilities: NodeCapabilities,
191 pub measure_policy: Rc<dyn MeasurePolicy>,
192 pub children: Vec<NodeId>,
194 cache: LayoutNodeCacheHandles,
195 needs_measure: Cell<bool>,
197 needs_layout: Cell<bool>,
198 needs_semantics: Cell<bool>,
199 needs_redraw: Cell<bool>,
200 needs_pointer_pass: Cell<bool>,
201 needs_focus_sync: Cell<bool>,
202 parent: Cell<Option<NodeId>>,
204 folded_parent: Cell<Option<NodeId>>,
206 id: Cell<Option<NodeId>>,
208 debug_modifiers: Cell<bool>,
209 is_virtual: bool,
212 virtual_children_count: Cell<usize>,
214
215 modifier_slices_buffer: RefCell<ModifierNodeSlices>,
217 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
218 modifier_slices_dirty: Cell<bool>,
219
220 layout_state: Rc<RefCell<LayoutState>>,
224}
225
226pub(crate) const RECYCLED_LAYOUT_NODE_POOL_LIMIT: usize = 128;
227
228thread_local! {
229 static EMPTY_MEASURE_POLICY: Rc<dyn MeasurePolicy> =
230 Rc::new(crate::layout::policies::EmptyMeasurePolicy);
231}
232
233fn empty_measure_policy() -> Rc<dyn MeasurePolicy> {
234 EMPTY_MEASURE_POLICY.with(Rc::clone)
235}
236
237impl LayoutNode {
238 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
239 Self::new_with_virtual(modifier, measure_policy, false)
240 }
241
242 pub fn new_virtual() -> Self {
245 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), true)
246 }
247
248 fn new_recycled_shell(is_virtual: bool) -> Self {
249 let mut shell =
250 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), is_virtual);
251 shell.needs_measure.set(false);
252 shell.needs_layout.set(false);
253 shell.needs_semantics.set(false);
254 shell.needs_redraw.set(false);
255 shell.needs_pointer_pass.set(false);
256 shell.needs_focus_sync.set(false);
257 shell.parent.set(None);
258 shell.folded_parent.set(None);
259 shell.id.set(None);
260 shell.debug_modifiers.set(false);
261 shell.virtual_children_count.set(0);
262 shell.cache = LayoutNodeCacheHandles::default();
263 shell.modifier_slices_buffer = RefCell::new(ModifierNodeSlices::default());
264 shell.modifier_slices_snapshot = RefCell::new(Rc::default());
265 shell.modifier_slices_dirty = Cell::new(true);
266 shell.layout_state = Rc::new(RefCell::new(LayoutState::default()));
267 shell
268 }
269
270 fn new_with_virtual(
271 modifier: Modifier,
272 measure_policy: Rc<dyn MeasurePolicy>,
273 is_virtual: bool,
274 ) -> Self {
275 let mut node = Self {
276 modifier,
277 modifier_chain: ModifierChainHandle::new(),
278 resolved_modifiers: ResolvedModifiers::default(),
279 modifier_capabilities: NodeCapabilities::default(),
280 modifier_child_capabilities: NodeCapabilities::default(),
281 measure_policy,
282 children: Vec::new(),
283 cache: LayoutNodeCacheHandles::default(),
284 needs_measure: Cell::new(true), needs_layout: Cell::new(true), needs_semantics: Cell::new(true), needs_redraw: Cell::new(true), needs_pointer_pass: Cell::new(false),
289 needs_focus_sync: Cell::new(false),
290 parent: Cell::new(None), folded_parent: Cell::new(None), id: Cell::new(None), debug_modifiers: Cell::new(false),
294 is_virtual,
295 virtual_children_count: Cell::new(0),
296 modifier_slices_buffer: RefCell::new(ModifierNodeSlices::default()),
297 modifier_slices_snapshot: RefCell::new(Rc::default()),
298 modifier_slices_dirty: Cell::new(true),
299 layout_state: Rc::new(RefCell::new(LayoutState::default())),
300 };
301 node.sync_modifier_chain();
302 node
303 }
304
305 pub fn set_modifier(&mut self, modifier: Modifier) {
306 let modifier_changed = !self.modifier.structural_eq(&modifier);
312 self.modifier = modifier;
313 self.sync_modifier_chain();
314 if modifier_changed {
315 self.cache.clear();
316 self.mark_needs_measure();
317 self.request_semantics_update();
318 }
319 }
320
321 fn sync_modifier_chain(&mut self) {
322 let start_parent = self.parent();
323 let mut resolver = move |token: ModifierLocalToken| {
324 resolve_modifier_local_from_parent_chain(start_parent, token)
325 };
326 self.modifier_chain
327 .set_debug_logging(self.debug_modifiers.get());
328 self.modifier_chain.set_node_id(self.id.get());
329 let modifier_local_invalidations = self
330 .modifier_chain
331 .update_with_resolver(&self.modifier, &mut resolver);
332 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
333 self.modifier_capabilities = self.modifier_chain.capabilities();
334 self.modifier_child_capabilities = self.modifier_chain.aggregate_child_capabilities();
335
336 self.update_modifier_slices_cache();
337
338 let mut invalidations = self.modifier_chain.take_invalidations();
339 invalidations.extend(modifier_local_invalidations);
340 self.dispatch_modifier_invalidations(&invalidations);
341 self.refresh_registry_state();
342 }
343
344 fn update_modifier_slices_cache(&self) {
345 use crate::modifier::collect_modifier_slices_into;
346
347 let mut buffer = self.modifier_slices_buffer.borrow_mut();
348 collect_modifier_slices_into(self.modifier_chain.chain(), &mut buffer);
349 *self.modifier_slices_snapshot.borrow_mut() = Rc::new(buffer.clone());
350 self.modifier_slices_dirty.set(false);
351 }
352
353 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
354 for invalidation in invalidations {
355 self.modifier_slices_dirty.set(true);
356 match invalidation.kind() {
357 InvalidationKind::Layout => {
358 if self.has_layout_modifier_nodes() {
359 self.mark_needs_measure();
360 if let Some(id) = self.id.get() {
361 let inside_composition =
362 cranpose_core::composer_context::try_with_composer(|_| ())
363 .is_some();
364 if !inside_composition {
365 crate::schedule_layout_repass(id);
366 }
367 }
368 }
369 }
370 InvalidationKind::Draw => {
371 if self.has_draw_modifier_nodes() {
372 self.mark_needs_redraw();
373 }
374 }
375 InvalidationKind::PointerInput => {
376 if self.has_pointer_input_modifier_nodes() {
377 self.mark_needs_pointer_pass();
378 crate::request_pointer_invalidation();
379 if let Some(id) = self.id.get() {
381 crate::schedule_pointer_repass(id);
382 }
383 }
384 }
385 InvalidationKind::Semantics => {
386 self.request_semantics_update();
387 }
388 InvalidationKind::Focus => {
389 if self.has_focus_modifier_nodes() {
390 self.mark_needs_focus_sync();
391 crate::request_focus_invalidation();
392 if let Some(id) = self.id.get() {
394 crate::schedule_focus_invalidation(id);
395 }
396 }
397 }
398 }
399 }
400 }
401
402 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
403 if !Rc::ptr_eq(&self.measure_policy, &policy) {
405 self.measure_policy = policy;
406 self.cache.clear();
407 self.mark_needs_measure();
408 if let Some(id) = self.id.get() {
409 cranpose_core::bubble_measure_dirty_in_composer(id);
410 }
411 }
412 }
413
414 pub fn mark_needs_measure(&self) {
416 self.needs_measure.set(true);
417 self.needs_layout.set(true);
418 }
419
420 pub fn mark_needs_layout(&self) {
422 self.needs_layout.set(true);
423 }
424
425 pub fn mark_needs_redraw(&self) {
427 self.needs_redraw.set(true);
428 if let Some(id) = self.id.get() {
429 crate::schedule_draw_repass(id);
430 }
431 crate::request_render_invalidation();
432 }
433
434 pub fn needs_measure(&self) -> bool {
436 self.needs_measure.get()
437 }
438
439 pub fn needs_layout(&self) -> bool {
441 self.needs_layout.get()
442 }
443
444 pub fn mark_needs_semantics(&self) {
446 self.needs_semantics.set(true);
447 }
448
449 pub(crate) fn clear_needs_semantics(&self) {
451 self.needs_semantics.set(false);
452 }
453
454 pub fn needs_semantics(&self) -> bool {
456 self.needs_semantics.get()
457 }
458
459 pub fn needs_redraw(&self) -> bool {
461 self.needs_redraw.get()
462 }
463
464 pub fn clear_needs_redraw(&self) {
465 self.needs_redraw.set(false);
466 }
467
468 fn request_semantics_update(&self) {
469 let already_dirty = self.needs_semantics.replace(true);
470 if already_dirty {
471 return;
472 }
473
474 if let Some(id) = self.id.get() {
475 cranpose_core::queue_semantics_invalidation(id);
476 }
477 }
478
479 pub(crate) fn clear_needs_measure(&self) {
481 self.needs_measure.set(false);
482 }
483
484 pub(crate) fn clear_needs_layout(&self) {
486 self.needs_layout.set(false);
487 }
488
489 pub fn mark_needs_pointer_pass(&self) {
491 self.needs_pointer_pass.set(true);
492 }
493
494 pub fn needs_pointer_pass(&self) -> bool {
496 self.needs_pointer_pass.get()
497 }
498
499 pub fn clear_needs_pointer_pass(&self) {
501 self.needs_pointer_pass.set(false);
502 }
503
504 pub fn mark_needs_focus_sync(&self) {
506 self.needs_focus_sync.set(true);
507 }
508
509 pub fn needs_focus_sync(&self) -> bool {
511 self.needs_focus_sync.get()
512 }
513
514 pub fn clear_needs_focus_sync(&self) {
516 self.needs_focus_sync.set(false);
517 }
518
519 pub fn set_node_id(&mut self, id: NodeId) {
521 if let Some(existing) = self.id.replace(Some(id)) {
522 unregister_layout_node(existing);
523 }
524 register_layout_node(id, self);
525 self.refresh_registry_state();
526
527 self.modifier_chain.set_node_id(Some(id));
530 }
531
532 pub fn node_id(&self) -> Option<NodeId> {
534 self.id.get()
535 }
536
537 pub fn set_parent(&self, parent: NodeId) {
540 self.folded_parent.set(Some(parent));
541 self.parent.set(Some(parent));
544 self.refresh_registry_state();
545 }
546
547 pub fn clear_parent(&self) {
549 self.folded_parent.set(None);
550 self.parent.set(None);
551 self.refresh_registry_state();
552 }
553
554 pub fn parent(&self) -> Option<NodeId> {
556 self.parent.get()
557 }
558
559 pub fn folded_parent(&self) -> Option<NodeId> {
561 self.folded_parent.get()
562 }
563
564 pub fn is_virtual(&self) -> bool {
566 self.is_virtual
567 }
568
569 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
570 self.cache.clone()
571 }
572
573 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
574 self.resolved_modifiers
575 }
576
577 pub fn modifier_capabilities(&self) -> NodeCapabilities {
578 self.modifier_capabilities
579 }
580
581 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
582 self.modifier_child_capabilities
583 }
584
585 pub fn set_debug_modifiers(&mut self, enabled: bool) {
586 self.debug_modifiers.set(enabled);
587 self.modifier_chain.set_debug_logging(enabled);
588 }
589
590 pub fn debug_modifiers_enabled(&self) -> bool {
591 self.debug_modifiers.get()
592 }
593
594 pub fn modifier_locals_handle(&self) -> ModifierLocalsHandle {
595 self.modifier_chain.modifier_locals_handle()
596 }
597
598 pub fn has_layout_modifier_nodes(&self) -> bool {
599 self.modifier_capabilities
600 .contains(NodeCapabilities::LAYOUT)
601 }
602
603 pub fn has_draw_modifier_nodes(&self) -> bool {
604 self.modifier_capabilities.contains(NodeCapabilities::DRAW)
605 }
606
607 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
608 self.modifier_capabilities
609 .contains(NodeCapabilities::POINTER_INPUT)
610 }
611
612 pub fn has_semantics_modifier_nodes(&self) -> bool {
613 self.modifier_capabilities
614 .contains(NodeCapabilities::SEMANTICS)
615 }
616
617 pub fn has_focus_modifier_nodes(&self) -> bool {
618 self.modifier_capabilities.contains(NodeCapabilities::FOCUS)
619 }
620
621 fn refresh_registry_state(&self) {
622 if let Some(id) = self.id.get() {
623 let parent = self.parent();
624 let capabilities = self.modifier_child_capabilities();
625 let modifier_locals = self.modifier_locals_handle();
626 LAYOUT_NODE_REGISTRY.with(|registry| {
627 if let Some(entry) = registry.borrow_mut().get_mut(&id) {
628 entry.parent = parent;
629 entry.modifier_child_capabilities = capabilities;
630 entry.modifier_locals = modifier_locals;
631 }
632 });
633 }
634 }
635
636 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
637 if self.modifier_slices_dirty.get() {
638 self.update_modifier_slices_cache();
639 }
640 self.modifier_slices_snapshot.borrow().clone()
641 }
642
643 pub fn layout_state(&self) -> LayoutState {
649 self.layout_state.borrow().clone()
650 }
651
652 pub fn measured_size(&self) -> Size {
654 self.layout_state.borrow().size
655 }
656
657 pub fn position(&self) -> Point {
659 self.layout_state.borrow().position
660 }
661
662 pub fn is_placed(&self) -> bool {
664 self.layout_state.borrow().is_placed
665 }
666
667 pub fn set_measured_size(&self, size: Size) {
669 let mut state = self.layout_state.borrow_mut();
670 state.size = size;
671 }
672
673 pub fn set_position(&self, position: Point) {
675 let mut state = self.layout_state.borrow_mut();
676 state.position = position;
677 state.is_placed = true;
678 }
679
680 pub fn set_measurement_constraints(&self, constraints: Constraints) {
682 self.layout_state.borrow_mut().measurement_constraints = constraints;
683 }
684
685 pub fn set_content_offset(&self, offset: Point) {
687 self.layout_state.borrow_mut().content_offset = offset;
688 }
689
690 pub fn clear_placed(&self) {
692 self.layout_state.borrow_mut().is_placed = false;
693 }
694
695 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
696 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
697 }
698
699 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
701 &self.modifier_chain
702 }
703
704 pub fn with_text_field_modifier_mut<R>(
709 &mut self,
710 f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
711 ) -> Option<R> {
712 self.modifier_chain.with_text_field_modifier_mut(f)
713 }
714
715 pub fn layout_state_handle(&self) -> Rc<RefCell<LayoutState>> {
718 self.layout_state.clone()
719 }
720}
721impl Clone for LayoutNode {
722 fn clone(&self) -> Self {
723 let mut node = Self {
724 modifier: self.modifier.clone(),
725 modifier_chain: ModifierChainHandle::new(),
726 resolved_modifiers: ResolvedModifiers::default(),
727 modifier_capabilities: self.modifier_capabilities,
728 modifier_child_capabilities: self.modifier_child_capabilities,
729 measure_policy: self.measure_policy.clone(),
730 children: self.children.clone(),
731 cache: self.cache.clone(),
732 needs_measure: Cell::new(self.needs_measure.get()),
733 needs_layout: Cell::new(self.needs_layout.get()),
734 needs_semantics: Cell::new(self.needs_semantics.get()),
735 needs_redraw: Cell::new(self.needs_redraw.get()),
736 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
737 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
738 parent: Cell::new(self.parent.get()),
739 folded_parent: Cell::new(self.folded_parent.get()),
740 id: Cell::new(None),
741 debug_modifiers: Cell::new(self.debug_modifiers.get()),
742 is_virtual: self.is_virtual,
743 virtual_children_count: Cell::new(self.virtual_children_count.get()),
744 modifier_slices_buffer: RefCell::new(ModifierNodeSlices::default()),
745 modifier_slices_snapshot: RefCell::new(Rc::default()),
746 modifier_slices_dirty: Cell::new(true),
747 layout_state: self.layout_state.clone(),
749 };
750 node.sync_modifier_chain();
751 node
752 }
753}
754
755impl Node for LayoutNode {
756 fn mount(&mut self) {
757 let (chain, mut context) = self.modifier_chain.chain_and_context_mut();
758 chain.repair_chain();
759 chain.attach_nodes(&mut *context);
760 }
761
762 fn unmount(&mut self) {
763 self.modifier_chain.chain_mut().detach_nodes();
764 }
765
766 fn set_node_id(&mut self, id: NodeId) {
767 LayoutNode::set_node_id(self, id);
769 }
770
771 fn insert_child(&mut self, child: NodeId) {
772 if self.children.contains(&child) {
773 return;
774 }
775 if is_virtual_node(child) {
776 let count = self.virtual_children_count.get();
777 self.virtual_children_count.set(count + 1);
778 }
779 self.children.push(child);
780 self.cache.clear();
781 self.mark_needs_measure();
782 }
783
784 fn remove_child(&mut self, child: NodeId) {
785 let before = self.children.len();
786 self.children.retain(|&id| id != child);
787 if self.children.len() < before {
788 if is_virtual_node(child) {
789 let count = self.virtual_children_count.get();
790 if count > 0 {
791 self.virtual_children_count.set(count - 1);
792 }
793 }
794 self.cache.clear();
795 self.mark_needs_measure();
796 }
797 }
798
799 fn move_child(&mut self, from: usize, to: usize) {
800 if from == to || from >= self.children.len() {
801 return;
802 }
803 let child = self.children.remove(from);
804 let target = to.min(self.children.len());
805 self.children.insert(target, child);
806 self.cache.clear();
807 self.mark_needs_measure();
808 }
809
810 fn update_children(&mut self, children: &[NodeId]) {
811 self.children.clear();
812 self.children.extend_from_slice(children);
813 self.cache.clear();
814 self.mark_needs_measure();
815 }
816
817 fn children(&self) -> Vec<NodeId> {
818 self.children.clone()
819 }
820
821 fn collect_children_into(&self, out: &mut smallvec::SmallVec<[NodeId; 8]>) {
822 out.clear();
823 out.extend(self.children.iter().copied());
824 }
825
826 fn on_attached_to_parent(&mut self, parent: NodeId) {
827 self.set_parent(parent);
828 }
829
830 fn on_removed_from_parent(&mut self) {
831 self.clear_parent();
832 }
833
834 fn parent(&self) -> Option<NodeId> {
835 self.parent.get()
836 }
837
838 fn mark_needs_layout(&self) {
839 self.needs_layout.set(true);
840 }
841
842 fn needs_layout(&self) -> bool {
843 self.needs_layout.get()
844 }
845
846 fn mark_needs_measure(&self) {
847 self.needs_measure.set(true);
848 self.needs_layout.set(true);
849 }
850
851 fn needs_measure(&self) -> bool {
852 self.needs_measure.get()
853 }
854
855 fn mark_needs_semantics(&self) {
856 self.needs_semantics.set(true);
857 }
858
859 fn needs_semantics(&self) -> bool {
860 self.needs_semantics.get()
861 }
862
863 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
868 self.parent.set(Some(parent));
869 }
870
871 fn recycle_key(&self) -> Option<TypeId> {
872 Some(TypeId::of::<Self>())
873 }
874
875 fn recycle_pool_limit(&self) -> Option<usize> {
876 Some(RECYCLED_LAYOUT_NODE_POOL_LIMIT)
877 }
878
879 fn prepare_for_recycle(&mut self) {
880 *self = Self::new_recycled_shell(self.is_virtual);
881 }
882
883 fn rehouse_for_recycle(&self) -> Option<Box<dyn cranpose_core::Node>> {
884 Some(Box::new(Self::new_recycled_shell(self.is_virtual)))
885 }
886
887 fn rehouse_for_live_compaction(&mut self) -> Option<Box<dyn cranpose_core::Node>> {
888 let mut previous = std::mem::replace(self, Self::new_recycled_shell(self.is_virtual));
889 let node_id = previous.id.replace(None);
890 let parent = previous.parent.get();
891 let folded_parent = previous.folded_parent.get();
892 let debug_modifiers = previous.debug_modifiers.get();
893 let needs_measure = previous.needs_measure.get();
894 let needs_layout = previous.needs_layout.get();
895 let needs_semantics = previous.needs_semantics.get();
896 let needs_redraw = previous.needs_redraw.get();
897 let needs_pointer_pass = previous.needs_pointer_pass.get();
898 let needs_focus_sync = previous.needs_focus_sync.get();
899 let virtual_children_count = previous.virtual_children_count.get();
900 let children = previous.children.to_vec();
901 let modifier = previous.modifier.rehouse_for_live_compaction();
902 let measure_policy = previous.measure_policy.clone();
903 let layout_state = previous.layout_state.clone();
904
905 previous.modifier_chain.chain_mut().detach_nodes();
906
907 let mut compact = Self::new_with_virtual(modifier, measure_policy, previous.is_virtual);
908 compact.children = children;
909 compact.parent.set(parent);
910 compact.folded_parent.set(folded_parent);
911 compact.id.set(node_id);
912 compact.debug_modifiers.set(debug_modifiers);
913 compact.needs_measure.set(needs_measure);
914 compact.needs_layout.set(needs_layout);
915 compact.needs_semantics.set(needs_semantics);
916 compact.needs_redraw.set(needs_redraw);
917 compact.needs_pointer_pass.set(needs_pointer_pass);
918 compact.needs_focus_sync.set(needs_focus_sync);
919 compact.virtual_children_count.set(virtual_children_count);
920 compact.layout_state = layout_state;
921 compact.sync_modifier_chain();
922 if let Some(id) = node_id {
923 register_layout_node(id, &compact);
924 }
925
926 Some(Box::new(compact))
927 }
928}
929
930impl Drop for LayoutNode {
931 fn drop(&mut self) {
932 if let Some(id) = self.id.get() {
933 unregister_layout_node(id);
934 }
935 }
936}
937
938thread_local! {
939 static LAYOUT_NODE_REGISTRY: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>> =
940 RefCell::new(HashMap::new());
941 static VIRTUAL_NODE_ID_COUNTER: std::sync::atomic::AtomicUsize = const { std::sync::atomic::AtomicUsize::new(0xC0000000) };
945}
946
947const MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY: usize = 128;
948
949#[cfg(test)]
950#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
951struct LayoutNodeRegistryDebugStats {
952 len: usize,
953 capacity: usize,
954}
955
956struct LayoutNodeRegistryEntry {
957 parent: Option<NodeId>,
958 modifier_child_capabilities: NodeCapabilities,
959 modifier_locals: ModifierLocalsHandle,
960 is_virtual: bool,
961}
962
963pub(crate) fn register_layout_node(id: NodeId, node: &LayoutNode) {
964 LAYOUT_NODE_REGISTRY.with(|registry| {
965 registry.borrow_mut().insert(
966 id,
967 LayoutNodeRegistryEntry {
968 parent: node.parent(),
969 modifier_child_capabilities: node.modifier_child_capabilities(),
970 modifier_locals: node.modifier_locals_handle(),
971 is_virtual: node.is_virtual(),
972 },
973 );
974 });
975}
976
977pub(crate) fn unregister_layout_node(id: NodeId) {
978 LAYOUT_NODE_REGISTRY.with(|registry| {
979 let mut registry = registry.borrow_mut();
980 registry.remove(&id);
981 let should_shrink = (registry.len() <= MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
982 && registry.capacity() > MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
983 || registry.capacity()
984 > registry
985 .len()
986 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
987 .saturating_mul(4);
988 if should_shrink {
989 let retained = registry
990 .len()
991 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY);
992 let mut rebuilt = HashMap::new();
993 rebuilt.reserve(retained);
994 rebuilt.extend(registry.drain());
995 *registry = rebuilt;
996 }
997 });
998}
999
1000#[cfg(test)]
1001fn layout_node_registry_stats() -> LayoutNodeRegistryDebugStats {
1002 LAYOUT_NODE_REGISTRY.with(|registry| {
1003 let registry = registry.borrow();
1004 LayoutNodeRegistryDebugStats {
1005 len: registry.len(),
1006 capacity: registry.capacity(),
1007 }
1008 })
1009}
1010
1011pub(crate) fn is_virtual_node(id: NodeId) -> bool {
1012 LAYOUT_NODE_REGISTRY.with(|registry| {
1013 registry
1014 .borrow()
1015 .get(&id)
1016 .map(|entry| entry.is_virtual)
1017 .unwrap_or(false)
1018 })
1019}
1020
1021pub(crate) fn allocate_virtual_node_id() -> NodeId {
1022 use std::sync::atomic::Ordering;
1023 VIRTUAL_NODE_ID_COUNTER.with(|counter| counter.fetch_add(1, Ordering::Relaxed))
1026}
1027
1028fn resolve_modifier_local_from_parent_chain(
1029 start: Option<NodeId>,
1030 token: ModifierLocalToken,
1031) -> Option<ResolvedModifierLocal> {
1032 let mut current = start;
1033 while let Some(parent_id) = current {
1034 let (next_parent, resolved) = LAYOUT_NODE_REGISTRY.with(|registry| {
1035 let registry = registry.borrow();
1036 if let Some(entry) = registry.get(&parent_id) {
1037 let resolved = if entry
1038 .modifier_child_capabilities
1039 .contains(NodeCapabilities::MODIFIER_LOCALS)
1040 {
1041 entry
1042 .modifier_locals
1043 .borrow()
1044 .resolve(token)
1045 .map(|value| value.with_source(ModifierLocalSource::Ancestor))
1046 } else {
1047 None
1048 };
1049 (entry.parent, resolved)
1050 } else {
1051 (None, None)
1052 }
1053 });
1054 if let Some(value) = resolved {
1055 return Some(value);
1056 }
1057 current = next_parent;
1058 }
1059 None
1060}
1061
1062#[cfg(test)]
1063mod tests {
1064 use super::*;
1065 use cranpose_ui_graphics::Size as GeometrySize;
1066 use cranpose_ui_layout::{Measurable, MeasureResult};
1067 use std::rc::Rc;
1068
1069 #[derive(Default)]
1070 struct TestMeasurePolicy;
1071
1072 impl MeasurePolicy for TestMeasurePolicy {
1073 fn measure(
1074 &self,
1075 _measurables: &[Box<dyn Measurable>],
1076 _constraints: Constraints,
1077 ) -> MeasureResult {
1078 MeasureResult::new(
1079 GeometrySize {
1080 width: 0.0,
1081 height: 0.0,
1082 },
1083 Vec::new(),
1084 )
1085 }
1086
1087 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1088 0.0
1089 }
1090
1091 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
1092 0.0
1093 }
1094
1095 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1096 0.0
1097 }
1098
1099 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
1100 0.0
1101 }
1102 }
1103
1104 fn fresh_node() -> LayoutNode {
1105 LayoutNode::new(Modifier::empty(), Rc::new(TestMeasurePolicy))
1106 }
1107
1108 #[test]
1109 fn layout_node_registry_retains_warm_capacity_after_large_cleanup() {
1110 let nodes: Vec<_> = (0..2048)
1111 .map(|_| {
1112 let id = allocate_virtual_node_id();
1113 let node = fresh_node();
1114 register_layout_node(id, &node);
1115 (id, node)
1116 })
1117 .collect();
1118
1119 for (id, _) in &nodes {
1120 unregister_layout_node(*id);
1121 }
1122
1123 let stats = layout_node_registry_stats();
1124 assert_eq!(stats.len, 0);
1125 assert!(
1126 (MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
1127 ..=MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY.saturating_mul(2))
1128 .contains(&stats.capacity),
1129 "registry warm capacity {} fell outside expected retained range {}..={}",
1130 stats.capacity,
1131 MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY,
1132 MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY.saturating_mul(2),
1133 );
1134 }
1135
1136 fn invalidation(kind: InvalidationKind) -> ModifierInvalidation {
1137 ModifierInvalidation::new(kind, NodeCapabilities::for_invalidation(kind))
1138 }
1139
1140 #[test]
1141 fn layout_invalidation_requires_layout_capability() {
1142 let mut node = fresh_node();
1143 node.clear_needs_measure();
1144 node.clear_needs_layout();
1145 node.modifier_capabilities = NodeCapabilities::DRAW;
1146 node.modifier_child_capabilities = node.modifier_capabilities;
1147
1148 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
1149
1150 assert!(!node.needs_measure());
1151 assert!(!node.needs_layout());
1152 }
1153
1154 #[test]
1155 fn semantics_configuration_reflects_modifier_state() {
1156 let mut node = fresh_node();
1157 node.set_modifier(Modifier::empty().semantics(|config| {
1158 config.content_description = Some("greeting".into());
1159 config.is_clickable = true;
1160 }));
1161
1162 let config = node
1163 .semantics_configuration()
1164 .expect("expected semantics configuration");
1165 assert_eq!(config.content_description.as_deref(), Some("greeting"));
1166 assert!(config.is_clickable);
1167 }
1168
1169 #[test]
1170 fn layout_invalidation_marks_flags_when_capability_present() {
1171 let _guard = crate::render_state::render_state_test_guard();
1172 crate::reset_render_state_for_tests();
1173 let mut node = fresh_node();
1174 node.id.set(Some(11));
1175 node.clear_needs_measure();
1176 node.clear_needs_layout();
1177 node.modifier_capabilities = NodeCapabilities::LAYOUT;
1178 node.modifier_child_capabilities = node.modifier_capabilities;
1179
1180 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
1181
1182 assert!(node.needs_measure());
1183 assert!(node.needs_layout());
1184 assert_eq!(crate::take_layout_repass_nodes(), vec![11]);
1185 assert!(crate::take_layout_invalidation());
1186 }
1187
1188 #[test]
1189 fn layout_invalidation_skips_repass_while_composing() {
1190 let _guard = crate::render_state::render_state_test_guard();
1191 crate::reset_render_state_for_tests();
1192
1193 let node = Rc::new(RefCell::new(fresh_node()));
1194 {
1195 let mut node = node.borrow_mut();
1196 node.id.set(Some(17));
1197 node.clear_needs_measure();
1198 node.clear_needs_layout();
1199 node.modifier_capabilities = NodeCapabilities::LAYOUT;
1200 node.modifier_child_capabilities = node.modifier_capabilities;
1201 }
1202
1203 let node_for_composition = Rc::clone(&node);
1204 let _composition = crate::run_test_composition(move || {
1205 node_for_composition
1206 .borrow()
1207 .dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
1208 });
1209
1210 let node = node.borrow();
1211 assert!(node.needs_measure());
1212 assert!(node.needs_layout());
1213 assert!(crate::take_layout_repass_nodes().is_empty());
1214 assert!(!crate::take_layout_invalidation());
1215 }
1216
1217 #[test]
1218 fn draw_invalidation_marks_redraw_flag_when_capable() {
1219 let mut node = fresh_node();
1220 node.clear_needs_measure();
1221 node.clear_needs_layout();
1222 node.modifier_capabilities = NodeCapabilities::DRAW;
1223 node.modifier_child_capabilities = node.modifier_capabilities;
1224
1225 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Draw)]);
1226
1227 assert!(node.needs_redraw());
1228 assert!(!node.needs_layout());
1229 }
1230
1231 #[test]
1232 fn semantics_invalidation_sets_semantics_flag_only() {
1233 let mut node = fresh_node();
1234 node.clear_needs_measure();
1235 node.clear_needs_layout();
1236 node.clear_needs_semantics();
1237 node.modifier_capabilities = NodeCapabilities::SEMANTICS;
1238 node.modifier_child_capabilities = node.modifier_capabilities;
1239
1240 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Semantics)]);
1241
1242 assert!(node.needs_semantics());
1243 assert!(!node.needs_measure());
1244 assert!(!node.needs_layout());
1245 }
1246
1247 #[test]
1248 fn pointer_invalidation_requires_pointer_capability() {
1249 let mut node = fresh_node();
1250 node.clear_needs_pointer_pass();
1251 node.modifier_capabilities = NodeCapabilities::DRAW;
1252 node.modifier_child_capabilities = node.modifier_capabilities;
1253 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::PointerInput)]);
1258
1259 assert!(!node.needs_pointer_pass());
1260 }
1261
1262 #[test]
1263 fn pointer_invalidation_marks_flag_and_requests_queue() {
1264 let mut node = fresh_node();
1265 node.clear_needs_pointer_pass();
1266 node.modifier_capabilities = NodeCapabilities::POINTER_INPUT;
1267 node.modifier_child_capabilities = node.modifier_capabilities;
1268 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::PointerInput)]);
1273
1274 assert!(node.needs_pointer_pass());
1275 }
1276
1277 #[test]
1278 fn focus_invalidation_requires_focus_capability() {
1279 let mut node = fresh_node();
1280 node.clear_needs_focus_sync();
1281 node.modifier_capabilities = NodeCapabilities::DRAW;
1282 node.modifier_child_capabilities = node.modifier_capabilities;
1283 crate::take_focus_invalidation();
1284
1285 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Focus)]);
1286
1287 assert!(!node.needs_focus_sync());
1288 assert!(!crate::take_focus_invalidation());
1289 }
1290
1291 #[test]
1292 fn focus_invalidation_marks_flag_and_requests_queue() {
1293 let mut node = fresh_node();
1294 node.clear_needs_focus_sync();
1295 node.modifier_capabilities = NodeCapabilities::FOCUS;
1296 node.modifier_child_capabilities = node.modifier_capabilities;
1297 crate::take_focus_invalidation();
1298
1299 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Focus)]);
1300
1301 assert!(node.needs_focus_sync());
1302 assert!(crate::take_focus_invalidation());
1303 }
1304
1305 #[test]
1306 fn set_modifier_marks_semantics_dirty() {
1307 let mut node = fresh_node();
1308 node.clear_needs_semantics();
1309 node.set_modifier(Modifier::empty().semantics(|config| {
1310 config.is_clickable = true;
1311 }));
1312
1313 assert!(node.needs_semantics());
1314 }
1315
1316 #[test]
1317 fn modifier_child_capabilities_reflect_chain_head() {
1318 let mut node = fresh_node();
1319 node.set_modifier(Modifier::empty().padding(4.0));
1320 assert!(
1321 node.modifier_child_capabilities()
1322 .contains(NodeCapabilities::LAYOUT),
1323 "padding should introduce layout capability"
1324 );
1325 }
1326}