1use crate::core::{Rectangle, Size};
2use crate::pane_grid::{Axis, Pane, Split};
3
4use std::collections::BTreeMap;
5
6#[derive(Debug, Clone)]
10pub enum Node {
11 Split {
13 id: Split,
15
16 axis: Axis,
18
19 ratio: f32,
21
22 a: Box<Node>,
24
25 b: Box<Node>,
27 },
28 Pane(Pane),
30}
31
32impl Node {
33 pub fn splits(&self) -> impl Iterator<Item = &Split> {
35 let mut unvisited_nodes = vec![self];
36
37 std::iter::from_fn(move || {
38 while let Some(node) = unvisited_nodes.pop() {
39 if let Node::Split { id, a, b, .. } = node {
40 unvisited_nodes.push(a);
41 unvisited_nodes.push(b);
42
43 return Some(id);
44 }
45 }
46
47 None
48 })
49 }
50
51 pub fn pane_regions(
54 &self,
55 spacing: f32,
56 size: Size,
57 ) -> BTreeMap<Pane, Rectangle> {
58 let mut regions = BTreeMap::new();
59
60 self.compute_regions(
61 spacing,
62 &Rectangle {
63 x: 0.0,
64 y: 0.0,
65 width: size.width,
66 height: size.height,
67 },
68 &mut regions,
69 );
70
71 regions
72 }
73
74 pub fn split_regions(
78 &self,
79 spacing: f32,
80 size: Size,
81 ) -> BTreeMap<Split, (Axis, Rectangle, f32)> {
82 let mut splits = BTreeMap::new();
83
84 self.compute_splits(
85 spacing,
86 &Rectangle {
87 x: 0.0,
88 y: 0.0,
89 width: size.width,
90 height: size.height,
91 },
92 &mut splits,
93 );
94
95 splits
96 }
97
98 pub(crate) fn find(&mut self, pane: Pane) -> Option<&mut Node> {
99 match self {
100 Node::Split { a, b, .. } => {
101 a.find(pane).or_else(move || b.find(pane))
102 }
103 Node::Pane(p) => {
104 if *p == pane {
105 Some(self)
106 } else {
107 None
108 }
109 }
110 }
111 }
112
113 pub(crate) fn split(&mut self, id: Split, axis: Axis, new_pane: Pane) {
114 *self = Node::Split {
115 id,
116 axis,
117 ratio: 0.5,
118 a: Box::new(self.clone()),
119 b: Box::new(Node::Pane(new_pane)),
120 };
121 }
122
123 pub(crate) fn split_inverse(&mut self, id: Split, axis: Axis, pane: Pane) {
124 *self = Node::Split {
125 id,
126 axis,
127 ratio: 0.5,
128 a: Box::new(Node::Pane(pane)),
129 b: Box::new(self.clone()),
130 };
131 }
132
133 pub(crate) fn update(&mut self, f: &impl Fn(&mut Node)) {
134 if let Node::Split { a, b, .. } = self {
135 a.update(f);
136 b.update(f);
137 }
138
139 f(self);
140 }
141
142 pub(crate) fn resize(&mut self, split: Split, percentage: f32) -> bool {
143 match self {
144 Node::Split {
145 id, ratio, a, b, ..
146 } => {
147 if *id == split {
148 *ratio = percentage;
149
150 true
151 } else if a.resize(split, percentage) {
152 true
153 } else {
154 b.resize(split, percentage)
155 }
156 }
157 Node::Pane(_) => false,
158 }
159 }
160
161 pub(crate) fn remove(&mut self, pane: Pane) -> Option<Pane> {
162 match self {
163 Node::Split { a, b, .. } => {
164 if a.pane() == Some(pane) {
165 *self = *b.clone();
166 Some(self.first_pane())
167 } else if b.pane() == Some(pane) {
168 *self = *a.clone();
169 Some(self.first_pane())
170 } else {
171 a.remove(pane).or_else(|| b.remove(pane))
172 }
173 }
174 Node::Pane(_) => None,
175 }
176 }
177
178 fn pane(&self) -> Option<Pane> {
179 match self {
180 Node::Split { .. } => None,
181 Node::Pane(pane) => Some(*pane),
182 }
183 }
184
185 fn first_pane(&self) -> Pane {
186 match self {
187 Node::Split { a, .. } => a.first_pane(),
188 Node::Pane(pane) => *pane,
189 }
190 }
191
192 fn compute_regions(
193 &self,
194 spacing: f32,
195 current: &Rectangle,
196 regions: &mut BTreeMap<Pane, Rectangle>,
197 ) {
198 match self {
199 Node::Split {
200 axis, ratio, a, b, ..
201 } => {
202 let (region_a, region_b) = axis.split(current, *ratio, spacing);
203
204 a.compute_regions(spacing, ®ion_a, regions);
205 b.compute_regions(spacing, ®ion_b, regions);
206 }
207 Node::Pane(pane) => {
208 let _ = regions.insert(*pane, *current);
209 }
210 }
211 }
212
213 fn compute_splits(
214 &self,
215 spacing: f32,
216 current: &Rectangle,
217 splits: &mut BTreeMap<Split, (Axis, Rectangle, f32)>,
218 ) {
219 match self {
220 Node::Split {
221 axis,
222 ratio,
223 a,
224 b,
225 id,
226 } => {
227 let (region_a, region_b) = axis.split(current, *ratio, spacing);
228
229 let _ = splits.insert(*id, (*axis, *current, *ratio));
230
231 a.compute_splits(spacing, ®ion_a, splits);
232 b.compute_splits(spacing, ®ion_b, splits);
233 }
234 Node::Pane(_) => {}
235 }
236 }
237}
238
239impl std::hash::Hash for Node {
240 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
241 match self {
242 Node::Split {
243 id,
244 axis,
245 ratio,
246 a,
247 b,
248 } => {
249 id.hash(state);
250 axis.hash(state);
251 ((ratio * 100_000.0) as u32).hash(state);
252 a.hash(state);
253 b.hash(state);
254 }
255 Node::Pane(pane) => {
256 pane.hash(state);
257 }
258 }
259 }
260}