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::cell::{Cell, RefCell};
15use std::collections::HashMap;
16use std::hash::{Hash, Hasher};
17use std::rc::Rc;
18
19#[derive(Clone, Debug)]
24pub struct LayoutState {
25 pub size: Size,
27 pub position: Point,
29 pub is_placed: bool,
31 pub measurement_constraints: Constraints,
33 pub content_offset: Point,
35}
36
37impl Default for LayoutState {
38 fn default() -> Self {
39 Self {
40 size: Size::default(),
41 position: Point::default(),
42 is_placed: false,
43 measurement_constraints: Constraints {
44 min_width: 0.0,
45 max_width: f32::INFINITY,
46 min_height: 0.0,
47 max_height: f32::INFINITY,
48 },
49 content_offset: Point::default(),
50 }
51 }
52}
53
54#[derive(Clone)]
55struct MeasurementCacheEntry {
56 constraints: Constraints,
57 measured: Rc<MeasuredNode>,
58}
59
60#[derive(Clone, Copy, Debug)]
61pub enum IntrinsicKind {
62 MinWidth(f32),
63 MaxWidth(f32),
64 MinHeight(f32),
65 MaxHeight(f32),
66}
67
68impl IntrinsicKind {
69 fn discriminant(&self) -> u8 {
70 match self {
71 IntrinsicKind::MinWidth(_) => 0,
72 IntrinsicKind::MaxWidth(_) => 1,
73 IntrinsicKind::MinHeight(_) => 2,
74 IntrinsicKind::MaxHeight(_) => 3,
75 }
76 }
77
78 fn value_bits(&self) -> u32 {
79 match self {
80 IntrinsicKind::MinWidth(value)
81 | IntrinsicKind::MaxWidth(value)
82 | IntrinsicKind::MinHeight(value)
83 | IntrinsicKind::MaxHeight(value) => value.to_bits(),
84 }
85 }
86}
87
88impl PartialEq for IntrinsicKind {
89 fn eq(&self, other: &Self) -> bool {
90 self.discriminant() == other.discriminant() && self.value_bits() == other.value_bits()
91 }
92}
93
94impl Eq for IntrinsicKind {}
95
96impl Hash for IntrinsicKind {
97 fn hash<H: Hasher>(&self, state: &mut H) {
98 self.discriminant().hash(state);
99 self.value_bits().hash(state);
100 }
101}
102
103#[derive(Default)]
104struct NodeCacheState {
105 epoch: u64,
106 measurements: Vec<MeasurementCacheEntry>,
107 intrinsics: Vec<(IntrinsicKind, f32)>,
108}
109
110#[derive(Clone, Default)]
111pub(crate) struct LayoutNodeCacheHandles {
112 state: Rc<RefCell<NodeCacheState>>,
113}
114
115impl LayoutNodeCacheHandles {
116 pub(crate) fn clear(&self) {
117 let mut state = self.state.borrow_mut();
118 state.measurements.clear();
119 state.intrinsics.clear();
120 state.epoch = 0;
121 }
122
123 pub(crate) fn activate(&self, epoch: u64) {
124 let mut state = self.state.borrow_mut();
125 if state.epoch != epoch {
126 state.measurements.clear();
127 state.intrinsics.clear();
128 state.epoch = epoch;
129 }
130 }
131
132 pub(crate) fn epoch(&self) -> u64 {
133 self.state.borrow().epoch
134 }
135
136 pub(crate) fn get_measurement(&self, constraints: Constraints) -> Option<Rc<MeasuredNode>> {
137 let state = self.state.borrow();
138 state
139 .measurements
140 .iter()
141 .find(|entry| entry.constraints == constraints)
142 .map(|entry| Rc::clone(&entry.measured))
143 }
144
145 pub(crate) fn store_measurement(&self, constraints: Constraints, measured: Rc<MeasuredNode>) {
146 let mut state = self.state.borrow_mut();
147 if let Some(entry) = state
148 .measurements
149 .iter_mut()
150 .find(|entry| entry.constraints == constraints)
151 {
152 entry.measured = measured;
153 } else {
154 state.measurements.push(MeasurementCacheEntry {
155 constraints,
156 measured,
157 });
158 }
159 }
160
161 pub(crate) fn get_intrinsic(&self, kind: &IntrinsicKind) -> Option<f32> {
162 let state = self.state.borrow();
163 state
164 .intrinsics
165 .iter()
166 .find(|(stored_kind, _)| stored_kind == kind)
167 .map(|(_, value)| *value)
168 }
169
170 pub(crate) fn store_intrinsic(&self, kind: IntrinsicKind, value: f32) {
171 let mut state = self.state.borrow_mut();
172 if let Some((_, existing)) = state
173 .intrinsics
174 .iter_mut()
175 .find(|(stored_kind, _)| stored_kind == &kind)
176 {
177 *existing = value;
178 } else {
179 state.intrinsics.push((kind, value));
180 }
181 }
182}
183
184pub struct LayoutNode {
185 pub modifier: Modifier,
186 modifier_chain: ModifierChainHandle,
187 resolved_modifiers: ResolvedModifiers,
188 modifier_capabilities: NodeCapabilities,
189 modifier_child_capabilities: NodeCapabilities,
190 pub measure_policy: Rc<dyn MeasurePolicy>,
191 pub children: Vec<NodeId>,
193 cache: LayoutNodeCacheHandles,
194 needs_measure: Cell<bool>,
196 needs_layout: Cell<bool>,
197 needs_semantics: Cell<bool>,
198 needs_redraw: Cell<bool>,
199 needs_pointer_pass: Cell<bool>,
200 needs_focus_sync: Cell<bool>,
201 parent: Cell<Option<NodeId>>,
203 folded_parent: Cell<Option<NodeId>>,
205 id: Cell<Option<NodeId>>,
207 debug_modifiers: Cell<bool>,
208 is_virtual: bool,
211 virtual_children_count: Cell<usize>,
213
214 modifier_slices_buffer: RefCell<ModifierNodeSlices>,
216 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
217
218 layout_state: Rc<RefCell<LayoutState>>,
222}
223
224impl LayoutNode {
225 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
226 Self::new_with_virtual(modifier, measure_policy, false)
227 }
228
229 pub fn new_virtual() -> Self {
232 Self::new_with_virtual(
233 Modifier::empty(),
234 Rc::new(crate::layout::policies::EmptyMeasurePolicy),
235 true,
236 )
237 }
238
239 fn new_with_virtual(
240 modifier: Modifier,
241 measure_policy: Rc<dyn MeasurePolicy>,
242 is_virtual: bool,
243 ) -> Self {
244 let mut node = Self {
245 modifier,
246 modifier_chain: ModifierChainHandle::new(),
247 resolved_modifiers: ResolvedModifiers::default(),
248 modifier_capabilities: NodeCapabilities::default(),
249 modifier_child_capabilities: NodeCapabilities::default(),
250 measure_policy,
251 children: Vec::new(),
252 cache: LayoutNodeCacheHandles::default(),
253 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),
258 needs_focus_sync: Cell::new(false),
259 parent: Cell::new(None), folded_parent: Cell::new(None), id: Cell::new(None), debug_modifiers: Cell::new(false),
263 is_virtual,
264 virtual_children_count: Cell::new(0),
265 modifier_slices_buffer: RefCell::new(ModifierNodeSlices::default()),
266 modifier_slices_snapshot: RefCell::new(Rc::default()),
267 layout_state: Rc::new(RefCell::new(LayoutState::default())),
268 };
269 node.sync_modifier_chain();
270 node
271 }
272
273 pub fn set_modifier(&mut self, modifier: Modifier) {
274 let modifier_changed = !self.modifier.structural_eq(&modifier);
280 self.modifier = modifier;
281 self.sync_modifier_chain();
282 if modifier_changed {
283 self.cache.clear();
284 self.mark_needs_measure();
285 self.request_semantics_update();
286 }
287 }
288
289 fn sync_modifier_chain(&mut self) {
290 let start_parent = self.parent();
291 let mut resolver = move |token: ModifierLocalToken| {
292 resolve_modifier_local_from_parent_chain(start_parent, token)
293 };
294 self.modifier_chain
295 .set_debug_logging(self.debug_modifiers.get());
296 self.modifier_chain.set_node_id(self.id.get());
297 let modifier_local_invalidations = self
298 .modifier_chain
299 .update_with_resolver(&self.modifier, &mut resolver);
300 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
301 self.modifier_capabilities = self.modifier_chain.capabilities();
302 self.modifier_child_capabilities = self.modifier_chain.aggregate_child_capabilities();
303
304 use crate::modifier::collect_modifier_slices_into;
307 let mut buffer = self.modifier_slices_buffer.borrow_mut();
308 collect_modifier_slices_into(self.modifier_chain.chain(), &mut buffer);
309 *self.modifier_slices_snapshot.borrow_mut() = Rc::new(buffer.clone());
310
311 let mut invalidations = self.modifier_chain.take_invalidations();
312 invalidations.extend(modifier_local_invalidations);
313 self.dispatch_modifier_invalidations(&invalidations);
314 self.refresh_registry_state();
315 }
316
317 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
318 for invalidation in invalidations {
319 match invalidation.kind() {
320 InvalidationKind::Layout => {
321 if self.has_layout_modifier_nodes() {
322 self.mark_needs_measure();
323 if let Some(id) = self.id.get() {
324 crate::schedule_layout_repass(id);
325 }
326 }
327 }
328 InvalidationKind::Draw => {
329 if self.has_draw_modifier_nodes() {
330 self.mark_needs_redraw();
331 }
332 }
333 InvalidationKind::PointerInput => {
334 if self.has_pointer_input_modifier_nodes() {
335 self.mark_needs_pointer_pass();
336 crate::request_pointer_invalidation();
337 if let Some(id) = self.id.get() {
339 crate::schedule_pointer_repass(id);
340 }
341 }
342 }
343 InvalidationKind::Semantics => {
344 self.request_semantics_update();
345 }
346 InvalidationKind::Focus => {
347 if self.has_focus_modifier_nodes() {
348 self.mark_needs_focus_sync();
349 crate::request_focus_invalidation();
350 if let Some(id) = self.id.get() {
352 crate::schedule_focus_invalidation(id);
353 }
354 }
355 }
356 }
357 }
358 }
359
360 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
361 if !Rc::ptr_eq(&self.measure_policy, &policy) {
363 self.measure_policy = policy;
364 self.cache.clear();
365 self.mark_needs_measure();
366 if let Some(id) = self.id.get() {
367 cranpose_core::bubble_measure_dirty_in_composer(id);
368 }
369 }
370 }
371
372 pub fn mark_needs_measure(&self) {
374 self.needs_measure.set(true);
375 self.needs_layout.set(true);
376 }
377
378 pub fn mark_needs_layout(&self) {
380 self.needs_layout.set(true);
381 }
382
383 pub fn mark_needs_redraw(&self) {
385 self.needs_redraw.set(true);
386 if let Some(id) = self.id.get() {
387 crate::schedule_draw_repass(id);
388 }
389 crate::request_render_invalidation();
390 }
391
392 pub fn needs_measure(&self) -> bool {
394 self.needs_measure.get()
395 }
396
397 pub fn needs_layout(&self) -> bool {
399 self.needs_layout.get()
400 }
401
402 pub fn mark_needs_semantics(&self) {
404 self.needs_semantics.set(true);
405 }
406
407 pub(crate) fn clear_needs_semantics(&self) {
409 self.needs_semantics.set(false);
410 }
411
412 pub fn needs_semantics(&self) -> bool {
414 self.needs_semantics.get()
415 }
416
417 pub fn needs_redraw(&self) -> bool {
419 self.needs_redraw.get()
420 }
421
422 pub fn clear_needs_redraw(&self) {
423 self.needs_redraw.set(false);
424 }
425
426 fn request_semantics_update(&self) {
427 let already_dirty = self.needs_semantics.replace(true);
428 if already_dirty {
429 return;
430 }
431
432 if let Some(id) = self.id.get() {
433 cranpose_core::queue_semantics_invalidation(id);
434 }
435 }
436
437 pub(crate) fn clear_needs_measure(&self) {
439 self.needs_measure.set(false);
440 }
441
442 pub(crate) fn clear_needs_layout(&self) {
444 self.needs_layout.set(false);
445 }
446
447 pub fn mark_needs_pointer_pass(&self) {
449 self.needs_pointer_pass.set(true);
450 }
451
452 pub fn needs_pointer_pass(&self) -> bool {
454 self.needs_pointer_pass.get()
455 }
456
457 pub fn clear_needs_pointer_pass(&self) {
459 self.needs_pointer_pass.set(false);
460 }
461
462 pub fn mark_needs_focus_sync(&self) {
464 self.needs_focus_sync.set(true);
465 }
466
467 pub fn needs_focus_sync(&self) -> bool {
469 self.needs_focus_sync.get()
470 }
471
472 pub fn clear_needs_focus_sync(&self) {
474 self.needs_focus_sync.set(false);
475 }
476
477 pub fn set_node_id(&mut self, id: NodeId) {
479 if let Some(existing) = self.id.replace(Some(id)) {
480 unregister_layout_node(existing);
481 }
482 register_layout_node(id, self);
483 self.refresh_registry_state();
484
485 self.modifier_chain.set_node_id(Some(id));
488 }
489
490 pub fn node_id(&self) -> Option<NodeId> {
492 self.id.get()
493 }
494
495 pub fn set_parent(&self, parent: NodeId) {
498 self.folded_parent.set(Some(parent));
499 self.parent.set(Some(parent));
502 self.refresh_registry_state();
503 }
504
505 pub fn clear_parent(&self) {
507 self.folded_parent.set(None);
508 self.parent.set(None);
509 self.refresh_registry_state();
510 }
511
512 pub fn parent(&self) -> Option<NodeId> {
514 self.parent.get()
515 }
516
517 pub fn folded_parent(&self) -> Option<NodeId> {
519 self.folded_parent.get()
520 }
521
522 pub fn is_virtual(&self) -> bool {
524 self.is_virtual
525 }
526
527 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
528 self.cache.clone()
529 }
530
531 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
532 self.resolved_modifiers
533 }
534
535 pub fn modifier_capabilities(&self) -> NodeCapabilities {
536 self.modifier_capabilities
537 }
538
539 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
540 self.modifier_child_capabilities
541 }
542
543 pub fn set_debug_modifiers(&mut self, enabled: bool) {
544 self.debug_modifiers.set(enabled);
545 self.modifier_chain.set_debug_logging(enabled);
546 }
547
548 pub fn debug_modifiers_enabled(&self) -> bool {
549 self.debug_modifiers.get()
550 }
551
552 pub fn modifier_locals_handle(&self) -> ModifierLocalsHandle {
553 self.modifier_chain.modifier_locals_handle()
554 }
555
556 pub fn has_layout_modifier_nodes(&self) -> bool {
557 self.modifier_capabilities
558 .contains(NodeCapabilities::LAYOUT)
559 }
560
561 pub fn has_draw_modifier_nodes(&self) -> bool {
562 self.modifier_capabilities.contains(NodeCapabilities::DRAW)
563 }
564
565 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
566 self.modifier_capabilities
567 .contains(NodeCapabilities::POINTER_INPUT)
568 }
569
570 pub fn has_semantics_modifier_nodes(&self) -> bool {
571 self.modifier_capabilities
572 .contains(NodeCapabilities::SEMANTICS)
573 }
574
575 pub fn has_focus_modifier_nodes(&self) -> bool {
576 self.modifier_capabilities.contains(NodeCapabilities::FOCUS)
577 }
578
579 fn refresh_registry_state(&self) {
580 if let Some(id) = self.id.get() {
581 let parent = self.parent();
582 let capabilities = self.modifier_child_capabilities();
583 let modifier_locals = self.modifier_locals_handle();
584 LAYOUT_NODE_REGISTRY.with(|registry| {
585 if let Some(entry) = registry.borrow_mut().get_mut(&id) {
586 entry.parent = parent;
587 entry.modifier_child_capabilities = capabilities;
588 entry.modifier_locals = modifier_locals;
589 }
590 });
591 }
592 }
593
594 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
595 use crate::modifier::collect_modifier_slices_into;
596
597 let mut buffer = self.modifier_slices_buffer.borrow_mut();
598 collect_modifier_slices_into(self.modifier_chain.chain(), &mut buffer);
599 let snapshot = Rc::new(buffer.clone());
600 *self.modifier_slices_snapshot.borrow_mut() = snapshot.clone();
601 snapshot
602 }
603
604 pub fn layout_state(&self) -> LayoutState {
610 self.layout_state.borrow().clone()
611 }
612
613 pub fn measured_size(&self) -> Size {
615 self.layout_state.borrow().size
616 }
617
618 pub fn position(&self) -> Point {
620 self.layout_state.borrow().position
621 }
622
623 pub fn is_placed(&self) -> bool {
625 self.layout_state.borrow().is_placed
626 }
627
628 pub fn set_measured_size(&self, size: Size) {
630 let mut state = self.layout_state.borrow_mut();
631 state.size = size;
632 }
633
634 pub fn set_position(&self, position: Point) {
636 let mut state = self.layout_state.borrow_mut();
637 state.position = position;
638 state.is_placed = true;
639 }
640
641 pub fn set_measurement_constraints(&self, constraints: Constraints) {
643 self.layout_state.borrow_mut().measurement_constraints = constraints;
644 }
645
646 pub fn set_content_offset(&self, offset: Point) {
648 self.layout_state.borrow_mut().content_offset = offset;
649 }
650
651 pub fn clear_placed(&self) {
653 self.layout_state.borrow_mut().is_placed = false;
654 }
655
656 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
657 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
658 }
659
660 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
662 &self.modifier_chain
663 }
664
665 pub fn with_text_field_modifier_mut<R>(
670 &mut self,
671 f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
672 ) -> Option<R> {
673 self.modifier_chain.with_text_field_modifier_mut(f)
674 }
675
676 pub fn layout_state_handle(&self) -> Rc<RefCell<LayoutState>> {
679 self.layout_state.clone()
680 }
681}
682impl Clone for LayoutNode {
683 fn clone(&self) -> Self {
684 let mut node = Self {
685 modifier: self.modifier.clone(),
686 modifier_chain: ModifierChainHandle::new(),
687 resolved_modifiers: ResolvedModifiers::default(),
688 modifier_capabilities: self.modifier_capabilities,
689 modifier_child_capabilities: self.modifier_child_capabilities,
690 measure_policy: self.measure_policy.clone(),
691 children: self.children.clone(),
692 cache: self.cache.clone(),
693 needs_measure: Cell::new(self.needs_measure.get()),
694 needs_layout: Cell::new(self.needs_layout.get()),
695 needs_semantics: Cell::new(self.needs_semantics.get()),
696 needs_redraw: Cell::new(self.needs_redraw.get()),
697 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
698 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
699 parent: Cell::new(self.parent.get()),
700 folded_parent: Cell::new(self.folded_parent.get()),
701 id: Cell::new(None),
702 debug_modifiers: Cell::new(self.debug_modifiers.get()),
703 is_virtual: self.is_virtual,
704 virtual_children_count: Cell::new(self.virtual_children_count.get()),
705 modifier_slices_buffer: RefCell::new(ModifierNodeSlices::default()),
706 modifier_slices_snapshot: RefCell::new(Rc::default()),
707 layout_state: self.layout_state.clone(),
709 };
710 node.sync_modifier_chain();
711 node
712 }
713}
714
715impl Node for LayoutNode {
716 fn mount(&mut self) {
717 let (chain, mut context) = self.modifier_chain.chain_and_context_mut();
718 chain.repair_chain();
719 chain.attach_nodes(&mut *context);
720 }
721
722 fn unmount(&mut self) {
723 self.modifier_chain.chain_mut().detach_nodes();
724 }
725
726 fn set_node_id(&mut self, id: NodeId) {
727 LayoutNode::set_node_id(self, id);
729 }
730
731 fn insert_child(&mut self, child: NodeId) {
732 if self.children.contains(&child) {
733 return;
734 }
735 if is_virtual_node(child) {
736 let count = self.virtual_children_count.get();
737 self.virtual_children_count.set(count + 1);
738 }
739 self.children.push(child);
740 self.cache.clear();
741 self.mark_needs_measure();
742 }
743
744 fn remove_child(&mut self, child: NodeId) {
745 let before = self.children.len();
746 self.children.retain(|&id| id != child);
747 if self.children.len() < before {
748 if is_virtual_node(child) {
749 let count = self.virtual_children_count.get();
750 if count > 0 {
751 self.virtual_children_count.set(count - 1);
752 }
753 }
754 self.cache.clear();
755 self.mark_needs_measure();
756 }
757 }
758
759 fn move_child(&mut self, from: usize, to: usize) {
760 if from == to || from >= self.children.len() {
761 return;
762 }
763 let child = self.children.remove(from);
764 let target = to.min(self.children.len());
765 self.children.insert(target, child);
766 self.cache.clear();
767 self.mark_needs_measure();
768 }
769
770 fn update_children(&mut self, children: &[NodeId]) {
771 self.children.clear();
772 self.children.extend_from_slice(children);
773 self.cache.clear();
774 self.mark_needs_measure();
775 }
776
777 fn children(&self) -> Vec<NodeId> {
778 self.children.clone()
779 }
780
781 fn on_attached_to_parent(&mut self, parent: NodeId) {
782 self.set_parent(parent);
783 }
784
785 fn on_removed_from_parent(&mut self) {
786 self.clear_parent();
787 }
788
789 fn parent(&self) -> Option<NodeId> {
790 self.parent.get()
791 }
792
793 fn mark_needs_layout(&self) {
794 self.needs_layout.set(true);
795 }
796
797 fn needs_layout(&self) -> bool {
798 self.needs_layout.get()
799 }
800
801 fn mark_needs_measure(&self) {
802 self.needs_measure.set(true);
803 self.needs_layout.set(true);
804 }
805
806 fn needs_measure(&self) -> bool {
807 self.needs_measure.get()
808 }
809
810 fn mark_needs_semantics(&self) {
811 self.needs_semantics.set(true);
812 }
813
814 fn needs_semantics(&self) -> bool {
815 self.needs_semantics.get()
816 }
817
818 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
823 self.parent.set(Some(parent));
824 }
825}
826
827impl Drop for LayoutNode {
828 fn drop(&mut self) {
829 if let Some(id) = self.id.get() {
830 unregister_layout_node(id);
831 }
832 }
833}
834
835thread_local! {
836 static LAYOUT_NODE_REGISTRY: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>> =
837 RefCell::new(HashMap::new());
838 static VIRTUAL_NODE_ID_COUNTER: std::sync::atomic::AtomicUsize = const { std::sync::atomic::AtomicUsize::new(0xC0000000) };
842}
843
844struct LayoutNodeRegistryEntry {
845 parent: Option<NodeId>,
846 modifier_child_capabilities: NodeCapabilities,
847 modifier_locals: ModifierLocalsHandle,
848 is_virtual: bool,
849}
850
851pub(crate) fn register_layout_node(id: NodeId, node: &LayoutNode) {
852 LAYOUT_NODE_REGISTRY.with(|registry| {
853 registry.borrow_mut().insert(
854 id,
855 LayoutNodeRegistryEntry {
856 parent: node.parent(),
857 modifier_child_capabilities: node.modifier_child_capabilities(),
858 modifier_locals: node.modifier_locals_handle(),
859 is_virtual: node.is_virtual(),
860 },
861 );
862 });
863}
864
865pub(crate) fn unregister_layout_node(id: NodeId) {
866 LAYOUT_NODE_REGISTRY.with(|registry| {
867 registry.borrow_mut().remove(&id);
868 });
869}
870
871pub(crate) fn is_virtual_node(id: NodeId) -> bool {
872 LAYOUT_NODE_REGISTRY.with(|registry| {
873 registry
874 .borrow()
875 .get(&id)
876 .map(|entry| entry.is_virtual)
877 .unwrap_or(false)
878 })
879}
880
881pub(crate) fn allocate_virtual_node_id() -> NodeId {
882 use std::sync::atomic::Ordering;
883 VIRTUAL_NODE_ID_COUNTER.with(|counter| counter.fetch_add(1, Ordering::Relaxed))
886}
887
888fn resolve_modifier_local_from_parent_chain(
889 start: Option<NodeId>,
890 token: ModifierLocalToken,
891) -> Option<ResolvedModifierLocal> {
892 let mut current = start;
893 while let Some(parent_id) = current {
894 let (next_parent, resolved) = LAYOUT_NODE_REGISTRY.with(|registry| {
895 let registry = registry.borrow();
896 if let Some(entry) = registry.get(&parent_id) {
897 let resolved = if entry
898 .modifier_child_capabilities
899 .contains(NodeCapabilities::MODIFIER_LOCALS)
900 {
901 entry
902 .modifier_locals
903 .borrow()
904 .resolve(token)
905 .map(|value| value.with_source(ModifierLocalSource::Ancestor))
906 } else {
907 None
908 };
909 (entry.parent, resolved)
910 } else {
911 (None, None)
912 }
913 });
914 if let Some(value) = resolved {
915 return Some(value);
916 }
917 current = next_parent;
918 }
919 None
920}
921
922#[cfg(test)]
923mod tests {
924 use super::*;
925 use cranpose_ui_graphics::Size as GeometrySize;
926 use cranpose_ui_layout::{Measurable, MeasureResult};
927 use std::rc::Rc;
928
929 #[derive(Default)]
930 struct TestMeasurePolicy;
931
932 impl MeasurePolicy for TestMeasurePolicy {
933 fn measure(
934 &self,
935 _measurables: &[Box<dyn Measurable>],
936 _constraints: Constraints,
937 ) -> MeasureResult {
938 MeasureResult::new(
939 GeometrySize {
940 width: 0.0,
941 height: 0.0,
942 },
943 Vec::new(),
944 )
945 }
946
947 fn min_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
948 0.0
949 }
950
951 fn max_intrinsic_width(&self, _measurables: &[Box<dyn Measurable>], _height: f32) -> f32 {
952 0.0
953 }
954
955 fn min_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
956 0.0
957 }
958
959 fn max_intrinsic_height(&self, _measurables: &[Box<dyn Measurable>], _width: f32) -> f32 {
960 0.0
961 }
962 }
963
964 fn fresh_node() -> LayoutNode {
965 LayoutNode::new(Modifier::empty(), Rc::new(TestMeasurePolicy))
966 }
967
968 fn invalidation(kind: InvalidationKind) -> ModifierInvalidation {
969 ModifierInvalidation::new(kind, NodeCapabilities::for_invalidation(kind))
970 }
971
972 #[test]
973 fn layout_invalidation_requires_layout_capability() {
974 let mut node = fresh_node();
975 node.clear_needs_measure();
976 node.clear_needs_layout();
977 node.modifier_capabilities = NodeCapabilities::DRAW;
978 node.modifier_child_capabilities = node.modifier_capabilities;
979
980 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
981
982 assert!(!node.needs_measure());
983 assert!(!node.needs_layout());
984 }
985
986 #[test]
987 fn semantics_configuration_reflects_modifier_state() {
988 let mut node = fresh_node();
989 node.set_modifier(Modifier::empty().semantics(|config| {
990 config.content_description = Some("greeting".into());
991 config.is_clickable = true;
992 }));
993
994 let config = node
995 .semantics_configuration()
996 .expect("expected semantics configuration");
997 assert_eq!(config.content_description.as_deref(), Some("greeting"));
998 assert!(config.is_clickable);
999 }
1000
1001 #[test]
1002 fn layout_invalidation_marks_flags_when_capability_present() {
1003 let mut node = fresh_node();
1004 node.clear_needs_measure();
1005 node.clear_needs_layout();
1006 node.modifier_capabilities = NodeCapabilities::LAYOUT;
1007 node.modifier_child_capabilities = node.modifier_capabilities;
1008
1009 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Layout)]);
1010
1011 assert!(node.needs_measure());
1012 assert!(node.needs_layout());
1013 }
1014
1015 #[test]
1016 fn draw_invalidation_marks_redraw_flag_when_capable() {
1017 let mut node = fresh_node();
1018 node.clear_needs_measure();
1019 node.clear_needs_layout();
1020 node.modifier_capabilities = NodeCapabilities::DRAW;
1021 node.modifier_child_capabilities = node.modifier_capabilities;
1022
1023 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Draw)]);
1024
1025 assert!(node.needs_redraw());
1026 assert!(!node.needs_layout());
1027 }
1028
1029 #[test]
1030 fn semantics_invalidation_sets_semantics_flag_only() {
1031 let mut node = fresh_node();
1032 node.clear_needs_measure();
1033 node.clear_needs_layout();
1034 node.clear_needs_semantics();
1035 node.modifier_capabilities = NodeCapabilities::SEMANTICS;
1036 node.modifier_child_capabilities = node.modifier_capabilities;
1037
1038 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Semantics)]);
1039
1040 assert!(node.needs_semantics());
1041 assert!(!node.needs_measure());
1042 assert!(!node.needs_layout());
1043 }
1044
1045 #[test]
1046 fn pointer_invalidation_requires_pointer_capability() {
1047 let mut node = fresh_node();
1048 node.clear_needs_pointer_pass();
1049 node.modifier_capabilities = NodeCapabilities::DRAW;
1050 node.modifier_child_capabilities = node.modifier_capabilities;
1051 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::PointerInput)]);
1056
1057 assert!(!node.needs_pointer_pass());
1058 }
1059
1060 #[test]
1061 fn pointer_invalidation_marks_flag_and_requests_queue() {
1062 let mut node = fresh_node();
1063 node.clear_needs_pointer_pass();
1064 node.modifier_capabilities = NodeCapabilities::POINTER_INPUT;
1065 node.modifier_child_capabilities = node.modifier_capabilities;
1066 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::PointerInput)]);
1071
1072 assert!(node.needs_pointer_pass());
1073 }
1074
1075 #[test]
1076 fn focus_invalidation_requires_focus_capability() {
1077 let mut node = fresh_node();
1078 node.clear_needs_focus_sync();
1079 node.modifier_capabilities = NodeCapabilities::DRAW;
1080 node.modifier_child_capabilities = node.modifier_capabilities;
1081 crate::take_focus_invalidation();
1082
1083 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Focus)]);
1084
1085 assert!(!node.needs_focus_sync());
1086 assert!(!crate::take_focus_invalidation());
1087 }
1088
1089 #[test]
1090 fn focus_invalidation_marks_flag_and_requests_queue() {
1091 let mut node = fresh_node();
1092 node.clear_needs_focus_sync();
1093 node.modifier_capabilities = NodeCapabilities::FOCUS;
1094 node.modifier_child_capabilities = node.modifier_capabilities;
1095 crate::take_focus_invalidation();
1096
1097 node.dispatch_modifier_invalidations(&[invalidation(InvalidationKind::Focus)]);
1098
1099 assert!(node.needs_focus_sync());
1100 assert!(crate::take_focus_invalidation());
1101 }
1102
1103 #[test]
1104 fn set_modifier_marks_semantics_dirty() {
1105 let mut node = fresh_node();
1106 node.clear_needs_semantics();
1107 node.set_modifier(Modifier::empty().semantics(|config| {
1108 config.is_clickable = true;
1109 }));
1110
1111 assert!(node.needs_semantics());
1112 }
1113
1114 #[test]
1115 fn modifier_child_capabilities_reflect_chain_head() {
1116 let mut node = fresh_node();
1117 node.set_modifier(Modifier::empty().padding(4.0));
1118 assert!(
1119 node.modifier_child_capabilities()
1120 .contains(NodeCapabilities::LAYOUT),
1121 "padding should introduce layout capability"
1122 );
1123 }
1124}