1use crate::core::{Point, Size};
5use crate::pane_grid::{
6 Axis, Configuration, Direction, Edge, Node, Pane, Region, Split, Target,
7};
8
9use rustc_hash::FxHashMap;
10
11#[derive(Debug, Clone)]
24pub struct State<T> {
25 pub panes: FxHashMap<Pane, T>,
29
30 pub internal: Internal,
34
35 pub(super) maximized: Option<Pane>,
39}
40
41impl<T> State<T> {
42 pub fn new(first_pane_state: T) -> (Self, Pane) {
47 (
48 Self::with_configuration(Configuration::Pane(first_pane_state)),
49 Pane(0),
50 )
51 }
52
53 pub fn with_configuration(config: impl Into<Configuration<T>>) -> Self {
55 let mut panes = FxHashMap::default();
56
57 let internal =
58 Internal::from_configuration(&mut panes, config.into(), 0);
59
60 State {
61 panes,
62 internal,
63 maximized: None,
64 }
65 }
66
67 pub fn len(&self) -> usize {
69 self.panes.len()
70 }
71
72 pub fn is_empty(&self) -> bool {
74 self.len() == 0
75 }
76
77 pub fn get(&self, pane: Pane) -> Option<&T> {
79 self.panes.get(&pane)
80 }
81
82 pub fn get_mut(&mut self, pane: Pane) -> Option<&mut T> {
85 self.panes.get_mut(&pane)
86 }
87
88 pub fn iter(&self) -> impl Iterator<Item = (&Pane, &T)> {
91 self.panes.iter()
92 }
93
94 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Pane, &mut T)> {
97 self.panes.iter_mut()
98 }
99
100 pub fn layout(&self) -> &Node {
102 &self.internal.layout
103 }
104
105 pub fn adjacent(&self, pane: Pane, direction: Direction) -> Option<Pane> {
108 let regions = self
109 .internal
110 .layout
111 .pane_regions(0.0, Size::new(4096.0, 4096.0));
112
113 let current_region = regions.get(&pane)?;
114
115 let target = match direction {
116 Direction::Left => {
117 Point::new(current_region.x - 1.0, current_region.y + 1.0)
118 }
119 Direction::Right => Point::new(
120 current_region.x + current_region.width + 1.0,
121 current_region.y + 1.0,
122 ),
123 Direction::Up => {
124 Point::new(current_region.x + 1.0, current_region.y - 1.0)
125 }
126 Direction::Down => Point::new(
127 current_region.x + 1.0,
128 current_region.y + current_region.height + 1.0,
129 ),
130 };
131
132 let mut colliding_regions =
133 regions.iter().filter(|(_, region)| region.contains(target));
134
135 let (pane, _) = colliding_regions.next()?;
136
137 Some(*pane)
138 }
139
140 pub fn split(
143 &mut self,
144 axis: Axis,
145 pane: Pane,
146 state: T,
147 ) -> Option<(Pane, Split)> {
148 self.split_node(axis, Some(pane), state, false)
149 }
150
151 pub fn split_with(&mut self, target: Pane, pane: Pane, region: Region) {
155 match region {
156 Region::Center => self.swap(pane, target),
157 Region::Edge(edge) => match edge {
158 Edge::Top => {
159 self.split_and_swap(Axis::Horizontal, target, pane, true);
160 }
161 Edge::Bottom => {
162 self.split_and_swap(Axis::Horizontal, target, pane, false);
163 }
164 Edge::Left => {
165 self.split_and_swap(Axis::Vertical, target, pane, true);
166 }
167 Edge::Right => {
168 self.split_and_swap(Axis::Vertical, target, pane, false);
169 }
170 },
171 }
172 }
173
174 pub fn drop(&mut self, pane: Pane, target: Target) {
176 match target {
177 Target::Edge(edge) => self.move_to_edge(pane, edge),
178 Target::Pane(target, region) => {
179 self.split_with(target, pane, region);
180 }
181 }
182 }
183
184 fn split_node(
185 &mut self,
186 axis: Axis,
187 pane: Option<Pane>,
188 state: T,
189 inverse: bool,
190 ) -> Option<(Pane, Split)> {
191 let node = if let Some(pane) = pane {
192 self.internal.layout.find(pane)?
193 } else {
194 &mut self.internal.layout
196 };
197
198 let new_pane = {
199 self.internal.last_id = self.internal.last_id.checked_add(1)?;
200
201 Pane(self.internal.last_id)
202 };
203
204 let new_split = {
205 self.internal.last_id = self.internal.last_id.checked_add(1)?;
206
207 Split(self.internal.last_id)
208 };
209
210 if inverse {
211 node.split_inverse(new_split, axis, new_pane);
212 } else {
213 node.split(new_split, axis, new_pane);
214 }
215
216 let _ = self.panes.insert(new_pane, state);
217 let _ = self.maximized.take();
218
219 Some((new_pane, new_split))
220 }
221
222 fn split_and_swap(
223 &mut self,
224 axis: Axis,
225 target: Pane,
226 pane: Pane,
227 swap: bool,
228 ) {
229 if let Some((state, _)) = self.close(pane) {
230 if let Some((new_pane, _)) = self.split(axis, target, state) {
231 if swap {
232 self.swap(target, new_pane);
233 }
234 }
235 }
236 }
237
238 pub fn move_to_edge(&mut self, pane: Pane, edge: Edge) {
242 match edge {
243 Edge::Top => {
244 self.split_major_node_and_swap(Axis::Horizontal, pane, true);
245 }
246 Edge::Bottom => {
247 self.split_major_node_and_swap(Axis::Horizontal, pane, false);
248 }
249 Edge::Left => {
250 self.split_major_node_and_swap(Axis::Vertical, pane, true);
251 }
252 Edge::Right => {
253 self.split_major_node_and_swap(Axis::Vertical, pane, false);
254 }
255 }
256 }
257
258 fn split_major_node_and_swap(
259 &mut self,
260 axis: Axis,
261 pane: Pane,
262 swap: bool,
263 ) {
264 if let Some((state, _)) = self.close(pane) {
265 let _ = self.split_node(axis, None, state, swap);
266 }
267 }
268
269 pub fn swap(&mut self, a: Pane, b: Pane) {
277 self.internal.layout.update(&|node| match node {
278 Node::Split { .. } => {}
279 Node::Pane(pane) => {
280 if *pane == a {
281 *node = Node::Pane(b);
282 } else if *pane == b {
283 *node = Node::Pane(a);
284 }
285 }
286 });
287 }
288
289 pub fn resize(&mut self, split: Split, ratio: f32) {
300 let _ = self.internal.layout.resize(split, ratio);
301 }
302
303 pub fn close(&mut self, pane: Pane) -> Option<(T, Pane)> {
306 if self.maximized == Some(pane) {
307 let _ = self.maximized.take();
308 }
309
310 if let Some(sibling) = self.internal.layout.remove(pane) {
311 self.panes.remove(&pane).map(|state| (state, sibling))
312 } else {
313 None
314 }
315 }
316
317 pub fn maximize(&mut self, pane: Pane) {
322 self.maximized = Some(pane);
323 }
324
325 pub fn restore(&mut self) {
330 let _ = self.maximized.take();
331 }
332
333 pub fn maximized(&self) -> Option<Pane> {
337 self.maximized
338 }
339}
340
341#[derive(Debug, Clone)]
345pub struct Internal {
346 layout: Node,
347 last_id: usize,
348}
349
350impl Internal {
351 pub fn from_configuration<T>(
356 panes: &mut FxHashMap<Pane, T>,
357 content: Configuration<T>,
358 next_id: usize,
359 ) -> Self {
360 let (layout, last_id) = match content {
361 Configuration::Split { axis, ratio, a, b } => {
362 let Internal {
363 layout: a,
364 last_id: next_id,
365 ..
366 } = Self::from_configuration(panes, *a, next_id);
367
368 let Internal {
369 layout: b,
370 last_id: next_id,
371 ..
372 } = Self::from_configuration(panes, *b, next_id);
373
374 (
375 Node::Split {
376 id: Split(next_id),
377 axis,
378 ratio,
379 a: Box::new(a),
380 b: Box::new(b),
381 },
382 next_id + 1,
383 )
384 }
385 Configuration::Pane(state) => {
386 let id = Pane(next_id);
387 let _ = panes.insert(id, state);
388
389 (Node::Pane(id), next_id + 1)
390 }
391 };
392
393 Self { layout, last_id }
394 }
395}
396
397#[derive(Debug, Clone, Copy, PartialEq)]
401pub enum Action {
402 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}
443
444impl Internal {
445 pub fn layout(&self) -> &Node {
447 &self.layout
448 }
449}