1use crate::core::{Point, Size};
5use crate::pane_grid::{Axis, Configuration, Direction, Edge, Node, Pane, Region, Split, Target};
6
7use std::borrow::Cow;
8use std::collections::BTreeMap;
9
10#[derive(Debug, Clone)]
23pub struct State<T> {
24 pub panes: BTreeMap<Pane, T>,
28
29 pub internal: Internal,
33}
34
35impl<T> State<T> {
36 pub fn new(first_pane_state: T) -> (Self, Pane) {
41 (
42 Self::with_configuration(Configuration::Pane(first_pane_state)),
43 Pane(0),
44 )
45 }
46
47 pub fn with_configuration(config: impl Into<Configuration<T>>) -> Self {
49 let mut panes = BTreeMap::default();
50
51 let internal = Internal::from_configuration(&mut panes, config.into(), 0);
52
53 State { panes, internal }
54 }
55
56 pub fn len(&self) -> usize {
58 self.panes.len()
59 }
60
61 pub fn is_empty(&self) -> bool {
63 self.len() == 0
64 }
65
66 pub fn get(&self, pane: Pane) -> Option<&T> {
68 self.panes.get(&pane)
69 }
70
71 pub fn get_mut(&mut self, pane: Pane) -> Option<&mut T> {
74 self.panes.get_mut(&pane)
75 }
76
77 pub fn iter(&self) -> impl Iterator<Item = (&Pane, &T)> {
80 self.panes.iter()
81 }
82
83 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Pane, &mut T)> {
86 self.panes.iter_mut()
87 }
88
89 pub fn layout(&self) -> &Node {
91 &self.internal.layout
92 }
93
94 pub fn adjacent(&self, pane: Pane, direction: Direction) -> Option<Pane> {
97 let regions = self
98 .internal
99 .layout
100 .pane_regions(0.0, 0.0, Size::new(4096.0, 4096.0));
101
102 let current_region = regions.get(&pane)?;
103
104 let target = match direction {
105 Direction::Left => Point::new(current_region.x - 1.0, current_region.y + 1.0),
106 Direction::Right => Point::new(
107 current_region.x + current_region.width + 1.0,
108 current_region.y + 1.0,
109 ),
110 Direction::Up => Point::new(current_region.x + 1.0, current_region.y - 1.0),
111 Direction::Down => Point::new(
112 current_region.x + 1.0,
113 current_region.y + current_region.height + 1.0,
114 ),
115 };
116
117 let mut colliding_regions = regions.iter().filter(|(_, region)| region.contains(target));
118
119 let (pane, _) = colliding_regions.next()?;
120
121 Some(*pane)
122 }
123
124 pub fn split(&mut self, axis: Axis, pane: Pane, state: T) -> Option<(Pane, Split)> {
127 self.split_node(axis, Some(pane), state, false)
128 }
129
130 pub fn split_with(&mut self, target: Pane, pane: Pane, region: Region) {
134 match region {
135 Region::Center => self.swap(pane, target),
136 Region::Edge(edge) => match edge {
137 Edge::Top => {
138 self.split_and_swap(Axis::Horizontal, target, pane, true);
139 }
140 Edge::Bottom => {
141 self.split_and_swap(Axis::Horizontal, target, pane, false);
142 }
143 Edge::Left => {
144 self.split_and_swap(Axis::Vertical, target, pane, true);
145 }
146 Edge::Right => {
147 self.split_and_swap(Axis::Vertical, target, pane, false);
148 }
149 },
150 }
151 }
152
153 pub fn drop(&mut self, pane: Pane, target: Target) {
155 match target {
156 Target::Edge(edge) => self.move_to_edge(pane, edge),
157 Target::Pane(target, region) => {
158 self.split_with(target, pane, region);
159 }
160 }
161 }
162
163 fn split_node(
164 &mut self,
165 axis: Axis,
166 pane: Option<Pane>,
167 state: T,
168 inverse: bool,
169 ) -> Option<(Pane, Split)> {
170 let node = if let Some(pane) = pane {
171 self.internal.layout.find(pane)?
172 } else {
173 &mut self.internal.layout
175 };
176
177 let new_pane = {
178 self.internal.last_id = self.internal.last_id.checked_add(1)?;
179
180 Pane(self.internal.last_id)
181 };
182
183 let new_split = {
184 self.internal.last_id = self.internal.last_id.checked_add(1)?;
185
186 Split(self.internal.last_id)
187 };
188
189 if inverse {
190 node.split_inverse(new_split, axis, new_pane);
191 } else {
192 node.split(new_split, axis, new_pane);
193 }
194
195 let _ = self.panes.insert(new_pane, state);
196 let _ = self.internal.maximized.take();
197
198 Some((new_pane, new_split))
199 }
200
201 fn split_and_swap(&mut self, axis: Axis, target: Pane, pane: Pane, swap: bool) {
202 if let Some((state, _)) = self.close(pane)
203 && let Some((new_pane, _)) = self.split(axis, target, state)
204 {
205 self.relabel(new_pane, pane);
207
208 if swap {
209 self.swap(target, pane);
210 }
211 }
212 }
213
214 pub fn move_to_edge(&mut self, pane: Pane, edge: Edge) {
218 match edge {
219 Edge::Top => {
220 self.split_major_node_and_swap(Axis::Horizontal, pane, true);
221 }
222 Edge::Bottom => {
223 self.split_major_node_and_swap(Axis::Horizontal, pane, false);
224 }
225 Edge::Left => {
226 self.split_major_node_and_swap(Axis::Vertical, pane, true);
227 }
228 Edge::Right => {
229 self.split_major_node_and_swap(Axis::Vertical, pane, false);
230 }
231 }
232 }
233
234 fn split_major_node_and_swap(&mut self, axis: Axis, pane: Pane, inverse: bool) {
235 if let Some((state, _)) = self.close(pane)
236 && let Some((new_pane, _)) = self.split_node(axis, None, state, inverse)
237 {
238 self.relabel(new_pane, pane);
240 }
241 }
242
243 fn relabel(&mut self, target: Pane, label: Pane) {
244 self.swap(target, label);
245
246 let _ = self
247 .panes
248 .remove(&target)
249 .and_then(|state| self.panes.insert(label, state));
250 }
251
252 pub fn swap(&mut self, a: Pane, b: Pane) {
260 self.internal.layout.update(&|node| match node {
261 Node::Split { .. } => {}
262 Node::Pane(pane) => {
263 if *pane == a {
264 *node = Node::Pane(b);
265 } else if *pane == b {
266 *node = Node::Pane(a);
267 }
268 }
269 });
270 }
271
272 pub fn resize(&mut self, split: Split, ratio: f32) {
283 let _ = self.internal.layout.resize(split, ratio);
284 }
285
286 pub fn close(&mut self, pane: Pane) -> Option<(T, Pane)> {
289 if self.internal.maximized == Some(pane) {
290 let _ = self.internal.maximized.take();
291 }
292
293 if let Some(sibling) = self.internal.layout.remove(pane) {
294 self.panes.remove(&pane).map(|state| (state, sibling))
295 } else {
296 None
297 }
298 }
299
300 pub fn maximize(&mut self, pane: Pane) {
305 self.internal.maximized = Some(pane);
306 }
307
308 pub fn restore(&mut self) {
313 let _ = self.internal.maximized.take();
314 }
315
316 pub fn maximized(&self) -> Option<Pane> {
320 self.internal.maximized
321 }
322}
323
324#[derive(Debug, Clone)]
328pub struct Internal {
329 layout: Node,
330 last_id: usize,
331 maximized: Option<Pane>,
332}
333
334impl Internal {
335 pub fn from_configuration<T>(
340 panes: &mut BTreeMap<Pane, T>,
341 content: Configuration<T>,
342 next_id: usize,
343 ) -> Self {
344 let (layout, last_id) = match content {
345 Configuration::Split { axis, ratio, a, b } => {
346 let Internal {
347 layout: a,
348 last_id: next_id,
349 ..
350 } = Self::from_configuration(panes, *a, next_id);
351
352 let Internal {
353 layout: b,
354 last_id: next_id,
355 ..
356 } = Self::from_configuration(panes, *b, next_id);
357
358 (
359 Node::Split {
360 id: Split(next_id),
361 axis,
362 ratio,
363 a: Box::new(a),
364 b: Box::new(b),
365 },
366 next_id + 1,
367 )
368 }
369 Configuration::Pane(state) => {
370 let id = Pane(next_id);
371 let _ = panes.insert(id, state);
372
373 (Node::Pane(id), next_id + 1)
374 }
375 };
376
377 Self {
378 layout,
379 last_id,
380 maximized: None,
381 }
382 }
383
384 pub(super) fn layout(&self) -> Cow<'_, Node> {
385 match self.maximized {
386 Some(pane) => Cow::Owned(Node::Pane(pane)),
387 None => Cow::Borrowed(&self.layout),
388 }
389 }
390
391 pub(super) fn maximized(&self) -> Option<Pane> {
392 self.maximized
393 }
394}
395
396#[derive(Debug, Clone, Copy, PartialEq, Default)]
400pub enum Action {
401 #[default]
405 Idle,
406 Dragging {
410 pane: Pane,
412 origin: Point,
414 },
415 Resizing {
419 split: Split,
421 axis: Axis,
423 },
424}
425
426impl Action {
427 pub fn picked_pane(&self) -> Option<(Pane, Point)> {
429 match *self {
430 Action::Dragging { pane, origin, .. } => Some((pane, origin)),
431 _ => None,
432 }
433 }
434
435 pub fn picked_split(&self) -> Option<(Split, Axis)> {
437 match *self {
438 Action::Resizing { split, axis, .. } => Some((split, axis)),
439 _ => None,
440 }
441 }
442}