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 semantics_reach: Cell<Option<cranpose_foundation::SemanticsReach>>,
287 needs_redraw: Cell<bool>,
288 needs_pointer_pass: Cell<bool>,
289 needs_focus_sync: Cell<bool>,
290 parent: Cell<Option<NodeId>>,
291 folded_parent: Cell<Option<NodeId>>,
292 id: Cell<Option<NodeId>>,
293 owner_context_id: Cell<Option<crate::render_state::AppContextId>>,
294 debug_modifiers: Cell<bool>,
295 is_virtual: bool,
296 virtual_children_count: Cell<usize>,
297
298 modifier_slices_snapshot: RefCell<Rc<ModifierNodeSlices>>,
299 modifier_slices_dirty: Cell<bool>,
300
301 layout_state: Rc<RefCell<LayoutState>>,
302 layout_runtime_state: Rc<RefCell<LayoutRuntimeState>>,
303}
304
305pub(crate) const RECYCLED_LAYOUT_NODE_POOL_LIMIT: usize = 128;
306
307thread_local! {
308 static EMPTY_MEASURE_POLICY: Rc<dyn MeasurePolicy> =
309 Rc::new(crate::layout::policies::EmptyMeasurePolicy);
310}
311
312fn empty_measure_policy() -> Rc<dyn MeasurePolicy> {
313 EMPTY_MEASURE_POLICY.with(Rc::clone)
314}
315
316impl LayoutNode {
317 pub fn new(modifier: Modifier, measure_policy: Rc<dyn MeasurePolicy>) -> Self {
318 Self::new_with_virtual(modifier, measure_policy, false)
319 }
320
321 pub fn new_virtual() -> Self {
324 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), true)
325 }
326
327 fn new_recycled_shell(is_virtual: bool) -> Self {
328 let mut shell =
329 Self::new_with_virtual(Modifier::empty(), empty_measure_policy(), is_virtual);
330 shell.needs_measure.set(false);
331 shell.needs_layout.set(false);
332 shell.needs_semantics.set(false);
333 shell.needs_redraw.set(false);
334 shell.needs_pointer_pass.set(false);
335 shell.needs_focus_sync.set(false);
336 shell.parent.set(None);
337 shell.folded_parent.set(None);
338 shell.id.set(None);
339 shell.owner_context_id.set(None);
340 shell.debug_modifiers.set(false);
341 shell.virtual_children_count.set(0);
342 shell.cache = LayoutNodeCacheHandles::default();
343 shell.modifier_slices_snapshot = RefCell::new(Rc::default());
344 shell.modifier_slices_dirty = Cell::new(true);
345 shell.layout_state = Rc::new(RefCell::new(LayoutState::default()));
346 shell.layout_runtime_state = Rc::new(RefCell::new(LayoutRuntimeState::default()));
347 shell
348 }
349
350 fn new_with_virtual(
351 modifier: Modifier,
352 measure_policy: Rc<dyn MeasurePolicy>,
353 is_virtual: bool,
354 ) -> Self {
355 let mut node = Self {
356 #[cfg(feature = "inspection")]
357 source_trace: cranpose_core::source_trace::current_source_trace(),
358 modifier,
359 modifier_chain: ModifierChainHandle::new(),
360 resolved_modifiers: ResolvedModifiers::default(),
361 modifier_capabilities: NodeCapabilities::default(),
362 modifier_child_capabilities: NodeCapabilities::default(),
363 measure_policy,
364 density: crate::density::Density::default(),
365 children: Vec::new(),
366 cache: LayoutNodeCacheHandles::default(),
367 needs_measure: Cell::new(true),
368 needs_layout: Cell::new(true),
369 needs_semantics: Cell::new(true),
370 semantics_reach: Cell::new(None),
371 needs_redraw: Cell::new(true),
372 needs_pointer_pass: Cell::new(false),
373 needs_focus_sync: Cell::new(false),
374 parent: Cell::new(None),
375 folded_parent: Cell::new(None),
376 id: Cell::new(None),
377 owner_context_id: Cell::new(None),
378 debug_modifiers: Cell::new(false),
379 is_virtual,
380 virtual_children_count: Cell::new(0),
381 modifier_slices_snapshot: RefCell::new(Rc::default()),
382 modifier_slices_dirty: Cell::new(true),
383 layout_state: Rc::new(RefCell::new(LayoutState::default())),
384 layout_runtime_state: Rc::new(RefCell::new(LayoutRuntimeState::default())),
385 };
386 node.sync_modifier_chain();
387 node
388 }
389
390 pub fn set_modifier(&mut self, modifier: Modifier) {
391 #[cfg(feature = "inspection")]
392 {
393 let trace = cranpose_core::source_trace::current_source_trace();
394 if !trace.is_empty() {
395 self.source_trace = trace;
396 }
397 }
398 if self.modifier == modifier
402 && !self
403 .modifier_capabilities
404 .contains(NodeCapabilities::MODIFIER_LOCALS)
405 {
406 return;
407 }
408 let modifier_changed = !self.modifier.structural_eq(&modifier);
409 self.modifier = modifier;
410 self.sync_modifier_chain();
411 if modifier_changed {
412 self.cache.clear();
413 self.request_semantics_update();
414 }
415 }
416
417 fn sync_modifier_chain(&mut self) {
418 let prev_caps = self.modifier_capabilities;
419 let start_parent = self.parent();
420 let mut resolver = move |token: &ModifierLocalToken| {
421 resolve_modifier_local_from_parent_chain(start_parent, token)
422 };
423 self.modifier_chain
424 .set_debug_logging(self.debug_modifiers.get());
425 self.modifier_chain.set_node_id(self.id.get());
426 let modifier_local_invalidations = self
427 .modifier_chain
428 .update_with_resolver(&self.modifier, &mut resolver);
429 self.resolved_modifiers = self.modifier_chain.resolved_modifiers();
430 self.modifier_capabilities = self.modifier_chain.capabilities();
431 self.modifier_child_capabilities = self.modifier_chain.aggregate_child_capabilities();
432 self.semantics_reach.set(None);
433 self.modifier_slices_dirty.set(true);
434
435 let mut invalidations = self.modifier_chain.take_invalidations();
436 invalidations.extend(modifier_local_invalidations);
437 self.dispatch_modifier_invalidations_with_prev(&invalidations, prev_caps);
438 self.refresh_registry_state();
439 }
440
441 fn update_modifier_slices_cache(&self) {
442 let mut snapshot = self.modifier_slices_snapshot.borrow_mut();
443 crate::modifier::collect_modifier_slices_into_shared(
444 self.modifier_chain.chain(),
445 &mut snapshot,
446 );
447 self.modifier_slices_dirty.set(false);
448 }
449
450 pub(crate) fn mark_modifier_slices_dirty(&self) {
451 self.modifier_slices_dirty.set(true);
452 }
453
454 #[cfg(test)]
455 fn dispatch_modifier_invalidations(&self, invalidations: &[ModifierInvalidation]) {
456 self.dispatch_modifier_invalidations_with_prev(invalidations, NodeCapabilities::empty());
457 }
458
459 fn dispatch_modifier_invalidations_with_prev(
460 &self,
461 invalidations: &[ModifierInvalidation],
462 prev_caps: NodeCapabilities,
463 ) {
464 let curr_caps = self.modifier_capabilities;
465 for invalidation in invalidations {
466 self.modifier_slices_dirty.set(true);
467 let has_capability =
468 |capability| curr_caps.contains(capability) || prev_caps.contains(capability);
469 match invalidation.kind() {
470 InvalidationKind::Layout => {
471 if has_capability(NodeCapabilities::LAYOUT) {
472 self.mark_needs_measure();
473 if let Some(id) = self.id.get() {
474 log_layout_invalidation_dispatch(
475 id,
476 invalidation,
477 curr_caps,
478 prev_caps,
479 &self.modifier,
480 );
481 let inside_composition =
482 cranpose_core::composer_context::try_with_composer(|_| ())
483 .is_some();
484 if inside_composition {
485 cranpose_core::bubble_measure_dirty_in_composer(id);
486 } else {
487 crate::schedule_layout_repass(id);
488 }
489 }
490 }
491 }
492 InvalidationKind::Draw => {
493 if has_capability(NodeCapabilities::DRAW)
494 || invalidation.capabilities().contains(NodeCapabilities::DRAW)
495 {
496 self.mark_needs_redraw();
497 }
498 }
499 InvalidationKind::PointerInput => {
500 if has_capability(NodeCapabilities::POINTER_INPUT) {
501 self.mark_needs_pointer_pass();
502 crate::request_pointer_invalidation();
503 if let Some(id) = self.id.get() {
504 crate::schedule_pointer_repass(id);
505 }
506 }
507 }
508 InvalidationKind::Semantics => {
509 self.request_semantics_update();
510 }
511 InvalidationKind::Focus => {
512 if has_capability(NodeCapabilities::FOCUS) {
513 self.mark_needs_focus_sync();
514 crate::request_focus_invalidation();
515 if let Some(id) = self.id.get() {
516 crate::schedule_focus_invalidation(id);
517 }
518 }
519 }
520 }
521 }
522 }
523
524 pub fn density(&self) -> crate::density::Density {
526 self.density
527 }
528
529 pub fn set_density(&mut self, density: crate::density::Density) {
531 if self.density != density {
532 self.density = density;
533 self.cache.clear();
534 self.mark_needs_measure();
535 }
536 }
537
538 pub fn set_measure_policy(&mut self, policy: Rc<dyn MeasurePolicy>) {
539 if !Rc::ptr_eq(&self.measure_policy, &policy) {
540 self.measure_policy = policy;
541 self.cache.clear();
542 self.mark_needs_measure();
543 if let Some(id) = self.id.get() {
544 cranpose_core::bubble_measure_dirty_in_composer(id);
545 }
546 }
547 }
548
549 pub fn mark_needs_measure(&self) {
551 self.needs_measure.set(true);
552 self.needs_layout.set(true);
553 }
554
555 pub fn mark_needs_layout(&self) {
557 self.needs_layout.set(true);
558 }
559
560 pub fn mark_needs_redraw(&self) {
562 self.needs_redraw.set(true);
563 if let Some(id) = self.id.get() {
564 crate::schedule_draw_repass(id);
565 }
566 crate::request_render_invalidation();
567 }
568
569 pub fn needs_measure(&self) -> bool {
571 self.needs_measure.get()
572 }
573
574 pub fn needs_layout(&self) -> bool {
576 self.needs_layout.get()
577 }
578
579 pub fn mark_needs_semantics(&self) {
581 self.needs_semantics.set(true);
582 self.semantics_reach.set(None);
583 }
584
585 pub(crate) fn clear_needs_semantics(&self) {
586 self.needs_semantics.set(false);
587 }
588
589 pub fn needs_semantics(&self) -> bool {
591 self.needs_semantics.get()
592 }
593
594 pub fn needs_redraw(&self) -> bool {
596 self.needs_redraw.get()
597 }
598
599 pub fn clear_needs_redraw(&self) {
600 self.needs_redraw.set(false);
601 }
602
603 fn request_semantics_update(&self) {
604 let already_dirty = self.needs_semantics.replace(true);
605 if already_dirty {
606 return;
607 }
608
609 if let Some(id) = self.id.get() {
610 cranpose_core::queue_semantics_invalidation(id);
611 }
612 }
613
614 pub(crate) fn clear_needs_measure(&self) {
615 self.needs_measure.set(false);
616 }
617
618 pub(crate) fn clear_needs_layout(&self) {
619 self.needs_layout.set(false);
620 }
621
622 pub fn mark_needs_pointer_pass(&self) {
624 self.needs_pointer_pass.set(true);
625 }
626
627 pub fn needs_pointer_pass(&self) -> bool {
629 self.needs_pointer_pass.get()
630 }
631
632 pub fn clear_needs_pointer_pass(&self) {
634 self.needs_pointer_pass.set(false);
635 }
636
637 pub fn mark_needs_focus_sync(&self) {
639 self.needs_focus_sync.set(true);
640 }
641
642 pub fn needs_focus_sync(&self) -> bool {
644 self.needs_focus_sync.get()
645 }
646
647 pub fn clear_needs_focus_sync(&self) {
649 self.needs_focus_sync.set(false);
650 }
651
652 pub fn set_node_id(&mut self, id: NodeId) {
654 if let Some(existing) = self.id.replace(Some(id))
655 && let Some(owner_context_id) = self.owner_context_id.take()
656 {
657 unregister_layout_node(owner_context_id, existing);
658 }
659 self.layout_state.borrow_mut().set_node_id(id);
660 let owner_context_id = register_layout_node(id, self);
661 self.owner_context_id.set(Some(owner_context_id));
662 self.refresh_registry_state();
663
664 self.modifier_chain.set_node_id(Some(id));
665 let invalidations = self.modifier_chain.take_invalidations();
666 self.dispatch_modifier_invalidations_with_prev(&invalidations, NodeCapabilities::empty());
667 self.modifier_slices_dirty.set(true);
669 }
670
671 pub fn node_id(&self) -> Option<NodeId> {
673 self.id.get()
674 }
675
676 pub fn set_parent(&self, parent: NodeId) {
679 self.folded_parent.set(Some(parent));
680 self.parent.set(Some(parent));
681 self.refresh_registry_state();
682 }
683
684 pub fn clear_parent(&self) {
686 self.folded_parent.set(None);
687 self.parent.set(None);
688 self.refresh_registry_state();
689 }
690
691 pub fn parent(&self) -> Option<NodeId> {
693 self.parent.get()
694 }
695
696 pub fn folded_parent(&self) -> Option<NodeId> {
698 self.folded_parent.get()
699 }
700
701 pub fn is_virtual(&self) -> bool {
703 self.is_virtual
704 }
705
706 pub(crate) fn cache_handles(&self) -> LayoutNodeCacheHandles {
707 self.cache.clone()
708 }
709
710 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
711 self.resolved_modifiers
712 }
713
714 pub fn modifier_capabilities(&self) -> NodeCapabilities {
715 self.modifier_capabilities
716 }
717
718 pub fn is_window_root(&self) -> bool {
722 self.modifier_capabilities
723 .contains(NodeCapabilities::WINDOW_ROOT)
724 }
725
726 pub fn modifier_child_capabilities(&self) -> NodeCapabilities {
727 self.modifier_child_capabilities
728 }
729
730 pub fn set_debug_modifiers(&mut self, enabled: bool) {
731 self.debug_modifiers.set(enabled);
732 self.modifier_chain.set_debug_logging(enabled);
733 }
734
735 pub fn debug_modifiers_enabled(&self) -> bool {
736 self.debug_modifiers.get()
737 }
738
739 pub fn modifier_locals_handle(&self) -> ModifierLocalsHandle {
740 self.modifier_chain.modifier_locals_handle()
741 }
742
743 pub fn has_layout_modifier_nodes(&self) -> bool {
744 self.modifier_capabilities
745 .contains(NodeCapabilities::LAYOUT)
746 }
747
748 pub fn has_draw_modifier_nodes(&self) -> bool {
749 self.modifier_capabilities.contains(NodeCapabilities::DRAW)
750 }
751
752 pub fn has_pointer_input_modifier_nodes(&self) -> bool {
753 self.modifier_capabilities
754 .contains(NodeCapabilities::POINTER_INPUT)
755 }
756
757 pub fn has_semantics_modifier_nodes(&self) -> bool {
758 self.modifier_capabilities
759 .contains(NodeCapabilities::SEMANTICS)
760 }
761
762 pub fn has_focus_modifier_nodes(&self) -> bool {
763 self.modifier_capabilities.contains(NodeCapabilities::FOCUS)
764 }
765
766 fn refresh_registry_state(&self) {
767 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
768 let parent = self.parent();
769 let capabilities = self.modifier_child_capabilities();
770 let modifier_locals = self.modifier_locals_handle();
771 let _ = crate::render_state::with_layout_node_registry_by_app_context(
772 owner_context_id,
773 |registry| {
774 registry.update_entry(id, parent, capabilities, modifier_locals);
775 },
776 );
777 }
778 }
779
780 pub fn modifier_slices_snapshot(&self) -> Rc<ModifierNodeSlices> {
781 if self.modifier_slices_dirty.get() {
782 self.update_modifier_slices_cache();
783 }
784 self.modifier_slices_snapshot.borrow().clone()
785 }
786
787 pub fn layout_state(&self) -> LayoutState {
789 self.layout_state.borrow().clone()
790 }
791
792 pub fn measured_size(&self) -> Size {
794 self.layout_state.borrow().size
795 }
796
797 pub fn position(&self) -> Point {
799 self.layout_state.borrow().position
800 }
801
802 pub fn is_placed(&self) -> bool {
804 self.layout_state.borrow().is_placed
805 }
806
807 pub fn set_measured_size(&self, size: Size) {
811 self.layout_state.borrow_mut().set_size(size);
812 }
813
814 pub fn set_position(&self, position: Point) {
817 self.layout_state.borrow_mut().place(position);
818 }
819
820 pub fn set_content_offset(&self, offset: Point) {
822 self.layout_state.borrow_mut().content_offset = offset;
823 }
824
825 pub fn clear_placed(&self) {
827 self.layout_state.borrow_mut().is_placed = false;
828 }
829
830 pub fn semantics_configuration(&self) -> Option<SemanticsConfiguration> {
831 crate::modifier::collect_semantics_from_chain(self.modifier_chain.chain())
832 }
833
834 pub fn semantics_reach(&self) -> cranpose_foundation::SemanticsReach {
836 if let Some(reach) = self.semantics_reach.get() {
837 return reach;
838 }
839 let reach = crate::modifier::semantics_reach_of_chain(self.modifier_chain.chain());
840 self.semantics_reach.set(Some(reach));
841 reach
842 }
843
844 pub(crate) fn modifier_chain(&self) -> &ModifierChainHandle {
845 &self.modifier_chain
846 }
847
848 pub fn with_text_field_modifier_mut<R>(
853 &mut self,
854 f: impl FnMut(&mut crate::TextFieldModifierNode) -> R,
855 ) -> Option<R> {
856 self.modifier_chain.with_text_field_modifier_mut(f)
857 }
858
859 pub fn layout_state_handle(&self) -> Rc<RefCell<LayoutState>> {
862 self.layout_state.clone()
863 }
864
865 pub(crate) fn layout_runtime_state_handle(&self) -> Rc<RefCell<LayoutRuntimeState>> {
866 self.layout_runtime_state.clone()
867 }
868
869 #[cfg(test)]
870 pub(crate) fn layout_runtime_debug_stats(&self) -> LayoutRuntimeDebugStats {
871 self.layout_runtime_state.borrow().debug_stats()
872 }
873}
874impl Clone for LayoutNode {
875 fn clone(&self) -> Self {
876 let mut node = Self {
877 #[cfg(feature = "inspection")]
878 source_trace: self.source_trace.clone(),
879 modifier: self.modifier.clone(),
880 modifier_chain: ModifierChainHandle::new(),
881 resolved_modifiers: ResolvedModifiers::default(),
882 modifier_capabilities: self.modifier_capabilities,
883 modifier_child_capabilities: self.modifier_child_capabilities,
884 measure_policy: self.measure_policy.clone(),
885 density: self.density,
886 children: self.children.clone(),
887 cache: self.cache.clone(),
888 needs_measure: Cell::new(self.needs_measure.get()),
889 needs_layout: Cell::new(self.needs_layout.get()),
890 needs_semantics: Cell::new(self.needs_semantics.get()),
891 semantics_reach: Cell::new(None),
892 needs_redraw: Cell::new(self.needs_redraw.get()),
893 needs_pointer_pass: Cell::new(self.needs_pointer_pass.get()),
894 needs_focus_sync: Cell::new(self.needs_focus_sync.get()),
895 parent: Cell::new(self.parent.get()),
896 folded_parent: Cell::new(self.folded_parent.get()),
897 id: Cell::new(None),
898 owner_context_id: Cell::new(None),
899 debug_modifiers: Cell::new(self.debug_modifiers.get()),
900 is_virtual: self.is_virtual,
901 virtual_children_count: Cell::new(self.virtual_children_count.get()),
902 modifier_slices_snapshot: RefCell::new(Rc::default()),
903 modifier_slices_dirty: Cell::new(true),
904 layout_state: self.layout_state.clone(),
905 layout_runtime_state: self.layout_runtime_state.clone(),
906 };
907 node.sync_modifier_chain();
908 node
909 }
910}
911
912impl Node for LayoutNode {
913 fn mount(&mut self) {
914 let (chain, mut context) = self.modifier_chain.chain_and_context_mut();
915 chain.repair_chain();
916 chain.attach_nodes(&mut *context);
917 }
918
919 fn unmount(&mut self) {
920 self.modifier_chain.chain_mut().detach_nodes();
921 }
922
923 fn set_node_id(&mut self, id: NodeId) {
924 LayoutNode::set_node_id(self, id);
925 }
926
927 fn insert_child(&mut self, child: NodeId) -> bool {
928 if self.children.contains(&child) {
929 return false;
930 }
931 if is_virtual_node(child) {
932 let count = self.virtual_children_count.get();
933 self.virtual_children_count.set(count + 1);
934 }
935 self.children.push(child);
936 self.cache.clear();
937 self.mark_needs_measure();
938 true
939 }
940
941 fn remove_child(&mut self, child: NodeId) -> bool {
942 let before = self.children.len();
943 self.children.retain(|&id| id != child);
944 let removed = self.children.len() < before;
945 if removed {
946 if is_virtual_node(child) {
947 let count = self.virtual_children_count.get();
948 if count > 0 {
949 self.virtual_children_count.set(count - 1);
950 }
951 }
952 self.cache.clear();
953 self.mark_needs_measure();
954 }
955 removed
956 }
957
958 fn move_child(&mut self, from: usize, to: usize) {
959 if from == to || from >= self.children.len() {
960 return;
961 }
962 let child = self.children.remove(from);
963 let target = to.min(self.children.len());
964 self.children.insert(target, child);
965 self.cache.clear();
966 self.mark_needs_measure();
967 }
968
969 fn update_children(&mut self, children: &[NodeId]) {
970 self.children.clear();
971 self.children.extend_from_slice(children);
972 self.cache.clear();
973 self.mark_needs_measure();
974 }
975
976 fn collect_children_into(&self, out: &mut smallvec::SmallVec<[NodeId; 8]>) {
977 out.clear();
978 out.extend_from_slice(&self.children);
979 }
980
981 fn on_attached_to_parent(&mut self, parent: NodeId) {
982 self.set_parent(parent);
983 }
984
985 fn on_removed_from_parent(&mut self) {
986 self.clear_parent();
987 }
988
989 fn parent(&self) -> Option<NodeId> {
990 self.parent.get()
991 }
992
993 fn mark_needs_layout(&self) {
994 self.needs_layout.set(true);
995 }
996
997 fn needs_layout(&self) -> bool {
998 self.needs_layout.get()
999 }
1000
1001 fn mark_needs_measure(&self) {
1002 self.needs_measure.set(true);
1003 self.needs_layout.set(true);
1004 }
1005
1006 fn needs_measure(&self) -> bool {
1007 self.needs_measure.get()
1008 }
1009
1010 fn mark_needs_semantics(&self) {
1011 self.needs_semantics.set(true);
1012 self.semantics_reach.set(None);
1013 }
1014
1015 fn needs_semantics(&self) -> bool {
1016 self.needs_semantics.get()
1017 }
1018
1019 fn set_parent_for_bubbling(&mut self, parent: NodeId) {
1020 if self.parent.get().is_none() {
1021 self.parent.set(Some(parent));
1022 }
1023 }
1024
1025 fn recycle_key(&self) -> Option<TypeId> {
1026 Some(TypeId::of::<Self>())
1027 }
1028
1029 fn recycle_pool_limit(&self) -> Option<usize> {
1030 Some(RECYCLED_LAYOUT_NODE_POOL_LIMIT)
1031 }
1032
1033 fn prepare_for_recycle(&mut self) {
1034 *self = Self::new_recycled_shell(self.is_virtual);
1035 }
1036
1037 fn rehouse_for_recycle(&self) -> Option<Box<dyn cranpose_core::Node>> {
1038 Some(Box::new(Self::new_recycled_shell(self.is_virtual)))
1039 }
1040
1041 fn rehouse_for_live_compaction(&mut self) -> Option<Box<dyn cranpose_core::Node>> {
1042 let mut previous = std::mem::replace(self, Self::new_recycled_shell(self.is_virtual));
1043 let node_id = previous.id.replace(None);
1044 let parent = previous.parent.get();
1045 let folded_parent = previous.folded_parent.get();
1046 let debug_modifiers = previous.debug_modifiers.get();
1047 let needs_measure = previous.needs_measure.get();
1048 let needs_layout = previous.needs_layout.get();
1049 let needs_semantics = previous.needs_semantics.get();
1050 let needs_redraw = previous.needs_redraw.get();
1051 let needs_pointer_pass = previous.needs_pointer_pass.get();
1052 let needs_focus_sync = previous.needs_focus_sync.get();
1053 let virtual_children_count = previous.virtual_children_count.get();
1054 let children = previous.children.to_vec();
1055 let modifier = previous.modifier.rehouse_for_live_compaction();
1056 let measure_policy = previous.measure_policy.clone();
1057 let layout_state = previous.layout_state.clone();
1058 let layout_runtime_state = previous.layout_runtime_state.clone();
1059
1060 previous.modifier_chain.chain_mut().detach_nodes();
1061
1062 let mut compact = Self::new_with_virtual(modifier, measure_policy, previous.is_virtual);
1063 compact.children = children;
1064 #[cfg(feature = "inspection")]
1065 {
1066 compact.source_trace = previous.source_trace.clone();
1067 }
1068 compact.parent.set(parent);
1069 compact.folded_parent.set(folded_parent);
1070 compact.id.set(node_id);
1071 compact.debug_modifiers.set(debug_modifiers);
1072 compact.needs_measure.set(needs_measure);
1073 compact.needs_layout.set(needs_layout);
1074 compact.needs_semantics.set(needs_semantics);
1075 compact.needs_redraw.set(needs_redraw);
1076 compact.needs_pointer_pass.set(needs_pointer_pass);
1077 compact.needs_focus_sync.set(needs_focus_sync);
1078 compact.virtual_children_count.set(virtual_children_count);
1079 compact.layout_state = layout_state;
1080 compact.layout_runtime_state = layout_runtime_state;
1081 compact.sync_modifier_chain();
1082 if let Some(id) = node_id {
1083 let owner_context_id = register_layout_node(id, &compact);
1084 compact.owner_context_id.set(Some(owner_context_id));
1085 }
1086
1087 Some(Box::new(compact))
1088 }
1089}
1090
1091impl Drop for LayoutNode {
1092 fn drop(&mut self) {
1093 if let (Some(id), Some(owner_context_id)) = (self.id.get(), self.owner_context_id.get()) {
1094 unregister_layout_node(owner_context_id, id);
1095 }
1096 }
1097}
1098
1099const MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY: usize = 128;
1100const VIRTUAL_NODE_ID_START: NodeId = 0xC0000000;
1101
1102#[cfg(test)]
1103#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1104struct LayoutNodeRegistryDebugStats {
1105 len: usize,
1106 capacity: usize,
1107}
1108
1109struct LayoutNodeRegistryEntry {
1110 parent: Option<NodeId>,
1111 modifier_child_capabilities: NodeCapabilities,
1112 modifier_locals: ModifierLocalsHandle,
1113 is_virtual: bool,
1114}
1115
1116pub(crate) struct LayoutNodeRegistryState {
1117 entries: RefCell<HashMap<NodeId, LayoutNodeRegistryEntry>>,
1118 virtual_node_id_counter: Cell<NodeId>,
1119}
1120
1121impl LayoutNodeRegistryState {
1122 pub(crate) fn new() -> Self {
1123 Self {
1124 entries: RefCell::new(HashMap::new()),
1125 virtual_node_id_counter: Cell::new(VIRTUAL_NODE_ID_START),
1126 }
1127 }
1128
1129 fn register(&self, id: NodeId, node: &LayoutNode) {
1130 self.entries.borrow_mut().insert(
1131 id,
1132 LayoutNodeRegistryEntry {
1133 parent: node.parent(),
1134 modifier_child_capabilities: node.modifier_child_capabilities(),
1135 modifier_locals: node.modifier_locals_handle(),
1136 is_virtual: node.is_virtual(),
1137 },
1138 );
1139 }
1140
1141 fn unregister(&self, id: NodeId) {
1142 let mut entries = self.entries.borrow_mut();
1143 entries.remove(&id);
1144 let should_shrink = (entries.len() <= MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY
1145 && entries.capacity() > MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1146 || entries.capacity()
1147 > entries
1148 .len()
1149 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY)
1150 .saturating_mul(4);
1151 if should_shrink {
1152 let retained = entries
1153 .len()
1154 .max(MIN_RETAINED_LAYOUT_NODE_REGISTRY_CAPACITY);
1155 let mut rebuilt = HashMap::new();
1156 rebuilt.reserve(retained);
1157 rebuilt.extend(entries.drain());
1158 *entries = rebuilt;
1159 }
1160 }
1161
1162 fn update_entry(
1163 &self,
1164 id: NodeId,
1165 parent: Option<NodeId>,
1166 modifier_child_capabilities: NodeCapabilities,
1167 modifier_locals: ModifierLocalsHandle,
1168 ) {
1169 if let Some(entry) = self.entries.borrow_mut().get_mut(&id) {
1170 entry.parent = parent;
1171 entry.modifier_child_capabilities = modifier_child_capabilities;
1172 entry.modifier_locals = modifier_locals;
1173 }
1174 }
1175
1176 #[cfg(test)]
1177 fn stats(&self) -> LayoutNodeRegistryDebugStats {
1178 let entries = self.entries.borrow();
1179 LayoutNodeRegistryDebugStats {
1180 len: entries.len(),
1181 capacity: entries.capacity(),
1182 }
1183 }
1184
1185 fn is_virtual_node(&self, id: NodeId) -> bool {
1186 self.entries
1187 .borrow()
1188 .get(&id)
1189 .is_some_and(|entry| entry.is_virtual)
1190 }
1191
1192 fn allocate_virtual_node_id(&self) -> NodeId {
1193 let id = self.virtual_node_id_counter.get();
1194 self.virtual_node_id_counter.set(id.wrapping_add(1));
1195 id
1196 }
1197
1198 fn resolve_modifier_local_from_parent_chain(
1199 &self,
1200 start: Option<NodeId>,
1201 token: &ModifierLocalToken,
1202 ) -> Option<ResolvedModifierLocal> {
1203 let mut current = start;
1204 while let Some(parent_id) = current {
1205 let (next_parent, resolved) = {
1206 let entries = self.entries.borrow();
1207 if let Some(entry) = entries.get(&parent_id) {
1208 let resolved = if entry
1209 .modifier_child_capabilities
1210 .contains(NodeCapabilities::MODIFIER_LOCALS)
1211 {
1212 entry
1213 .modifier_locals
1214 .borrow()
1215 .resolve(token)
1216 .map(|value| value.with_source(ModifierLocalSource::Ancestor))
1217 } else {
1218 None
1219 };
1220 (entry.parent, resolved)
1221 } else {
1222 (None, None)
1223 }
1224 };
1225 if let Some(value) = resolved {
1226 return Some(value);
1227 }
1228 current = next_parent;
1229 }
1230 None
1231 }
1232}
1233
1234pub(crate) fn register_layout_node(
1235 id: NodeId,
1236 node: &LayoutNode,
1237) -> crate::render_state::AppContextId {
1238 let owner_context_id = crate::render_state::current_app_context_id();
1239 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1240 owner_context_id,
1241 |registry| {
1242 registry.register(id, node);
1243 },
1244 );
1245 owner_context_id
1246}
1247
1248pub(crate) fn unregister_layout_node(
1249 owner_context_id: crate::render_state::AppContextId,
1250 id: NodeId,
1251) {
1252 let _ = crate::render_state::with_layout_node_registry_by_app_context(
1253 owner_context_id,
1254 |registry| {
1255 registry.unregister(id);
1256 },
1257 );
1258}
1259
1260#[cfg(test)]
1261fn layout_node_registry_stats() -> LayoutNodeRegistryDebugStats {
1262 crate::render_state::with_layout_node_registry(LayoutNodeRegistryState::stats)
1263}
1264
1265pub(crate) fn is_virtual_node(id: NodeId) -> bool {
1266 crate::render_state::with_layout_node_registry(|registry| registry.is_virtual_node(id))
1267}
1268
1269pub(crate) fn allocate_virtual_node_id() -> NodeId {
1270 crate::render_state::with_layout_node_registry(
1271 LayoutNodeRegistryState::allocate_virtual_node_id,
1272 )
1273}
1274
1275fn resolve_modifier_local_from_parent_chain(
1276 start: Option<NodeId>,
1277 token: &ModifierLocalToken,
1278) -> Option<ResolvedModifierLocal> {
1279 crate::render_state::with_layout_node_registry(|registry| {
1280 registry.resolve_modifier_local_from_parent_chain(start, token)
1281 })
1282}
1283
1284#[cfg(test)]
1285#[path = "tests/layout_node_tests.rs"]
1286mod tests;