1use crate::core::{Point, Size};
5use crate::pane_grid::{
6 Axis, Configuration, Direction, Edge, Node, Pane, Region, Split, Target,
7};
8
9use std::borrow::Cow;
10use std::collections::BTreeMap;
11
12#[derive(Debug, Clone)]
25pub struct State<T> {
26 pub panes: BTreeMap<Pane, T>,
30
31 pub internal: Internal,
35}
36
37impl<T> State<T> {
38 pub fn new(first_pane_state: T) -> (Self, Pane) {
43 (
44 Self::with_configuration(Configuration::Pane(first_pane_state)),
45 Pane(0),
46 )
47 }
48
49 pub fn with_configuration(config: impl Into<Configuration<T>>) -> Self {
51 let mut panes = BTreeMap::default();
52
53 let internal =
54 Internal::from_configuration(&mut panes, config.into(), 0);
55
56 State { panes, internal }
57 }
58
59 pub fn len(&self) -> usize {
61 self.panes.len()
62 }
63
64 pub fn is_empty(&self) -> bool {
66 self.len() == 0
67 }
68
69 pub fn get(&self, pane: Pane) -> Option<&T> {
71 self.panes.get(&pane)
72 }
73
74 pub fn get_mut(&mut self, pane: Pane) -> Option<&mut T> {
77 self.panes.get_mut(&pane)
78 }
79
80 pub fn iter(&self) -> impl Iterator<Item = (&Pane, &T)> {
83 self.panes.iter()
84 }
85
86 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Pane, &mut T)> {
89 self.panes.iter_mut()
90 }
91
92 pub fn layout(&self) -> &Node {
94 &self.internal.layout
95 }
96
97 pub fn adjacent(&self, pane: Pane, direction: Direction) -> Option<Pane> {
100 let regions = self.internal.layout.pane_regions(
101 0.0,
102 0.0,
103 Size::new(4096.0, 4096.0),
104 );
105
106 let current_region = regions.get(&pane)?;
107
108 let target = match direction {
109 Direction::Left => {
110 Point::new(current_region.x - 1.0, current_region.y + 1.0)
111 }
112 Direction::Right => Point::new(
113 current_region.x + current_region.width + 1.0,
114 current_region.y + 1.0,
115 ),
116 Direction::Up => {
117 Point::new(current_region.x + 1.0, current_region.y - 1.0)
118 }
119 Direction::Down => Point::new(
120 current_region.x + 1.0,
121 current_region.y + current_region.height + 1.0,
122 ),
123 };
124
125 let mut colliding_regions =
126 regions.iter().filter(|(_, region)| region.contains(target));
127
128 let (pane, _) = colliding_regions.next()?;
129
130 Some(*pane)
131 }
132
133 pub fn split(
136 &mut self,
137 axis: Axis,
138 pane: Pane,
139 state: T,
140 ) -> Option<(Pane, Split)> {
141 self.split_node(axis, Some(pane), state, false)
142 }
143
144 pub fn split_with(&mut self, target: Pane, pane: Pane, region: Region) {
148 match region {
149 Region::Center => self.swap(pane, target),
150 Region::Edge(edge) => match edge {
151 Edge::Top => {
152 self.split_and_swap(Axis::Horizontal, target, pane, true);
153 }
154 Edge::Bottom => {
155 self.split_and_swap(Axis::Horizontal, target, pane, false);
156 }
157 Edge::Left => {
158 self.split_and_swap(Axis::Vertical, target, pane, true);
159 }
160 Edge::Right => {
161 self.split_and_swap(Axis::Vertical, target, pane, false);
162 }
163 },
164 }
165 }
166
167 pub fn drop(&mut self, pane: Pane, target: Target) {
169 match target {
170 Target::Edge(edge) => self.move_to_edge(pane, edge),
171 Target::Pane(target, region) => {
172 self.split_with(target, pane, region);
173 }
174 }
175 }
176
177 fn split_node(
178 &mut self,
179 axis: Axis,
180 pane: Option<Pane>,
181 state: T,
182 inverse: bool,
183 ) -> Option<(Pane, Split)> {
184 let node = if let Some(pane) = pane {
185 self.internal.layout.find(pane)?
186 } else {
187 &mut self.internal.layout
189 };
190
191 let new_pane = {
192 self.internal.last_id = self.internal.last_id.checked_add(1)?;
193
194 Pane(self.internal.last_id)
195 };
196
197 let new_split = {
198 self.internal.last_id = self.internal.last_id.checked_add(1)?;
199
200 Split(self.internal.last_id)
201 };
202
203 if inverse {
204 node.split_inverse(new_split, axis, new_pane);
205 } else {
206 node.split(new_split, axis, new_pane);
207 }
208
209 let _ = self.panes.insert(new_pane, state);
210 let _ = self.internal.maximized.take();
211
212 Some((new_pane, new_split))
213 }
214
215 fn split_and_swap(
216 &mut self,
217 axis: Axis,
218 target: Pane,
219 pane: Pane,
220 swap: bool,
221 ) {
222 if let Some((state, _)) = self.close(pane) {
223 if let Some((new_pane, _)) = self.split(axis, target, state) {
224 self.relabel(new_pane, pane);
226
227 if swap {
228 self.swap(target, pane);
229 }
230 }
231 }
232 }
233
234 pub fn move_to_edge(&mut self, pane: Pane, edge: Edge) {
238 match edge {
239 Edge::Top => {
240 self.split_major_node_and_swap(Axis::Horizontal, pane, true);
241 }
242 Edge::Bottom => {
243 self.split_major_node_and_swap(Axis::Horizontal, pane, false);
244 }
245 Edge::Left => {
246 self.split_major_node_and_swap(Axis::Vertical, pane, true);
247 }
248 Edge::Right => {
249 self.split_major_node_and_swap(Axis::Vertical, pane, false);
250 }
251 }
252 }
253
254 fn split_major_node_and_swap(
255 &mut self,
256 axis: Axis,
257 pane: Pane,
258 inverse: bool,
259 ) {
260 if let Some((state, _)) = self.close(pane) {
261 if let Some((new_pane, _)) =
262 self.split_node(axis, None, state, inverse)
263 {
264 self.relabel(new_pane, pane);
266 }
267 }
268 }
269
270 fn relabel(&mut self, target: Pane, label: Pane) {
271 self.swap(target, label);
272
273 let _ = self
274 .panes
275 .remove(&target)
276 .and_then(|state| self.panes.insert(label, state));
277 }
278
279 pub fn swap(&mut self, a: Pane, b: Pane) {
287 self.internal.layout.update(&|node| match node {
288 Node::Split { .. } => {}
289 Node::Pane(pane) => {
290 if *pane == a {
291 *node = Node::Pane(b);
292 } else if *pane == b {
293 *node = Node::Pane(a);
294 }
295 }
296 });
297 }
298
299 pub fn resize(&mut self, split: Split, ratio: f32) {
310 let _ = self.internal.layout.resize(split, ratio);
311 }
312
313 pub fn close(&mut self, pane: Pane) -> Option<(T, Pane)> {
316 if self.internal.maximized == Some(pane) {
317 let _ = self.internal.maximized.take();
318 }
319
320 if let Some(sibling) = self.internal.layout.remove(pane) {
321 self.panes.remove(&pane).map(|state| (state, sibling))
322 } else {
323 None
324 }
325 }
326
327 pub fn maximize(&mut self, pane: Pane) {
332 self.internal.maximized = Some(pane);
333 }
334
335 pub fn restore(&mut self) {
340 let _ = self.internal.maximized.take();
341 }
342
343 pub fn maximized(&self) -> Option<Pane> {
347 self.internal.maximized
348 }
349}
350
351#[derive(Debug, Clone)]
355pub struct Internal {
356 layout: Node,
357 last_id: usize,
358 maximized: Option<Pane>,
359}
360
361impl Internal {
362 pub fn from_configuration<T>(
367 panes: &mut BTreeMap<Pane, T>,
368 content: Configuration<T>,
369 next_id: usize,
370 ) -> Self {
371 let (layout, last_id) = match content {
372 Configuration::Split { axis, ratio, a, b } => {
373 let Internal {
374 layout: a,
375 last_id: next_id,
376 ..
377 } = Self::from_configuration(panes, *a, next_id);
378
379 let Internal {
380 layout: b,
381 last_id: next_id,
382 ..
383 } = Self::from_configuration(panes, *b, next_id);
384
385 (
386 Node::Split {
387 id: Split(next_id),
388 axis,
389 ratio,
390 a: Box::new(a),
391 b: Box::new(b),
392 },
393 next_id + 1,
394 )
395 }
396 Configuration::Pane(state) => {
397 let id = Pane(next_id);
398 let _ = panes.insert(id, state);
399
400 (Node::Pane(id), next_id + 1)
401 }
402 };
403
404 Self {
405 layout,
406 last_id,
407 maximized: None,
408 }
409 }
410
411 pub(super) fn layout(&self) -> Cow<'_, Node> {
412 match self.maximized {
413 Some(pane) => Cow::Owned(Node::Pane(pane)),
414 None => Cow::Borrowed(&self.layout),
415 }
416 }
417
418 pub(super) fn maximized(&self) -> Option<Pane> {
419 self.maximized
420 }
421}
422
423#[derive(Debug, Clone, Copy, PartialEq, Default)]
427pub enum Action {
428 #[default]
432 Idle,
433 Dragging {
437 pane: Pane,
439 origin: Point,
441 },
442 Resizing {
446 split: Split,
448 axis: Axis,
450 },
451}
452
453impl Action {
454 pub fn picked_pane(&self) -> Option<(Pane, Point)> {
456 match *self {
457 Action::Dragging { pane, origin, .. } => Some((pane, origin)),
458 _ => None,
459 }
460 }
461
462 pub fn picked_split(&self) -> Option<(Split, Axis)> {
464 match *self {
465 Action::Resizing { split, axis, .. } => Some((split, axis)),
466 _ => None,
467 }
468 }
469}