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
101 .internal
102 .layout
103 .pane_regions(0.0, Size::new(4096.0, 4096.0));
104
105 let current_region = regions.get(&pane)?;
106
107 let target = match direction {
108 Direction::Left => {
109 Point::new(current_region.x - 1.0, current_region.y + 1.0)
110 }
111 Direction::Right => Point::new(
112 current_region.x + current_region.width + 1.0,
113 current_region.y + 1.0,
114 ),
115 Direction::Up => {
116 Point::new(current_region.x + 1.0, current_region.y - 1.0)
117 }
118 Direction::Down => Point::new(
119 current_region.x + 1.0,
120 current_region.y + current_region.height + 1.0,
121 ),
122 };
123
124 let mut colliding_regions =
125 regions.iter().filter(|(_, region)| region.contains(target));
126
127 let (pane, _) = colliding_regions.next()?;
128
129 Some(*pane)
130 }
131
132 pub fn split(
135 &mut self,
136 axis: Axis,
137 pane: Pane,
138 state: T,
139 ) -> Option<(Pane, Split)> {
140 self.split_node(axis, Some(pane), state, false)
141 }
142
143 pub fn split_with(&mut self, target: Pane, pane: Pane, region: Region) {
147 match region {
148 Region::Center => self.swap(pane, target),
149 Region::Edge(edge) => match edge {
150 Edge::Top => {
151 self.split_and_swap(Axis::Horizontal, target, pane, true);
152 }
153 Edge::Bottom => {
154 self.split_and_swap(Axis::Horizontal, target, pane, false);
155 }
156 Edge::Left => {
157 self.split_and_swap(Axis::Vertical, target, pane, true);
158 }
159 Edge::Right => {
160 self.split_and_swap(Axis::Vertical, target, pane, false);
161 }
162 },
163 }
164 }
165
166 pub fn drop(&mut self, pane: Pane, target: Target) {
168 match target {
169 Target::Edge(edge) => self.move_to_edge(pane, edge),
170 Target::Pane(target, region) => {
171 self.split_with(target, pane, region);
172 }
173 }
174 }
175
176 fn split_node(
177 &mut self,
178 axis: Axis,
179 pane: Option<Pane>,
180 state: T,
181 inverse: bool,
182 ) -> Option<(Pane, Split)> {
183 let node = if let Some(pane) = pane {
184 self.internal.layout.find(pane)?
185 } else {
186 &mut self.internal.layout
188 };
189
190 let new_pane = {
191 self.internal.last_id = self.internal.last_id.checked_add(1)?;
192
193 Pane(self.internal.last_id)
194 };
195
196 let new_split = {
197 self.internal.last_id = self.internal.last_id.checked_add(1)?;
198
199 Split(self.internal.last_id)
200 };
201
202 if inverse {
203 node.split_inverse(new_split, axis, new_pane);
204 } else {
205 node.split(new_split, axis, new_pane);
206 }
207
208 let _ = self.panes.insert(new_pane, state);
209 let _ = self.internal.maximized.take();
210
211 Some((new_pane, new_split))
212 }
213
214 fn split_and_swap(
215 &mut self,
216 axis: Axis,
217 target: Pane,
218 pane: Pane,
219 swap: bool,
220 ) {
221 if let Some((state, _)) = self.close(pane) {
222 if let Some((new_pane, _)) = self.split(axis, target, state) {
223 self.relabel(new_pane, pane);
225
226 if swap {
227 self.swap(target, pane);
228 }
229 }
230 }
231 }
232
233 pub fn move_to_edge(&mut self, pane: Pane, edge: Edge) {
237 match edge {
238 Edge::Top => {
239 self.split_major_node_and_swap(Axis::Horizontal, pane, true);
240 }
241 Edge::Bottom => {
242 self.split_major_node_and_swap(Axis::Horizontal, pane, false);
243 }
244 Edge::Left => {
245 self.split_major_node_and_swap(Axis::Vertical, pane, true);
246 }
247 Edge::Right => {
248 self.split_major_node_and_swap(Axis::Vertical, pane, false);
249 }
250 }
251 }
252
253 fn split_major_node_and_swap(
254 &mut self,
255 axis: Axis,
256 pane: Pane,
257 inverse: bool,
258 ) {
259 if let Some((state, _)) = self.close(pane) {
260 if let Some((new_pane, _)) =
261 self.split_node(axis, None, state, inverse)
262 {
263 self.relabel(new_pane, pane);
265 }
266 }
267 }
268
269 fn relabel(&mut self, target: Pane, label: Pane) {
270 self.swap(target, label);
271
272 let _ = self
273 .panes
274 .remove(&target)
275 .and_then(|state| self.panes.insert(label, state));
276 }
277
278 pub fn swap(&mut self, a: Pane, b: Pane) {
286 self.internal.layout.update(&|node| match node {
287 Node::Split { .. } => {}
288 Node::Pane(pane) => {
289 if *pane == a {
290 *node = Node::Pane(b);
291 } else if *pane == b {
292 *node = Node::Pane(a);
293 }
294 }
295 });
296 }
297
298 pub fn resize(&mut self, split: Split, ratio: f32) {
309 let _ = self.internal.layout.resize(split, ratio);
310 }
311
312 pub fn close(&mut self, pane: Pane) -> Option<(T, Pane)> {
315 if self.internal.maximized == Some(pane) {
316 let _ = self.internal.maximized.take();
317 }
318
319 if let Some(sibling) = self.internal.layout.remove(pane) {
320 self.panes.remove(&pane).map(|state| (state, sibling))
321 } else {
322 None
323 }
324 }
325
326 pub fn maximize(&mut self, pane: Pane) {
331 self.internal.maximized = Some(pane);
332 }
333
334 pub fn restore(&mut self) {
339 let _ = self.internal.maximized.take();
340 }
341
342 pub fn maximized(&self) -> Option<Pane> {
346 self.internal.maximized
347 }
348}
349
350#[derive(Debug, Clone)]
354pub struct Internal {
355 layout: Node,
356 last_id: usize,
357 maximized: Option<Pane>,
358}
359
360impl Internal {
361 pub fn from_configuration<T>(
366 panes: &mut BTreeMap<Pane, T>,
367 content: Configuration<T>,
368 next_id: usize,
369 ) -> Self {
370 let (layout, last_id) = match content {
371 Configuration::Split { axis, ratio, a, b } => {
372 let Internal {
373 layout: a,
374 last_id: next_id,
375 ..
376 } = Self::from_configuration(panes, *a, next_id);
377
378 let Internal {
379 layout: b,
380 last_id: next_id,
381 ..
382 } = Self::from_configuration(panes, *b, next_id);
383
384 (
385 Node::Split {
386 id: Split(next_id),
387 axis,
388 ratio,
389 a: Box::new(a),
390 b: Box::new(b),
391 },
392 next_id + 1,
393 )
394 }
395 Configuration::Pane(state) => {
396 let id = Pane(next_id);
397 let _ = panes.insert(id, state);
398
399 (Node::Pane(id), next_id + 1)
400 }
401 };
402
403 Self {
404 layout,
405 last_id,
406 maximized: None,
407 }
408 }
409
410 pub(super) fn layout(&self) -> Cow<'_, Node> {
411 match self.maximized {
412 Some(pane) => Cow::Owned(Node::Pane(pane)),
413 None => Cow::Borrowed(&self.layout),
414 }
415 }
416
417 pub(super) fn maximized(&self) -> Option<Pane> {
418 self.maximized
419 }
420}
421
422#[derive(Debug, Clone, Copy, PartialEq, Default)]
426pub enum Action {
427 #[default]
431 Idle,
432 Dragging {
436 pane: Pane,
438 origin: Point,
440 },
441 Resizing {
445 split: Split,
447 axis: Axis,
449 },
450}
451
452impl Action {
453 pub fn picked_pane(&self) -> Option<(Pane, Point)> {
455 match *self {
456 Action::Dragging { pane, origin, .. } => Some((pane, origin)),
457 _ => None,
458 }
459 }
460
461 pub fn picked_split(&self) -> Option<(Split, Axis)> {
463 match *self {
464 Action::Resizing { split, axis, .. } => Some((split, axis)),
465 _ => None,
466 }
467 }
468}