1use std::sync::atomic::{AtomicU64, Ordering};
2
3use smallvec::SmallVec;
4
5use super::node::{NodeId, NodeKind, PaneNode, PaneRef, PanelId, TilePanel};
6
7#[derive(Clone, Copy, PartialEq, Eq, Debug)]
12pub enum RootKind {
13 Split,
14 Any,
15}
16
17pub(crate) type NodePath = SmallVec<[usize; 8]>;
19
20static NEXT_NODE_ID: AtomicU64 = AtomicU64::new(0);
29
30fn next_node_id() -> NodeId {
31 NodeId::from_u64(NEXT_NODE_ID.fetch_add(1, Ordering::Relaxed))
32}
33
34#[derive(Clone, PartialEq, Debug)]
46pub struct PaneTree {
47 root: PaneNode,
48 root_kind: RootKind,
49}
50
51impl PaneTree {
52 pub fn new(root_kind: RootKind) -> Self {
53 let root = PaneNode::new(
54 next_node_id(),
55 NodeKind::Split {
56 axis: gpui::Axis::Horizontal,
57 children: Vec::new(),
58 sizes: Vec::new(),
59 },
60 );
61 Self { root, root_kind }
62 }
63
64 pub fn root(&self) -> &PaneNode {
65 &self.root
66 }
67
68 pub(crate) fn root_mut(&mut self) -> &mut PaneNode {
70 &mut self.root
71 }
72
73 pub fn root_kind(&self) -> RootKind {
74 self.root_kind
75 }
76
77 pub(crate) fn allocate_node_id(&mut self) -> NodeId {
78 next_node_id()
79 }
80
81 pub fn node_ids(&self) -> Vec<NodeId> {
83 let mut ids = Vec::new();
84 self.root.walk(&mut |node| ids.push(node.id()));
85 ids
86 }
87
88 pub fn panels(&self) -> impl Iterator<Item = PanelId> {
90 let mut found = Vec::new();
91 self.root.walk(&mut |node| match node.kind() {
92 PaneRef::Tabs { panels, .. } => found.extend_from_slice(panels),
93 PaneRef::Tiles { panels } => found.extend(panels.iter().map(TilePanel::panel)),
94 PaneRef::Split { .. } => {}
95 });
96 found.into_iter()
97 }
98
99 pub fn find_node(&self, id: NodeId) -> Option<&PaneNode> {
100 self.path_of_node(id).map(|path| self.node_at(&path))
101 }
102
103 pub fn find_panel_node(&self, panel: PanelId) -> Option<NodeId> {
105 let mut found = None;
106 self.root.walk(&mut |node| {
107 let holds = match node.kind() {
108 PaneRef::Tabs { panels, .. } => panels.contains(&panel),
109 PaneRef::Tiles { panels } => panels.iter().any(|p| p.panel() == panel),
110 PaneRef::Split { .. } => false,
111 };
112 if holds {
113 found = Some(node.id());
114 }
115 });
116 found
117 }
118
119 pub(crate) fn path_of_node(&self, id: NodeId) -> Option<NodePath> {
120 fn search(node: &PaneNode, id: NodeId, path: &mut NodePath) -> bool {
121 if node.id() == id {
122 return true;
123 }
124 if let NodeKind::Split { children, .. } = node.kind_ref() {
125 for (ix, child) in children.iter().enumerate() {
126 path.push(ix);
127 if search(child, id, path) {
128 return true;
129 }
130 path.pop();
131 }
132 }
133 false
134 }
135
136 let mut path = NodePath::new();
137 search(&self.root, id, &mut path).then_some(path)
138 }
139
140 pub(crate) fn node_at(&self, path: &NodePath) -> &PaneNode {
141 let mut node = &self.root;
142 for ix in path {
143 let NodeKind::Split { children, .. } = node.kind_ref() else {
144 unreachable!("path traverses a non-split node");
145 };
146 node = &children[*ix];
147 }
148 node
149 }
150
151 pub(crate) fn node_at_mut(&mut self, path: &NodePath) -> &mut PaneNode {
152 let mut node = &mut self.root;
153 for ix in path {
154 let NodeKind::Split { children, .. } = node.kind_mut() else {
155 unreachable!("path traverses a non-split node");
156 };
157 node = &mut children[*ix];
158 }
159 node
160 }
161
162 pub(crate) fn replace_root(&mut self, node: PaneNode) {
165 self.root = node;
166 }
167}
168
169#[cfg(test)]
170use gpui::{Axis, Pixels};
171
172#[cfg(test)]
173impl PaneTree {
174 pub(crate) fn push_tabs_for_test(&mut self, parent: NodeId, panels: Vec<PanelId>) -> NodeId {
175 let id = self.allocate_node_id();
176 let path = self.path_of_node(parent).expect("parent must exist");
177 let NodeKind::Split {
178 children, sizes, ..
179 } = self.node_at_mut(&path).kind_mut()
180 else {
181 panic!("parent must be a split");
182 };
183 children.push(PaneNode::new(
184 id,
185 NodeKind::Tabs {
186 panels,
187 active_ix: 0,
188 },
189 ));
190 sizes.push(None);
191 id
192 }
193
194 pub(crate) fn push_sized_tabs_for_test(
199 &mut self,
200 parent: NodeId,
201 panels: Vec<PanelId>,
202 size: Option<Pixels>,
203 ) -> NodeId {
204 let id = self.allocate_node_id();
205 let path = self.path_of_node(parent).expect("parent must exist");
206 let NodeKind::Split {
207 children, sizes, ..
208 } = self.node_at_mut(&path).kind_mut()
209 else {
210 panic!("parent must be a split");
211 };
212 children.push(PaneNode::new(
213 id,
214 NodeKind::Tabs {
215 panels,
216 active_ix: 0,
217 },
218 ));
219 sizes.push(size);
220 id
221 }
222
223 pub(crate) fn set_root_tiles_for_test(&mut self, panels: Vec<TilePanel>) -> NodeId {
224 let id = self.allocate_node_id();
225 self.root = PaneNode::new(id, NodeKind::Tiles { panels });
226 id
227 }
228
229 pub(crate) fn set_root_split_for_test(&mut self, axis: Axis) -> NodeId {
230 let id = self.allocate_node_id();
231 self.root = PaneNode::new(
232 id,
233 NodeKind::Split {
234 axis,
235 children: Vec::new(),
236 sizes: Vec::new(),
237 },
238 );
239 id
240 }
241
242 pub(crate) fn set_root_axis_for_test(&mut self, new_axis: Axis) {
243 if let NodeKind::Split { axis, .. } = self.root.kind_mut() {
244 *axis = new_axis;
245 }
246 }
247
248 pub(crate) fn set_root_tabs_for_test(
249 &mut self,
250 panels: Vec<PanelId>,
251 active_ix: usize,
252 ) -> NodeId {
253 let id = self.allocate_node_id();
254 self.root = PaneNode::new(id, NodeKind::Tabs { panels, active_ix });
255 id
256 }
257
258 pub(crate) fn push_split_for_test(
259 &mut self,
260 parent: NodeId,
261 axis: Axis,
262 size: Option<Pixels>,
263 ) -> NodeId {
264 let id = self.allocate_node_id();
265 let path = self.path_of_node(parent).expect("parent must exist");
266 let NodeKind::Split {
267 children, sizes, ..
268 } = self.node_at_mut(&path).kind_mut()
269 else {
270 panic!("parent must be a split");
271 };
272 children.push(PaneNode::new(
273 id,
274 NodeKind::Split {
275 axis,
276 children: Vec::new(),
277 sizes: Vec::new(),
278 },
279 ));
280 sizes.push(size);
281 id
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use gpui::{Bounds, point, px, size};
288
289 use super::*;
290
291 fn panel_id(n: u64) -> PanelId {
292 PanelId::from_u64(n)
293 }
294
295 #[test]
296 fn a_fresh_split_root_tree_has_no_panels() {
297 let tree = PaneTree::new(RootKind::Split);
298 assert!(
299 matches!(tree.root().kind(), PaneRef::Split { children, .. } if children.is_empty())
300 );
301 assert_eq!(tree.panels().count(), 0);
302 }
303
304 #[test]
305 fn node_ids_are_unique_and_resolvable() {
306 let mut tree = PaneTree::new(RootKind::Split);
307 let root = tree.root().id();
308 let tabs = tree.push_tabs_for_test(root, vec![panel_id(1)]);
309 assert_ne!(tabs, tree.root().id());
310 assert!(tree.find_node(tabs).is_some());
311 assert_eq!(tree.find_panel_node(panel_id(1)), Some(tabs));
312 }
313
314 #[test]
315 fn tile_panels_carry_bounds_and_z_index() {
316 let mut tree = PaneTree::new(RootKind::Any);
317 let tiles = tree.set_root_tiles_for_test(vec![
318 TilePanel::new(
319 panel_id(7),
320 Bounds {
321 origin: point(px(10.), px(20.)),
322 size: size(px(100.), px(50.)),
323 },
324 )
325 .with_z_index(3),
326 ]);
327 let PaneRef::Tiles { panels } = tree.find_node(tiles).unwrap().kind() else {
328 panic!("expected tiles root");
329 };
330 assert_eq!(panels[0].panel(), panel_id(7));
331 assert_eq!(panels[0].z_index(), 3);
332 assert_eq!(panels[0].bounds().size.width, px(100.));
333 }
334}