Skip to main content

repose_tree/
node.rs

1//! Tree node definitions.
2
3use repose_core::{Modifier, Rect, ViewId, ViewKind};
4use slotmap::new_key_type;
5use smallvec::SmallVec;
6
7new_key_type! {
8 /// Internal node identifier, stable within a tree's lifetime.
9 pub struct NodeId;
10}
11
12/// A node in the persistent view tree.
13#[derive(Clone)]
14pub struct TreeNode {
15    /// Internal slotmap key.
16    pub id: NodeId,
17
18    /// User-facing stable view ID (for hit testing, focus, etc.).
19    pub view_id: ViewId,
20
21    /// The kind of view this node represents.
22    pub kind: ViewKind,
23
24    /// Layout and styling modifiers.
25    pub modifier: Modifier,
26
27    /// Child node IDs.
28    pub children: SmallVec<[NodeId; 4]>,
29
30    /// Parent node ID (None for root).
31    pub parent: Option<NodeId>,
32
33    /// Hash of this node's content (kind + modifier + children structure).
34    /// Used for change detection.
35    pub content_hash: u64,
36
37    /// Hash including all descendants.
38    /// If this matches, the entire subtree is unchanged.
39    pub subtree_hash: u64,
40
41    /// Cached layout result.
42    pub layout_cache: Option<LayoutCache>,
43
44    /// Generation when this node was last updated.
45    pub generation: u64,
46
47    /// Key provided by user for stable identity in dynamic lists.
48    pub user_key: Option<u64>,
49
50    /// Depth in tree (root = 0).
51    pub depth: u32,
52}
53
54impl TreeNode {
55    /// Create a new tree node.
56    pub fn new(
57        id: NodeId,
58        view_id: ViewId,
59        kind: ViewKind,
60        modifier: Modifier,
61        generation: u64,
62    ) -> Self {
63        Self {
64            id,
65            view_id,
66            kind,
67            modifier,
68            children: SmallVec::new(),
69            parent: None,
70            content_hash: 0,
71            subtree_hash: 0,
72            layout_cache: None,
73            generation,
74            user_key: None,
75            depth: 0,
76        }
77    }
78
79    /// Check if this node's layout cache is still valid.
80    pub fn has_valid_layout(&self) -> bool {
81        self.layout_cache.is_some()
82    }
83
84    /// Invalidate layout cache (called when content changes).
85    pub fn invalidate_layout(&mut self) {
86        self.layout_cache = None;
87    }
88
89    /// Get cached layout if available.
90    pub fn cached_rect(&self) -> Option<Rect> {
91        self.layout_cache.as_ref().map(|c| c.rect)
92    }
93
94    pub fn set_layout(&mut self, rect: Rect) {
95        self.layout_cache = Some(LayoutCache {
96            rect,
97            // constraints and screen_rect are less critical for basic paint
98            screen_rect: Rect::default(),
99            constraints: LayoutConstraints::default(),
100            generation: self.generation,
101        });
102    }
103}
104
105/// Cached layout information for a node.
106#[derive(Clone, Debug)]
107pub struct LayoutCache {
108    /// The computed rectangle in parent coordinates.
109    pub rect: Rect,
110
111    /// The computed rectangle in screen coordinates.
112    pub screen_rect: Rect,
113
114    /// Size constraints used when computing this layout.
115    pub constraints: LayoutConstraints,
116
117    /// Generation when this cache was computed.
118    pub generation: u64,
119}
120
121/// Constraints used during layout.
122#[derive(Clone, Debug, PartialEq)]
123pub struct LayoutConstraints {
124    pub min_width: f32,
125    pub max_width: f32,
126    pub min_height: f32,
127    pub max_height: f32,
128}
129
130impl Default for LayoutConstraints {
131    fn default() -> Self {
132        Self {
133            min_width: 0.0,
134            max_width: f32::INFINITY,
135            min_height: 0.0,
136            max_height: f32::INFINITY,
137        }
138    }
139}
140
141impl LayoutConstraints {
142    pub fn fixed(width: f32, height: f32) -> Self {
143        Self {
144            min_width: width,
145            max_width: width,
146            min_height: height,
147            max_height: height,
148        }
149    }
150
151    pub fn tight(size: (f32, f32)) -> Self {
152        Self::fixed(size.0, size.1)
153    }
154}
155
156/// Statistics about tree operations.
157#[derive(Clone, Debug, Default)]
158pub struct TreeStats {
159    /// Total nodes in tree.
160    pub total_nodes: usize,
161
162    /// Nodes marked dirty this frame.
163    pub dirty_nodes: usize,
164
165    /// Nodes that were reconciled (updated).
166    pub reconciled_nodes: usize,
167
168    /// Nodes that were skipped (unchanged).
169    pub skipped_nodes: usize,
170
171    /// Nodes that were created.
172    pub created_nodes: usize,
173
174    /// Nodes that were removed.
175    pub removed_nodes: usize,
176
177    /// Layout cache hits.
178    pub layout_cache_hits: usize,
179
180    /// Layout cache misses.
181    pub layout_cache_misses: usize,
182}