Skip to main content

modalkit_ratatui/windows/
layout.rs

1//! # Window Layouts
2//!
3//! ## Overview
4//!
5//! Windows are placed in subdivided rows and columns, and by default equally sized according to
6//! how many windows are in the widest portion of the layout, and how many windows are in the
7//! tallest portion of the layout.
8use std::cmp::Ordering;
9use std::convert::TryInto;
10use std::marker::PhantomData;
11use std::ops::Not;
12
13use serde::{Deserialize, Serialize};
14
15use ratatui::{
16    buffer::Buffer,
17    layout::Rect,
18    style::Style,
19    widgets::{Block, BorderType, Borders, StatefulWidget, Widget},
20};
21
22use crate::util::{rect_down, rect_right, rect_zero_height, rect_zero_width};
23use crate::{TermOffset, TerminalCursor, Window, WindowOps};
24
25use super::size::{ResizeInfo, ResizeInfoTrail, MIN_WIN_LEN};
26use super::slot::WindowSlot;
27use super::tree::{AxisTreeIterMut, SubtreeOps, TreeOps};
28use super::{
29    winnr_cmp,
30    AxisT,
31    AxisTree,
32    AxisTreeNode,
33    HorizontalT,
34    TreeInfo,
35    Value,
36    VerticalT,
37    WindowActions,
38    WindowInfo,
39};
40
41use modalkit::actions::{Jumpable, WindowAction, WindowContainer, WindowCount};
42use modalkit::errors::{EditError, EditResult, UIError, UIResult};
43use modalkit::prelude::Axis::{Horizontal, Vertical};
44use modalkit::prelude::MoveDir2D::{Down, Left, Right, Up};
45use modalkit::prelude::*;
46use modalkit::ui::idx_offset;
47
48use modalkit::editing::{
49    application::ApplicationInfo,
50    context::{EditContext, Resolve},
51    store::Store,
52};
53
54fn windex(count: &Count, ctx: &EditContext) -> usize {
55    ctx.resolve(count).saturating_sub(1)
56}
57
58fn slopped_length(base: u16, count: u16, slop: &mut u16) -> u16 {
59    let slopped = count.min(*slop);
60    let length = base * count + slopped;
61
62    *slop -= slopped;
63
64    return length;
65}
66
67fn set_area_lens<W, X, Y>(node: &mut AxisTreeNode<W, X, Y>, area: Rect, info: &ResizeInfo)
68where
69    X: AxisT,
70    Y: AxisT,
71{
72    let Some(ref lengths) = info.lengths else {
73        return;
74    };
75    assert_eq!(lengths.len(), node.weight());
76
77    fn winlen(sd: &super::size::SizeDescription, winrem: u16, lenrem: u16) -> u16 {
78        if winrem == 0 {
79            // Ignore sd.length for the last window.
80            return lenrem;
81        }
82
83        // Make sure large window lengths don't steal too much.
84        lenrem.saturating_sub(winrem * MIN_WIN_LEN).min(sd.length)
85    }
86
87    match X::axis() {
88        Axis::Horizontal => {
89            let mut carea = rect_zero_height(area);
90            let mut iter = lengths.iter();
91            let mut rem = area.height;
92
93            let mut f = |value: &mut Value<W, X, Y>| {
94                let size = iter.next().unwrap();
95                let height = winlen(size, iter.as_slice().len() as u16, rem);
96
97                carea = rect_down(carea, height);
98                rem -= height;
99
100                value.set_area(carea, info);
101            };
102
103            node.for_each_value(&mut f);
104        },
105        Axis::Vertical => {
106            let mut carea = rect_zero_width(area);
107            let mut iter = lengths.iter();
108            let mut rem = area.width;
109
110            let mut f = |value: &mut Value<W, X, Y>| {
111                let size = iter.next().unwrap();
112                let width = winlen(size, iter.as_slice().len() as u16, rem);
113
114                carea = rect_right(carea, width);
115                rem -= width;
116
117                value.set_area(carea, info);
118            };
119
120            node.for_each_value(&mut f);
121        },
122    }
123}
124
125fn set_area_equal<W, X, Y>(node: &mut AxisTreeNode<W, X, Y>, area: Rect, info: &ResizeInfo)
126where
127    X: AxisT,
128    Y: AxisT,
129{
130    let (lw, lh) = node.dimensions();
131    let (lw, lh) = (lw as u16, lh as u16);
132
133    match X::axis() {
134        Axis::Horizontal => {
135            let height = area.height / lh;
136
137            let mut slop = area.height % lh;
138            let mut carea = rect_zero_height(area);
139
140            let mut f = |value: &mut Value<W, X, Y>| {
141                let height = slopped_length(height, value.height() as u16, &mut slop);
142
143                carea = rect_down(carea, height);
144
145                value.set_area(carea, info);
146            };
147
148            node.for_each_value(&mut f);
149        },
150        Axis::Vertical => {
151            let width = area.width / lw;
152
153            let mut slop = area.width % lw;
154            let mut carea = rect_zero_width(area);
155
156            let mut f = |value: &mut Value<W, X, Y>| {
157                let width = slopped_length(width, value.width() as u16, &mut slop);
158
159                carea = rect_right(carea, width);
160
161                value.set_area(carea, info);
162            };
163
164            node.for_each_value(&mut f);
165        },
166    }
167}
168
169enum WrappedIterMut<'a, W> {
170    Horizontal(AxisTreeIterMut<'a, W, HorizontalT, VerticalT>),
171    Vertical(AxisTreeIterMut<'a, W, VerticalT, HorizontalT>),
172}
173
174struct SlotIter<'a, W> {
175    stack: Vec<WrappedIterMut<'a, WindowSlot<W>>>,
176}
177
178impl<'a, W> Iterator for SlotIter<'a, W> {
179    type Item = &'a mut WindowSlot<W>;
180
181    fn next(&mut self) -> Option<Self::Item> {
182        loop {
183            let last = self.stack.last_mut()?;
184
185            match last {
186                WrappedIterMut::Horizontal(node) => {
187                    match node.next() {
188                        None => {
189                            let _ = self.stack.pop();
190                        },
191                        Some(Value::Window(w, _)) => {
192                            return Some(w);
193                        },
194                        Some(Value::Tree(t, _)) => {
195                            let iter = WrappedIterMut::Vertical(t.iter_mut());
196                            self.stack.push(iter);
197                            continue;
198                        },
199                    }
200                },
201                WrappedIterMut::Vertical(node) => {
202                    match node.next() {
203                        None => {
204                            let _ = self.stack.pop();
205                        },
206                        Some(Value::Window(w, _)) => {
207                            return Some(w);
208                        },
209                        Some(Value::Tree(t, _)) => {
210                            let iter = WrappedIterMut::Horizontal(t.iter_mut());
211                            self.stack.push(iter);
212                            continue;
213                        },
214                    }
215                },
216            }
217        }
218    }
219}
220
221/// These operations are for manipulating the [Windows](Window) contained within the current tree
222/// and all of the trees it contains along the rotated axis. The indices here are for all of the
223/// reachable windows within this view of the layout, and not for the [Values](Value) within the
224/// tree.
225pub(super) trait LayoutOps<W, X, Y>
226where
227    X: AxisT,
228    Y: AxisT,
229{
230    /// Return how many [Windows](Window) this tree contains.
231    fn size(&self) -> usize;
232
233    /// Return the widest and tallest [Window] count in this tree.
234    fn dimensions(&self) -> (usize, usize);
235
236    /// Return the widest [Window] count in this tree.
237    fn width(&self) -> usize {
238        self.dimensions().0
239    }
240
241    /// Return the tallest [Window] count in this tree.
242    fn height(&self) -> usize {
243        self.dimensions().1
244    }
245
246    /// Remove the [Window] at the given index.
247    fn close(&mut self, at: usize, trail: Box<ResizeInfoTrail<'_, X, Y>>) -> Option<W>;
248
249    /// Collapse this tree to just the [Window] located at the given index.
250    fn collapse(self, at: usize) -> Option<W>;
251
252    /// Get a reference to the [Window] at the given index and the area it occupies.
253    fn get_area(&self, at: usize) -> Option<(&W, Rect)>;
254
255    /// Get a reference to the [Window] at the given index.
256    fn get(&self, at: usize) -> Option<&W> {
257        self.get_area(at).map(|(w, _)| w)
258    }
259
260    /// Get a mutable reference to the [Window] at the given index.
261    fn get_mut(&mut self, at: usize) -> Option<&mut W>;
262
263    /// Swap the [Windows](Window) at the given indices.
264    fn swap(&mut self, a: usize, b: usize);
265
266    /// Split the screen space occupied by the [Window] at the given index along the given axis,
267    /// and insert `W` before or after it.
268    fn open(
269        &mut self,
270        at: usize,
271        open: W,
272        length: Option<u16>,
273        rel: MoveDir1D,
274        split_axis: Axis,
275        trail: Box<ResizeInfoTrail<'_, X, Y>>,
276    ) -> usize;
277
278    /// Clear all explicitly specified window sizes.
279    fn clear_sizes(&mut self);
280
281    /// Save all of the current lengths in the tree along a given [Axis].
282    fn freeze(&mut self, axis: Axis);
283
284    /// Set the area that this portion of the layout covers.
285    fn resize(
286        &mut self,
287        at: usize,
288        axis: Axis,
289        change: SizeChange<u16>,
290        trail: Box<ResizeInfoTrail<'_, X, Y>>,
291    );
292
293    /// Set the area that this portion of the layout covers.
294    fn set_area(&mut self, area: Rect, info: &ResizeInfo);
295
296    fn _neighbor_walk(
297        &self,
298        base: usize,
299        current: (usize, usize),
300        c: TermOffset,
301        dir: MoveDir2D,
302    ) -> (usize, usize);
303
304    fn _neighbor_of(
305        &self,
306        base: usize,
307        at: usize,
308        c: TermOffset,
309        dir: MoveDir2D,
310        count: usize,
311    ) -> Option<(usize, usize)>;
312
313    /// Find the neighbor of a given [Window] that is [*n*](Count) windows away in [MoveDir2D]
314    /// direction.
315    fn neighbor(&self, at: usize, dir: MoveDir2D, count: usize) -> Option<usize>
316    where
317        W: TerminalCursor;
318}
319
320impl<W, X, Y> LayoutOps<W, X, Y> for Value<W, X, Y>
321where
322    X: AxisT,
323    Y: AxisT,
324{
325    fn close(&mut self, idx: usize, trail: Box<ResizeInfoTrail<'_, X, Y>>) -> Option<W> {
326        match self {
327            Value::Window(_, _) => panic!("cannot remove element from non-tree"),
328            Value::Tree(tree, ref mut info) => {
329                let trail = ResizeInfoTrail::new(idx, &mut info.resized, Some(trail));
330
331                tree.close(idx, trail)
332            },
333        }
334    }
335
336    fn collapse(self, at: usize) -> Option<W> {
337        match self {
338            Value::Window(w, _) => Some(w),
339            Value::Tree(tree, _) => tree.collapse(at),
340        }
341    }
342
343    fn get_area(&self, at: usize) -> Option<(&W, Rect)> {
344        match self {
345            Value::Window(ref window, info) => {
346                if at == 0 {
347                    Some((window, info.area))
348                } else {
349                    None
350                }
351            },
352            Value::Tree(ref tree, _) => {
353                return tree.get_area(at);
354            },
355        }
356    }
357
358    fn get_mut(&mut self, at: usize) -> Option<&mut W> {
359        match self {
360            Value::Window(ref mut window, _) => {
361                if at == 0 {
362                    Some(window)
363                } else {
364                    None
365                }
366            },
367            Value::Tree(ref mut tree, _) => {
368                return tree.get_mut(at);
369            },
370        }
371    }
372
373    fn dimensions(&self) -> (usize, usize) {
374        match self {
375            Value::Window(_, _) => (1, 1),
376            Value::Tree(tree, _) => tree.dimensions(),
377        }
378    }
379
380    fn size(&self) -> usize {
381        match self {
382            Value::Window(_, _) => 1,
383            Value::Tree(tree, _) => tree.size(),
384        }
385    }
386
387    fn swap(&mut self, a: usize, b: usize) {
388        match self {
389            Value::Window(_, _) => (),
390            Value::Tree(tree, _) => tree.swap(a, b),
391        }
392    }
393
394    fn clear_sizes(&mut self) {
395        match self {
396            Value::Window(_, _) => {
397                return;
398            },
399            Value::Tree(tree, ref mut info) => {
400                info.resized.lengths = None;
401
402                tree.clear_sizes();
403            },
404        }
405    }
406
407    fn freeze(&mut self, axis: Axis) {
408        match self {
409            Value::Window(_, _) => {
410                return;
411            },
412            Value::Tree(tree, info) => {
413                if axis == Y::axis() {
414                    info.resized.lengths = Some(tree.get_lengths());
415                }
416
417                tree.freeze(axis);
418            },
419        }
420    }
421
422    fn resize(
423        &mut self,
424        at: usize,
425        axis: Axis,
426        change: SizeChange<u16>,
427        mut trail: Box<ResizeInfoTrail<'_, X, Y>>,
428    ) {
429        match self {
430            Value::Window(_, info) => {
431                let len = match change {
432                    SizeChange::Equal => {
433                        trail.clear(axis);
434                        return;
435                    },
436                    SizeChange::Exact(amt) => amt.max(MIN_WIN_LEN),
437                    SizeChange::Decrease(amt) => info.get_length(axis).saturating_sub(amt),
438                    SizeChange::Increase(amt) => info.get_length(axis).saturating_add(amt),
439                };
440
441                trail.set_size(axis, len);
442            },
443            Value::Tree(tree, ref mut info) => {
444                info.resized.lengths = Some(tree.get_lengths());
445
446                let trail = ResizeInfoTrail::new(at, &mut info.resized, Some(trail));
447
448                tree.resize(at, axis, change, trail);
449            },
450        }
451    }
452
453    fn set_area(&mut self, area: Rect, _: &ResizeInfo) {
454        match self {
455            Value::Window(_, ref mut info) => {
456                info.area = area;
457            },
458            Value::Tree(tree, ref mut info) => {
459                info.area = area;
460                tree.set_area(area, &info.resized);
461            },
462        }
463    }
464
465    fn open(
466        &mut self,
467        at: usize,
468        open: W,
469        length: Option<u16>,
470        rel: MoveDir1D,
471        split_axis: Axis,
472        trail: Box<ResizeInfoTrail<'_, X, Y>>,
473    ) -> usize {
474        match self {
475            Value::Window(_, info) => {
476                // Change info for the rotated tree.
477                let mut info = info.to_tree(length.map(|_| split_axis));
478                let mut trail = ResizeInfoTrail::new(at, &mut info.resized, Some(trail));
479                trail.split_or_clear(rel, split_axis, length);
480
481                // Make new tree with the converted info.
482                let tree = AxisTreeNode::singleton(open);
483                let mut node = Value::Tree(Box::new(tree), info);
484                std::mem::swap(self, &mut node);
485
486                match (self, node, rel) {
487                    (Value::Tree(t, _), Value::Window(w, _), MoveDir1D::Previous) => {
488                        t.insert_max(w);
489                        0
490                    },
491                    (Value::Tree(t, _), Value::Window(w, _), MoveDir1D::Next) => {
492                        t.insert_min(w);
493                        1
494                    },
495                    (_, _, _) => {
496                        panic!("split: invalid state");
497                    },
498                }
499            },
500            Value::Tree(tree, ref mut info) => {
501                let trail = ResizeInfoTrail::new(at, &mut info.resized, Some(trail));
502
503                tree.open(at, open, length, rel, split_axis, trail)
504            },
505        }
506    }
507
508    fn _neighbor_walk(
509        &self,
510        base: usize,
511        current: (usize, usize),
512        c: TermOffset,
513        dir: MoveDir2D,
514    ) -> (usize, usize) {
515        if current.1 == 0 {
516            return current;
517        }
518
519        match self {
520            Value::Window(_, _) => (base, current.1 - 1),
521            Value::Tree(tree, _) => tree._neighbor_walk(base, current, c, dir),
522        }
523    }
524
525    fn _neighbor_of(
526        &self,
527        base: usize,
528        at: usize,
529        c: TermOffset,
530        dir: MoveDir2D,
531        count: usize,
532    ) -> Option<(usize, usize)> {
533        match self {
534            Value::Window(_, _) => Some((base, count)),
535            Value::Tree(tree, _) => tree._neighbor_of(base, at, c, dir, count),
536        }
537    }
538
539    fn neighbor(&self, _: usize, _: MoveDir2D, _: usize) -> Option<usize>
540    where
541        W: TerminalCursor,
542    {
543        unreachable!();
544    }
545}
546
547type HorizontalTree<W> = AxisTree<W, HorizontalT, VerticalT>;
548
549impl<W, X, Y> LayoutOps<W, X, Y> for AxisTreeNode<W, X, Y>
550where
551    X: AxisT,
552    Y: AxisT,
553{
554    fn size(&self) -> usize {
555        self.info.size
556    }
557
558    fn dimensions(&self) -> (usize, usize) {
559        self.info.dimensions
560    }
561
562    fn close(&mut self, at: usize, mut trail: Box<ResizeInfoTrail<'_, X, Y>>) -> Option<W> {
563        let lsize = self.left.size();
564        let vsize = self.value.size();
565
566        match winnr_cmp(at, lsize, vsize) {
567            (Ordering::Less, idx) => {
568                let ret = self.left.close(idx, trail);
569                self.balance_left();
570                return ret;
571            },
572            (Ordering::Equal, idx) => {
573                if vsize == 1 {
574                    trail.clear(X::axis());
575
576                    return self.remove_root().collapse(0);
577                } else {
578                    let ret = self.value.close(idx, trail);
579                    self._update_info();
580                    return ret;
581                }
582            },
583            (Ordering::Greater, idx) => {
584                let ret = self.right.close(idx, trail);
585                self.balance_right();
586                return ret;
587            },
588        }
589    }
590
591    fn collapse(self, at: usize) -> Option<W> {
592        let lsize = self.left.size();
593        let vsize = self.value.size();
594
595        match winnr_cmp(at, lsize, vsize) {
596            (Ordering::Less, idx) => self.left.collapse(idx),
597            (Ordering::Equal, idx) => self.value.collapse(idx),
598            (Ordering::Greater, idx) => self.right.collapse(idx),
599        }
600    }
601
602    fn get_area(&self, at: usize) -> Option<(&W, Rect)> {
603        let lsize = self.left.size();
604        let vsize = self.value.size();
605
606        match winnr_cmp(at, lsize, vsize) {
607            (Ordering::Less, idx) => self.left.get_area(idx),
608            (Ordering::Equal, idx) => self.value.get_area(idx),
609            (Ordering::Greater, idx) => self.right.get_area(idx),
610        }
611    }
612
613    fn get_mut(&mut self, at: usize) -> Option<&mut W> {
614        let lsize = self.left.size();
615        let vsize = self.value.size();
616
617        match winnr_cmp(at, lsize, vsize) {
618            (Ordering::Less, idx) => self.left.get_mut(idx),
619            (Ordering::Equal, idx) => self.value.get_mut(idx),
620            (Ordering::Greater, idx) => self.right.get_mut(idx),
621        }
622    }
623
624    fn open(
625        &mut self,
626        at: usize,
627        open: W,
628        length: Option<u16>,
629        rel: MoveDir1D,
630        split_axis: Axis,
631        mut trail: Box<ResizeInfoTrail<'_, X, Y>>,
632    ) -> usize {
633        let lsize = self.left.size();
634        let vsize = self.value.size();
635
636        match winnr_cmp(at, lsize, vsize) {
637            (Ordering::Less, idx) => {
638                let winnr = self.left.open(idx, open, length, rel, split_axis, trail);
639                self.balance_right();
640                winnr
641            },
642            (Ordering::Equal, idx) => {
643                let split_here = vsize == 1 && split_axis == X::axis();
644
645                if split_here {
646                    trail.split_or_clear(rel, split_axis, length);
647
648                    match rel {
649                        MoveDir1D::Previous => {
650                            let winnr = self.left.insert_max(open);
651                            self.balance_right();
652                            winnr
653                        },
654                        MoveDir1D::Next => {
655                            let winnr = self.right.insert_min(open);
656                            let winnr = lsize + vsize + winnr;
657                            self.balance_left();
658                            winnr
659                        },
660                    }
661                } else {
662                    let winnr = self.value.open(idx, open, length, rel, split_axis, trail);
663                    let winnr = lsize + winnr;
664                    self._update_info();
665                    winnr
666                }
667            },
668            (Ordering::Greater, idx) => {
669                let winnr = self.right.open(idx, open, length, rel, split_axis, trail);
670                let winnr = lsize + vsize + winnr;
671                self.balance_left();
672                winnr
673            },
674        }
675    }
676
677    /*
678     * Ideally this would just be two self.get_mut() calls, but that would
679     * mutably borrow self twice, so we need to descend the tree until we get
680     * to the divergent point.
681     */
682    fn swap(&mut self, a: usize, b: usize) {
683        if a == b {
684            /*
685             * No-op, do nothing.
686             */
687            return;
688        }
689
690        let lsize = self.left.size();
691        let vsize = self.value.size();
692
693        let ap = winnr_cmp(a, lsize, vsize);
694        let bp = winnr_cmp(b, lsize, vsize);
695
696        let (amut, bmut) = match (ap, bp) {
697            ((Ordering::Less, aidx), (Ordering::Less, bidx)) => {
698                self.left.swap(aidx, bidx);
699                return;
700            },
701            ((Ordering::Less, aidx), (Ordering::Equal, bidx)) => {
702                (self.left.get_mut(aidx), self.value.get_mut(bidx))
703            },
704            ((Ordering::Less, aidx), (Ordering::Greater, bidx)) => {
705                (self.left.get_mut(aidx), self.right.get_mut(bidx))
706            },
707            ((Ordering::Equal, aidx), (Ordering::Less, bidx)) => {
708                (self.value.get_mut(aidx), self.left.get_mut(bidx))
709            },
710            ((Ordering::Equal, aidx), (Ordering::Equal, bidx)) => {
711                self.value.swap(aidx, bidx);
712                return;
713            },
714            ((Ordering::Equal, aidx), (Ordering::Greater, bidx)) => {
715                (self.value.get_mut(aidx), self.right.get_mut(bidx))
716            },
717            ((Ordering::Greater, aidx), (Ordering::Less, bidx)) => {
718                (self.right.get_mut(aidx), self.left.get_mut(bidx))
719            },
720            ((Ordering::Greater, aidx), (Ordering::Equal, bidx)) => {
721                (self.right.get_mut(aidx), self.value.get_mut(bidx))
722            },
723            ((Ordering::Greater, aidx), (Ordering::Greater, bidx)) => {
724                self.right.swap(aidx, bidx);
725                return;
726            },
727        };
728
729        if let (Some(awin), Some(bwin)) = (amut, bmut) {
730            /*
731             * If both indexes were valid, swap their windows.
732             */
733            std::mem::swap(awin, bwin);
734        }
735    }
736
737    fn clear_sizes(&mut self) {
738        self.left.clear_sizes();
739        self.value.clear_sizes();
740        self.right.clear_sizes();
741    }
742
743    fn freeze(&mut self, axis: Axis) {
744        self.left.freeze(axis);
745        self.value.freeze(axis);
746        self.right.freeze(axis);
747    }
748
749    fn resize(
750        &mut self,
751        at: usize,
752        axis: Axis,
753        change: SizeChange<u16>,
754        trail: Box<ResizeInfoTrail<'_, X, Y>>,
755    ) {
756        let lsize = self.left.size();
757        let vsize = self.value.size();
758
759        match winnr_cmp(at, lsize, vsize) {
760            (Ordering::Less, idx) => {
761                self.left.resize(idx, axis, change, trail);
762            },
763            (Ordering::Equal, idx) => {
764                self.value.resize(idx, axis, change, trail);
765            },
766            (Ordering::Greater, idx) => {
767                self.right.resize(idx, axis, change, trail);
768            },
769        }
770    }
771
772    fn set_area(&mut self, area: Rect, info: &ResizeInfo) {
773        if info.lengths.is_some() {
774            set_area_lens(self, area, info);
775        } else {
776            set_area_equal(self, area, info);
777        }
778    }
779
780    fn _neighbor_walk(
781        &self,
782        base: usize,
783        current: (usize, usize),
784        c: TermOffset,
785        dir: MoveDir2D,
786    ) -> (usize, usize) {
787        if current.1 == 0 {
788            /*
789             * We've already found the target index.
790             */
791            return current;
792        }
793
794        let lsize = self.left.size();
795        let vsize = self.value.size();
796
797        let lbase = base;
798        let vbase = lbase + lsize;
799        let rbase = vbase + vsize;
800
801        match (X::axis(), dir) {
802            (Vertical, Left) | (Horizontal, Up) => {
803                let current = self.right._neighbor_walk(rbase, current, c, dir);
804                let current = self.value._neighbor_walk(vbase, current, c, dir);
805                return self.left._neighbor_walk(lbase, current, c, dir);
806            },
807            (Vertical, Right) | (Horizontal, Down) => {
808                let current = self.left._neighbor_walk(lbase, current, c, dir);
809                let current = self.value._neighbor_walk(vbase, current, c, dir);
810                return self.right._neighbor_walk(rbase, current, c, dir);
811            },
812            (Vertical, Up | Down) => {
813                /*
814                 * Find the node with the same screen column offset as the starting window's
815                 * cursor, and continue from there.
816                 */
817                let mut off = base;
818                let col = c.0;
819
820                for value in self.iter() {
821                    let area = value.area();
822                    let lc = area.left();
823                    let rc = area.right();
824
825                    if col >= lc && col < rc {
826                        return value._neighbor_walk(off, current, c, dir);
827                    }
828
829                    off += value.size();
830                }
831
832                // We should always find a valid window, but fall back here if we don't.
833                self.value._neighbor_walk(base, current, c, dir)
834            },
835            (Horizontal, Left | Right) => {
836                /*
837                 * Find the node with the same screen row offset as the starting window's cursor,
838                 * and continue from there.
839                 */
840
841                let mut off = base;
842                let row = c.1;
843
844                for value in self.iter() {
845                    let area = value.area();
846                    let tr = area.top();
847                    let br = area.bottom();
848
849                    if row >= tr && row < br {
850                        return value._neighbor_walk(off, current, c, dir);
851                    }
852
853                    off += value.size();
854                }
855
856                // We should always find a valid window, but fall back here if we don't.
857                self.value._neighbor_walk(base, current, c, dir)
858            },
859        }
860    }
861
862    fn _neighbor_of(
863        &self,
864        base: usize,
865        at: usize,
866        c: TermOffset,
867        dir: MoveDir2D,
868        count: usize,
869    ) -> Option<(usize, usize)> {
870        let lsize = self.left.size();
871        let vsize = self.value.size();
872
873        let branch = winnr_cmp(at, lsize, vsize);
874
875        let lbase = base;
876        let vbase = lbase + lsize;
877        let rbase = vbase + vsize;
878
879        match (X::axis(), dir) {
880            (Vertical, Left) | (Horizontal, Up) => {
881                match branch {
882                    (Ordering::Less, idx) => {
883                        return self.left._neighbor_of(lbase, idx, c, dir, count);
884                    },
885                    (Ordering::Equal, idx) => {
886                        let current = self.value._neighbor_of(vbase, idx, c, dir, count)?;
887                        let current = self.left._neighbor_walk(lbase, current, c, dir);
888
889                        return Some(current);
890                    },
891                    (Ordering::Greater, idx) => {
892                        let current = self.right._neighbor_of(rbase, idx, c, dir, count)?;
893                        let current = self.value._neighbor_walk(vbase, current, c, dir);
894                        let current = self.left._neighbor_walk(lbase, current, c, dir);
895
896                        return Some(current);
897                    },
898                }
899            },
900            (Vertical, Right) | (Horizontal, Down) => {
901                match branch {
902                    (Ordering::Less, idx) => {
903                        let current = self.left._neighbor_of(lbase, idx, c, dir, count)?;
904                        let current = self.value._neighbor_walk(vbase, current, c, dir);
905                        let current = self.right._neighbor_walk(rbase, current, c, dir);
906
907                        return Some(current);
908                    },
909                    (Ordering::Equal, idx) => {
910                        let current = self.value._neighbor_of(vbase, idx, c, dir, count)?;
911                        let current = self.right._neighbor_walk(rbase, current, c, dir);
912
913                        return Some(current);
914                    },
915                    (Ordering::Greater, idx) => {
916                        return self.right._neighbor_of(rbase, idx, c, dir, count);
917                    },
918                }
919            },
920            (Vertical, Up | Down) | (Horizontal, Left | Right) => {
921                match branch {
922                    (Ordering::Less, idx) => {
923                        return self.left._neighbor_of(lbase, idx, c, dir, count);
924                    },
925                    (Ordering::Equal, idx) => {
926                        return self.value._neighbor_of(vbase, idx, c, dir, count);
927                    },
928                    (Ordering::Greater, idx) => {
929                        return self.right._neighbor_of(rbase, idx, c, dir, count);
930                    },
931                }
932            },
933        }
934    }
935
936    fn neighbor(&self, at: usize, dir: MoveDir2D, count: usize) -> Option<usize>
937    where
938        W: TerminalCursor,
939    {
940        let (w, r) = self.get_area(at)?;
941        let c = w.get_term_cursor().unwrap_or((r.x, r.y));
942        self._neighbor_of(0, at, c, dir, count).map(|current| current.0)
943    }
944}
945
946impl<W, X, Y> LayoutOps<W, X, Y> for AxisTree<W, X, Y>
947where
948    X: AxisT,
949    Y: AxisT,
950{
951    fn size(&self) -> usize {
952        match self {
953            None => 0,
954            Some(node) => node.size(),
955        }
956    }
957
958    fn dimensions(&self) -> (usize, usize) {
959        match self {
960            None => (0, 0),
961            Some(node) => node.dimensions(),
962        }
963    }
964
965    fn close(&mut self, at: usize, mut trail: Box<ResizeInfoTrail<'_, X, Y>>) -> Option<W> {
966        if let Some(node) = self {
967            let lsize = node.left.size();
968            let vsize = node.value.size();
969
970            match winnr_cmp(at, lsize, vsize) {
971                (Ordering::Less, idx) => {
972                    let ret = node.left.close(idx, trail);
973                    node.balance_left();
974                    return ret;
975                },
976                (Ordering::Equal, idx) => {
977                    if vsize == 1 {
978                        trail.clear(X::axis());
979
980                        return self.remove_root().collapse(0);
981                    } else {
982                        let ret = node.value.close(idx, trail);
983                        node._update_info();
984                        return ret;
985                    }
986                },
987                (Ordering::Greater, idx) => {
988                    let ret = node.right.close(idx, trail);
989                    node.balance_right();
990                    return ret;
991                },
992            }
993        } else {
994            return None;
995        }
996    }
997
998    fn collapse(self, at: usize) -> Option<W> {
999        self.and_then(|node| node.collapse(at))
1000    }
1001
1002    fn get_area(&self, at: usize) -> Option<(&W, Rect)> {
1003        self.as_ref().and_then(|node| node.get_area(at))
1004    }
1005
1006    fn get_mut(&mut self, at: usize) -> Option<&mut W> {
1007        self.as_mut().and_then(|node| node.get_mut(at))
1008    }
1009
1010    fn swap(&mut self, a: usize, b: usize) {
1011        if let Some(node) = self {
1012            node.swap(a, b);
1013        }
1014    }
1015
1016    fn open(
1017        &mut self,
1018        at: usize,
1019        open: W,
1020        length: Option<u16>,
1021        rel: MoveDir1D,
1022        split_axis: Axis,
1023        trail: Box<ResizeInfoTrail<'_, X, Y>>,
1024    ) -> usize {
1025        match self {
1026            None => {
1027                *self = AxisTree::from(if split_axis == X::axis() {
1028                    Value::from(open)
1029                } else {
1030                    Value::from(AxisTreeNode::singleton(open))
1031                });
1032
1033                return 0;
1034            },
1035            Some(node) => {
1036                return node.open(at, open, length, rel, split_axis, trail);
1037            },
1038        }
1039    }
1040
1041    fn _neighbor_walk(
1042        &self,
1043        base: usize,
1044        current: (usize, usize),
1045        c: TermOffset,
1046        dir: MoveDir2D,
1047    ) -> (usize, usize) {
1048        match self {
1049            None => current,
1050            Some(node) => node._neighbor_walk(base, current, c, dir),
1051        }
1052    }
1053
1054    fn _neighbor_of(
1055        &self,
1056        base: usize,
1057        at: usize,
1058        c: TermOffset,
1059        dir: MoveDir2D,
1060        count: usize,
1061    ) -> Option<(usize, usize)> {
1062        self.as_ref().and_then(|node| node._neighbor_of(base, at, c, dir, count))
1063    }
1064
1065    fn clear_sizes(&mut self) {
1066        if let Some(node) = self {
1067            node.clear_sizes();
1068        }
1069    }
1070
1071    fn freeze(&mut self, axis: Axis) {
1072        if let Some(node) = self {
1073            node.freeze(axis);
1074        }
1075    }
1076
1077    fn resize(
1078        &mut self,
1079        at: usize,
1080        axis: Axis,
1081        change: SizeChange<u16>,
1082        trail: Box<ResizeInfoTrail<'_, X, Y>>,
1083    ) {
1084        if let Some(node) = self {
1085            node.resize(at, axis, change, trail);
1086        }
1087    }
1088
1089    fn set_area(&mut self, area: Rect, info: &ResizeInfo) {
1090        if let Some(node) = self {
1091            node.set_area(area, info);
1092        }
1093    }
1094
1095    fn neighbor(&self, at: usize, dir: MoveDir2D, count: usize) -> Option<usize>
1096    where
1097        W: TerminalCursor,
1098    {
1099        self.as_ref().and_then(|tree| tree.neighbor(at, dir, count))
1100    }
1101}
1102
1103/// Data structure holding layout description and state
1104#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1105#[serde(bound(deserialize = "I::WindowId: Deserialize<'de>"))]
1106#[serde(bound(serialize = "I::WindowId: Serialize"))]
1107#[serde(rename_all = "lowercase")]
1108pub struct WindowLayoutRoot<I: ApplicationInfo> {
1109    layout: WindowLayoutDescription<I>,
1110    focused: usize,
1111    zoomed: bool,
1112}
1113
1114impl<I> WindowLayoutRoot<I>
1115where
1116    I: ApplicationInfo,
1117{
1118    /// Restore a layout from a description of windows and splits.
1119    pub fn to_layout<W: Window<I>>(
1120        self,
1121        area: Option<Rect>,
1122        store: &mut Store<I>,
1123    ) -> UIResult<WindowLayoutState<W, I>, I> {
1124        let mut layout = self.layout.to_layout(area, store)?;
1125        layout._focus(self.focused);
1126        layout.zoom = self.zoomed;
1127        Ok(layout)
1128    }
1129}
1130
1131/// A description of a window layout.
1132#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1133#[serde(bound(deserialize = "I::WindowId: Deserialize<'de>"))]
1134#[serde(bound(serialize = "I::WindowId: Serialize"))]
1135#[serde(rename_all = "lowercase", tag = "type")]
1136pub enum WindowLayoutDescription<I: ApplicationInfo> {
1137    /// A single window.
1138    Window {
1139        /// The identifier for this window.
1140        window: I::WindowId,
1141
1142        /// The length of this window within the parent split.
1143        length: Option<u16>,
1144    },
1145
1146    /// A collection of adjacent windows.
1147    Split {
1148        /// The windows in this container.
1149        children: Vec<Self>,
1150
1151        /// The length of this container within the parent split.
1152        length: Option<u16>,
1153    },
1154}
1155
1156impl<I, W, X, Y> From<&Value<W, X, Y>> for WindowLayoutDescription<I>
1157where
1158    I: ApplicationInfo,
1159    W: Window<I>,
1160    X: AxisT,
1161    Y: AxisT,
1162{
1163    fn from(node: &Value<W, X, Y>) -> Self {
1164        match node {
1165            Value::Window(window, info) => {
1166                let length = if X::axis() == Axis::Horizontal {
1167                    info.area.height.into()
1168                } else {
1169                    info.area.width.into()
1170                };
1171
1172                WindowLayoutDescription::Window { window: window.id(), length }
1173            },
1174            Value::Tree(tree, info) => {
1175                let length = if X::axis() == Axis::Horizontal {
1176                    info.area.height.into()
1177                } else {
1178                    info.area.width.into()
1179                };
1180
1181                WindowLayoutDescription::Split {
1182                    children: tree.iter().map(Self::from).collect(),
1183                    length,
1184                }
1185            },
1186        }
1187    }
1188}
1189
1190impl<I> WindowLayoutDescription<I>
1191where
1192    I: ApplicationInfo,
1193{
1194    fn into_value<W, X, Y>(self, store: &mut Store<I>) -> UIResult<Value<W, X, Y>, I>
1195    where
1196        W: Window<I>,
1197        X: AxisT,
1198        Y: AxisT,
1199    {
1200        match self {
1201            WindowLayoutDescription::Window { window, .. } => {
1202                let w = W::open(window, store)?;
1203                let v = Value::Window(w, WindowInfo::default());
1204
1205                Ok(v)
1206            },
1207            WindowLayoutDescription::Split { children, .. } => {
1208                let mut layout: AxisTree<W, Y, X> = None;
1209                let sizes = children
1210                    .iter()
1211                    .map(|v| {
1212                        match v {
1213                            Self::Window { length, .. } | Self::Split { length, .. } => *length,
1214                        }
1215                    })
1216                    .collect::<Vec<_>>();
1217
1218                for child in children {
1219                    let value = child.into_value(store)?;
1220                    layout.insert_max_value(value);
1221                }
1222
1223                let Some(layout) = layout else {
1224                    let msg = "Cannot open empty split";
1225                    let err = UIError::Failure(msg.to_string());
1226
1227                    return Err(err);
1228                };
1229
1230                let lengths = if sizes.iter().any(Option::is_some) {
1231                    let mut l = layout.get_lengths();
1232
1233                    for (sd, length) in l.iter_mut().zip(sizes.into_iter()) {
1234                        if let Some(length) = length {
1235                            sd.length = length;
1236                        }
1237                    }
1238
1239                    Some(l)
1240                } else {
1241                    None
1242                };
1243
1244                let resized = ResizeInfo { lengths };
1245                let info = TreeInfo { area: Rect::default(), resized };
1246                Ok(Value::Tree(layout, info))
1247            },
1248        }
1249    }
1250
1251    /// Restore a layout from a description of windows and splits.
1252    pub fn to_layout<W: Window<I>>(
1253        self,
1254        area: Option<Rect>,
1255        store: &mut Store<I>,
1256    ) -> UIResult<WindowLayoutState<W, I>, I> {
1257        let mut layout = WindowLayoutState::empty();
1258
1259        match self.into_value(store)? {
1260            Value::Window(w, _) => {
1261                layout.root.insert_min(w);
1262            },
1263            Value::Tree(tree, info) => {
1264                layout.root = Some(tree);
1265                layout.info = info;
1266            },
1267        }
1268
1269        if let Some(area) = area {
1270            layout.root.set_area(area, &layout.info.resized);
1271            layout.info.area = area;
1272        }
1273
1274        return Ok(layout);
1275    }
1276}
1277
1278/// Manages the current layout and focus of [Windows](Window) on the screen.
1279pub struct WindowLayoutState<W: Window<I>, I: ApplicationInfo> {
1280    root: HorizontalTree<WindowSlot<W>>,
1281    info: TreeInfo,
1282    zoom: bool,
1283    focused: usize,
1284    focused_last: usize,
1285    _p: PhantomData<I>,
1286}
1287
1288impl<W, I> WindowLayoutState<W, I>
1289where
1290    W: Window<I>,
1291    I: ApplicationInfo,
1292{
1293    /// Create a new instance containing a single [Window].
1294    pub fn new(window: W) -> Self {
1295        WindowLayoutState::from_slot(window.into())
1296    }
1297
1298    /// Create a new instance without any windows.
1299    pub fn empty() -> Self {
1300        WindowLayoutState {
1301            root: None,
1302            info: TreeInfo::default(),
1303            zoom: false,
1304            focused: 0,
1305            focused_last: 0,
1306            _p: PhantomData,
1307        }
1308    }
1309
1310    /// Convert this layout to a serializable summary of its windows and splits.
1311    pub fn as_description(&self) -> WindowLayoutRoot<I> {
1312        let mut children = vec![];
1313        let focused = self.focused;
1314        let zoomed = self.zoom;
1315
1316        let Some(root) = &self.root else {
1317            return WindowLayoutRoot {
1318                layout: WindowLayoutDescription::Split { children, length: None },
1319                focused,
1320                zoomed,
1321            };
1322        };
1323
1324        for w in root.iter() {
1325            children.push(w.into());
1326        }
1327
1328        return WindowLayoutRoot {
1329            layout: WindowLayoutDescription::Split { children, length: None },
1330            focused,
1331            zoomed,
1332        };
1333    }
1334
1335    /// Create a new instance containing a single [Window] displaying some content.
1336    pub fn from_target(
1337        &self,
1338        target: &OpenTarget<I::WindowId>,
1339        ctx: &EditContext,
1340        store: &mut Store<I>,
1341    ) -> UIResult<Self, I> {
1342        let w = self._open(target, ctx, store)?;
1343        let layout = WindowLayoutState::new(w);
1344
1345        Ok(layout)
1346    }
1347
1348    fn from_slot(slot: WindowSlot<W>) -> Self {
1349        WindowLayoutState {
1350            root: AxisTree::singleton(slot),
1351            info: TreeInfo::default(),
1352            zoom: false,
1353            focused: 0,
1354            focused_last: 0,
1355            _p: PhantomData,
1356        }
1357    }
1358
1359    /// Iterate over the [WindowSlot] values in the window layout.
1360    fn slots(&mut self) -> SlotIter<'_, W> {
1361        let iter = SubtreeOps::iter_mut(&mut self.root);
1362        let stack = vec![WrappedIterMut::Horizontal(iter)];
1363
1364        SlotIter { stack }
1365    }
1366
1367    /// Fetch a reference to the currently focused [Window].
1368    pub fn get(&self) -> Option<&W> {
1369        self.get_slot().map(WindowSlot::get)
1370    }
1371
1372    /// Fetch a mutable reference the currently focused [Window].
1373    pub fn get_mut(&mut self) -> Option<&mut W> {
1374        self.get_slot_mut().map(WindowSlot::get_mut)
1375    }
1376
1377    /// Fetch a mutable reference the currently focused [WindowSlot].
1378    fn get_slot(&self) -> Option<&WindowSlot<W>> {
1379        self.root.get(self.focused)
1380    }
1381
1382    /// Fetch a mutable reference the currently focused [WindowSlot].
1383    fn get_slot_mut(&mut self) -> Option<&mut WindowSlot<W>> {
1384        self.root.get_mut(self.focused)
1385    }
1386
1387    /// Extract the currently focused [Window] into its own window layout.
1388    pub fn extract(&mut self) -> Self {
1389        let at = self.focused;
1390        let trail = ResizeInfoTrail::new(at, &mut self.info.resized, None);
1391
1392        if let Some(slot) = self.root.close(at, trail) {
1393            self._clamp_focus();
1394
1395            return WindowLayoutState::from_slot(slot);
1396        } else {
1397            return WindowLayoutState::empty();
1398        }
1399    }
1400
1401    fn move_side(&mut self, at: usize, dir: MoveDir2D) {
1402        let trail = ResizeInfoTrail::new(at, &mut self.info.resized, None);
1403
1404        if let Some(window) = self.root.close(at, trail) {
1405            let nr = self.root.insert_side(window, dir);
1406            self._focus(nr);
1407        }
1408    }
1409
1410    fn close(&mut self, at: usize, _flags: CloseFlags) -> bool {
1411        let trail = ResizeInfoTrail::new(at, &mut self.info.resized, None);
1412
1413        self.root.close(at, trail);
1414        self._clamp_focus();
1415
1416        return self.root.size() == 0;
1417    }
1418
1419    fn only(&mut self, at: usize, _flags: CloseFlags) -> bool {
1420        let target = if at < self.root.size() {
1421            at
1422        } else {
1423            self.focused
1424        };
1425
1426        self.root = std::mem::take(&mut self.root)
1427            .collapse(target)
1428            .and_then(AxisTree::singleton);
1429        self._clamp_focus();
1430        self.clear_sizes();
1431
1432        return self.root.size() == 0;
1433    }
1434
1435    fn open(&mut self, w: W, length: Option<u16>, axis: Axis, rel: MoveDir1D) {
1436        let windex = self.focused;
1437
1438        if length.is_some() {
1439            self.freeze(axis);
1440        }
1441
1442        let trail = ResizeInfoTrail::new(windex, &mut self.info.resized, None);
1443
1444        let nr = self.root.open(windex, w.into(), length, rel, axis, trail);
1445        self.root.set_area(self.info.area, &self.info.resized);
1446
1447        self._focus(nr);
1448    }
1449
1450    fn clear_sizes(&mut self) {
1451        self.info.resized.clear_sizes();
1452        self.root.clear_sizes();
1453        self.root.set_area(self.info.area, &self.info.resized);
1454    }
1455
1456    fn freeze(&mut self, axis: Axis) {
1457        if axis == Axis::Horizontal {
1458            self.info.resized.lengths = Some(self.root.get_lengths());
1459        }
1460
1461        self.root.freeze(axis);
1462    }
1463
1464    fn resize(&mut self, windex: usize, axis: Axis, change: SizeChange<u16>) {
1465        self.freeze(axis);
1466
1467        let trail = ResizeInfoTrail::new(windex, &mut self.info.resized, None);
1468
1469        self.root.resize(windex, axis, change, trail);
1470        self.root.set_area(self.info.area, &self.info.resized);
1471    }
1472
1473    fn _focus(&mut self, focus: usize) {
1474        let max = self.root.size().saturating_sub(1);
1475
1476        self.focused_last = self.focused;
1477        self.focused = focus.min(max);
1478    }
1479
1480    fn _clamp_focus(&mut self) {
1481        let nwins = self.root.size();
1482        let lastw = nwins.saturating_sub(1);
1483
1484        self.focused_last = self.focused_last.min(lastw);
1485        self.focused = self.focused.min(lastw);
1486    }
1487
1488    fn _max_idx(&self) -> usize {
1489        self.root.size().saturating_sub(1)
1490    }
1491
1492    fn _open(
1493        &self,
1494        target: &OpenTarget<I::WindowId>,
1495        ctx: &EditContext,
1496        store: &mut Store<I>,
1497    ) -> UIResult<W, I> {
1498        match target {
1499            OpenTarget::Alternate => {
1500                let slot = self.get_slot().ok_or(UIError::NoWindow)?;
1501
1502                match slot.get_alt() {
1503                    Some(alt) => Ok(alt.dup(store)),
1504                    None => Err(UIError::Failure("No alternate window".into())),
1505                }
1506            },
1507            OpenTarget::Application(id) => W::open(id.clone(), store),
1508            OpenTarget::Current => {
1509                let slot = self.get_slot().ok_or(UIError::NoWindow)?;
1510
1511                Ok(slot.get().dup(store))
1512            },
1513            OpenTarget::Cursor(style) => {
1514                let slot = self.get_slot().ok_or(UIError::NoWindow)?;
1515
1516                if let Some(text) = slot.get().get_cursor_word(style) {
1517                    W::find(text, store)
1518                } else {
1519                    let msg = "No word under cursor".to_string();
1520                    let err = UIError::Failure(msg);
1521
1522                    Err(err)
1523                }
1524            },
1525            OpenTarget::List(count) => W::posn(ctx.resolve(count), store),
1526            OpenTarget::Name(name) => W::find(name.clone(), store),
1527            OpenTarget::Offset(dir, count) => {
1528                let slot = self.get_slot().ok_or(UIError::NoWindow)?;
1529                let count = ctx.resolve(count);
1530
1531                match slot.get_off(*dir, count) {
1532                    Some(w) => Ok(w.dup(store)),
1533                    None => Err(UIError::Failure("Not a valid window offset".into())),
1534                }
1535            },
1536            OpenTarget::Selection => {
1537                let slot = self.get_slot().ok_or(UIError::NoWindow)?;
1538
1539                if let Some(text) = slot.get().get_selected_word() {
1540                    W::find(text, store)
1541                } else {
1542                    let msg = "No text currently selected".to_string();
1543                    let err = UIError::Failure(msg);
1544
1545                    Err(err)
1546                }
1547            },
1548            OpenTarget::Unnamed => W::unnamed(store),
1549        }
1550    }
1551
1552    fn _target(&self, change: &FocusChange, ctx: &EditContext) -> Option<usize> {
1553        match change {
1554            FocusChange::Current => {
1555                return Some(self.focused);
1556            },
1557            FocusChange::Offset(count, false) => {
1558                let winnr = windex(count, ctx);
1559
1560                if winnr >= self.root.size() {
1561                    // Invalid window index; do nothing.
1562                    return None;
1563                }
1564
1565                return Some(winnr);
1566            },
1567            FocusChange::Offset(count, true) => {
1568                let target = windex(count, ctx).min(self._max_idx());
1569
1570                return Some(target);
1571            },
1572            FocusChange::Direction1D(dir, count, wrap) => {
1573                let nwins = self.root.size();
1574                let count = ctx.resolve(count);
1575
1576                return idx_offset(self.focused, count, dir, nwins, *wrap);
1577            },
1578            FocusChange::Direction2D(dir, count) => {
1579                let count = ctx.resolve(count);
1580
1581                return self.root.neighbor(self.focused, *dir, count);
1582            },
1583            FocusChange::Position(MovePosition::Beginning) => {
1584                return Some(0);
1585            },
1586            FocusChange::Position(MovePosition::Middle) => {
1587                return Some(self.root.size() / 2);
1588            },
1589            FocusChange::Position(MovePosition::End) => {
1590                return Some(self.root.size().saturating_sub(1));
1591            },
1592            FocusChange::PreviouslyFocused => {
1593                return Some(self.focused_last);
1594            },
1595        }
1596    }
1597}
1598
1599impl<W, I> WindowActions<EditContext, I> for WindowLayoutState<W, I>
1600where
1601    W: Window<I>,
1602    I: ApplicationInfo,
1603{
1604    fn window_focus(
1605        &mut self,
1606        change: &FocusChange,
1607        ctx: &EditContext,
1608        _: &mut Store<I>,
1609    ) -> EditResult<EditInfo, I> {
1610        if let Some(target) = self._target(change, ctx) {
1611            self.zoom = false;
1612            self._focus(target);
1613        }
1614
1615        return Ok(None);
1616    }
1617
1618    fn window_exchange(
1619        &mut self,
1620        change: &FocusChange,
1621        ctx: &EditContext,
1622        _: &mut Store<I>,
1623    ) -> EditResult<EditInfo, I> {
1624        if let Some(target) = self._target(change, ctx) {
1625            self.zoom = false;
1626            self.root.swap(self.focused, target);
1627        }
1628
1629        return Ok(None);
1630    }
1631
1632    fn window_move_side(
1633        &mut self,
1634        dir: MoveDir2D,
1635        _: &EditContext,
1636        _: &mut Store<I>,
1637    ) -> EditResult<EditInfo, I> {
1638        self.zoom = false;
1639        self.move_side(self.focused, dir);
1640
1641        return Ok(None);
1642    }
1643
1644    fn window_rotate(
1645        &mut self,
1646        _dir: MoveDir1D,
1647        _ctx: &EditContext,
1648        _: &mut Store<I>,
1649    ) -> EditResult<EditInfo, I> {
1650        // XXX: implement
1651        let msg = "Window rotation is not currently implemented";
1652        let err = EditError::Unimplemented(msg.into());
1653
1654        return Err(err);
1655    }
1656
1657    fn window_split(
1658        &mut self,
1659        target: &OpenTarget<I::WindowId>,
1660        axis: Axis,
1661        rel: MoveDir1D,
1662        count: &Count,
1663        ctx: &EditContext,
1664        store: &mut Store<I>,
1665    ) -> UIResult<EditInfo, I> {
1666        let count = ctx.resolve(count);
1667        let w = self._open(target, ctx, store)?;
1668
1669        self.zoom = false;
1670
1671        for _ in 0..count {
1672            self.open(w.dup(store), None, axis, rel);
1673        }
1674
1675        return Ok(None);
1676    }
1677
1678    fn window_clear_sizes(&mut self, _: &EditContext, _: &mut Store<I>) -> EditResult<EditInfo, I> {
1679        self.clear_sizes();
1680
1681        return Ok(None);
1682    }
1683
1684    fn window_resize(
1685        &mut self,
1686        change: &FocusChange,
1687        axis: Axis,
1688        size: &SizeChange<Count>,
1689        ctx: &EditContext,
1690        _: &mut Store<I>,
1691    ) -> EditResult<EditInfo, I> {
1692        let Some(target) = self._target(change, ctx) else {
1693            return Ok(None);
1694        };
1695
1696        let change: SizeChange<u16> = match &size {
1697            SizeChange::Equal => SizeChange::Equal,
1698            SizeChange::Exact(count) => SizeChange::Exact(ctx.resolve(count).try_into()?),
1699            SizeChange::Increase(count) => SizeChange::Increase(ctx.resolve(count).try_into()?),
1700            SizeChange::Decrease(count) => SizeChange::Decrease(ctx.resolve(count).try_into()?),
1701        };
1702
1703        self.zoom = false;
1704        self.resize(target, axis, change);
1705
1706        return Ok(None);
1707    }
1708
1709    fn window_close(
1710        &mut self,
1711        target: &WindowTarget,
1712        flags: CloseFlags,
1713        ctx: &EditContext,
1714        _: &mut Store<I>,
1715    ) -> EditResult<EditInfo, I> {
1716        let nwins = self.root.size();
1717
1718        if nwins == 1 && !flags.contains(CloseFlags::QUIT) {
1719            /*
1720             * If the QUIT flag is not present, disallow closing the last window.
1721             */
1722            return Ok(None);
1723        }
1724
1725        self.zoom = false;
1726
1727        match target {
1728            WindowTarget::Single(focus) => {
1729                if let Some(target) = self._target(focus, ctx) {
1730                    self.close(target, flags);
1731                }
1732
1733                return Ok(None);
1734            },
1735            WindowTarget::AllBut(focus) => {
1736                if let Some(target) = self._target(focus, ctx) {
1737                    self.only(target, flags);
1738                }
1739
1740                return Ok(None);
1741            },
1742            WindowTarget::All => {
1743                self.root = None;
1744
1745                return Ok(None);
1746            },
1747        }
1748    }
1749
1750    fn window_open(
1751        &mut self,
1752        target: &OpenTarget<I::WindowId>,
1753        axis: Axis,
1754        rel: MoveDir1D,
1755        count: &Count,
1756        ctx: &EditContext,
1757        store: &mut Store<I>,
1758    ) -> UIResult<EditInfo, I> {
1759        let count: u16 = ctx.resolve(count).try_into().map_err(EditError::from)?;
1760        let w = self._open(target, ctx, store)?;
1761
1762        self.open(w, Some(count), axis, rel);
1763
1764        Ok(None)
1765    }
1766
1767    fn window_switch(
1768        &mut self,
1769        target: &OpenTarget<I::WindowId>,
1770        ctx: &EditContext,
1771        store: &mut Store<I>,
1772    ) -> UIResult<EditInfo, I> {
1773        let slot = self.get_slot_mut().ok_or(UIError::NoWindow)?;
1774
1775        let w = match target {
1776            OpenTarget::Alternate => {
1777                slot.alternate();
1778
1779                return Ok(None);
1780            },
1781            OpenTarget::Application(id) => W::open(id.clone(), store)?,
1782            OpenTarget::Current => slot.get().dup(store),
1783            OpenTarget::Cursor(style) => {
1784                if let Some(text) = slot.get().get_cursor_word(style) {
1785                    W::find(text, store)?
1786                } else {
1787                    let msg = "No word under cursor".to_string();
1788                    let err = UIError::Failure(msg);
1789
1790                    return Err(err);
1791                }
1792            },
1793            OpenTarget::List(count) => W::posn(ctx.resolve(count), store)?,
1794            OpenTarget::Name(name) => W::find(name.clone(), store)?,
1795            OpenTarget::Offset(dir, count) => {
1796                slot.offset(*dir, ctx.resolve(count));
1797
1798                return Ok(None);
1799            },
1800            OpenTarget::Selection => {
1801                if let Some(text) = slot.get().get_selected_word() {
1802                    W::find(text, store)?
1803                } else {
1804                    let msg = "No text currently selected".to_string();
1805                    let err = UIError::Failure(msg);
1806
1807                    return Err(err);
1808                }
1809            },
1810            OpenTarget::Unnamed => W::unnamed(store)?,
1811        };
1812
1813        slot.open(w);
1814
1815        Ok(None)
1816    }
1817
1818    fn window_write(
1819        &mut self,
1820        target: &WindowTarget,
1821        path: Option<&str>,
1822        flags: WriteFlags,
1823        ctx: &EditContext,
1824        store: &mut Store<I>,
1825    ) -> UIResult<EditInfo, I> {
1826        match target {
1827            WindowTarget::Single(focus) => {
1828                if let Some(target) = self._target(focus, ctx) {
1829                    if let Some(w) = self.root.get_mut(target) {
1830                        return w.write(path, flags, store);
1831                    }
1832                }
1833
1834                return Ok(None);
1835            },
1836            WindowTarget::AllBut(focus) => {
1837                let target = self._target(focus, ctx);
1838
1839                for (i, slot) in self.slots().enumerate() {
1840                    if matches!(target, Some(idx) if idx == i) {
1841                        continue;
1842                    }
1843
1844                    let _ = slot.write(path, flags, store)?;
1845                }
1846
1847                return Ok(None);
1848            },
1849            WindowTarget::All => {
1850                for slot in self.slots() {
1851                    let _ = slot.write(path, flags, store)?;
1852                }
1853
1854                return Ok(None);
1855            },
1856        }
1857    }
1858
1859    fn window_zoom_toggle(&mut self, _: &EditContext, _: &mut Store<I>) -> EditResult<EditInfo, I> {
1860        self.zoom = self.zoom.not();
1861
1862        Ok(None)
1863    }
1864}
1865
1866impl<W, I> WindowCount for WindowLayoutState<W, I>
1867where
1868    W: Window<I>,
1869    I: ApplicationInfo,
1870{
1871    fn windows(&self) -> usize {
1872        self.root.size()
1873    }
1874}
1875
1876impl<W, C, I> Jumpable<C, I> for WindowLayoutState<W, I>
1877where
1878    W: Window<I> + Jumpable<C, I>,
1879    I: ApplicationInfo,
1880{
1881    fn jump(
1882        &mut self,
1883        list: PositionList,
1884        dir: MoveDir1D,
1885        count: usize,
1886        ctx: &C,
1887    ) -> UIResult<usize, I> {
1888        self.get_slot_mut().ok_or(UIError::NoWindow)?.jump(list, dir, count, ctx)
1889    }
1890}
1891
1892impl<W, I> WindowContainer<EditContext, Store<I>, I> for WindowLayoutState<W, I>
1893where
1894    W: Window<I>,
1895    I: ApplicationInfo,
1896{
1897    fn window_command(
1898        &mut self,
1899        action: &WindowAction<I>,
1900        ctx: &EditContext,
1901        store: &mut Store<I>,
1902    ) -> UIResult<EditInfo, I> {
1903        let info = match action {
1904            WindowAction::ClearSizes => self.window_clear_sizes(ctx, store)?,
1905            WindowAction::Close(target, flags) => self.window_close(target, *flags, ctx, store)?,
1906            WindowAction::Exchange(target) => self.window_exchange(target, ctx, store)?,
1907            WindowAction::Focus(target) => self.window_focus(target, ctx, store)?,
1908            WindowAction::MoveSide(dir) => self.window_move_side(*dir, ctx, store)?,
1909            WindowAction::Open(target, axis, rel, count) => {
1910                self.window_open(target, *axis, *rel, count, ctx, store)?
1911            },
1912            WindowAction::Resize(target, axis, size) => {
1913                self.window_resize(target, *axis, size, ctx, store)?
1914            },
1915            WindowAction::Rotate(dir) => self.window_rotate(*dir, ctx, store)?,
1916            WindowAction::Split(target, axis, rel, count) => {
1917                self.window_split(target, *axis, *rel, count, ctx, store)?
1918            },
1919            WindowAction::Switch(target) => self.window_switch(target, ctx, store)?,
1920            WindowAction::Write(target, path, flags) => {
1921                let path = path.as_ref().map(String::as_str);
1922
1923                self.window_write(target, path, *flags, ctx, store)?
1924            },
1925            WindowAction::ZoomToggle => self.window_zoom_toggle(ctx, store)?,
1926            act => {
1927                let msg = format!("unknown window action: {act:?}");
1928                return Err(UIError::Unimplemented(msg));
1929            },
1930        };
1931
1932        return Ok(info);
1933    }
1934}
1935
1936/// Handles rendering the current window layout state to the terminal.
1937pub struct WindowLayout<'a, W: Window<I>, I: ApplicationInfo> {
1938    store: &'a mut Store<I>,
1939    focused: bool,
1940
1941    borders: bool,
1942    border_style: Style,
1943    border_style_focused: Style,
1944    border_type: BorderType,
1945
1946    _pw: PhantomData<(W, I)>,
1947}
1948
1949impl<'a, W, I> WindowLayout<'a, W, I>
1950where
1951    W: Window<I>,
1952    I: ApplicationInfo,
1953{
1954    /// Create a new widget for displaying window layouts.
1955    pub fn new(store: &'a mut Store<I>) -> Self {
1956        WindowLayout {
1957            store,
1958            focused: false,
1959            borders: false,
1960            border_style: Style::default(),
1961            border_style_focused: Style::default(),
1962            border_type: BorderType::Plain,
1963            _pw: PhantomData,
1964        }
1965    }
1966
1967    /// What [Style] should be used when drawing borders.
1968    pub fn border_style(mut self, style: Style) -> Self {
1969        self.border_style = style;
1970        self
1971    }
1972
1973    /// What [Style] should be used when drawing the border of the selected window.
1974    pub fn border_style_focused(mut self, style: Style) -> Self {
1975        self.border_style_focused = style;
1976        self
1977    }
1978
1979    /// What characters should be used when drawing borders.
1980    pub fn border_type(mut self, border_type: BorderType) -> Self {
1981        self.border_type = border_type;
1982        self
1983    }
1984
1985    /// Indicate whether to draw borders around windows.
1986    pub fn borders(mut self, borders: bool) -> Self {
1987        self.borders = borders;
1988        self
1989    }
1990
1991    /// Indicate whether the window layout tree is currently focused.
1992    pub fn focus(mut self, focused: bool) -> Self {
1993        self.focused = focused;
1994        self
1995    }
1996
1997    fn _draw<X, Y>(
1998        &mut self,
1999        node: &mut AxisTreeNode<WindowSlot<W>, X, Y>,
2000        focus: Option<usize>,
2001        _outer: Rect,
2002        buf: &mut Buffer,
2003    ) where
2004        X: AxisT,
2005        Y: AxisT,
2006    {
2007        let mut base = 0;
2008        let mut f = |value: &mut Value<WindowSlot<W>, X, Y>| {
2009            let focus = focus.and_then(|n| n.checked_sub(base));
2010
2011            match value {
2012                Value::Window(w, info) => {
2013                    let focused = matches!(focus, Some(0));
2014
2015                    if self.borders {
2016                        let title = w.get().get_win_title(self.store);
2017                        let block = Block::default()
2018                            .title(title)
2019                            .borders(Borders::ALL)
2020                            .border_style(if focused {
2021                                self.border_style_focused
2022                            } else {
2023                                self.border_style
2024                            })
2025                            .border_type(self.border_type);
2026                        let inner = block.inner(info.area);
2027
2028                        block.render(info.area, buf);
2029                        w.draw(inner, buf, focused, self.store);
2030                    } else {
2031                        w.draw(info.area, buf, focused, self.store);
2032                    }
2033                },
2034                Value::Tree(t, _) => {
2035                    self._draw(t, focus, _outer, buf);
2036                },
2037            }
2038
2039            base += value.size();
2040        };
2041
2042        node.for_each_value(&mut f);
2043    }
2044}
2045
2046impl<W, I> StatefulWidget for WindowLayout<'_, W, I>
2047where
2048    W: Window<I>,
2049    I: ApplicationInfo,
2050{
2051    type State = WindowLayoutState<W, I>;
2052
2053    fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
2054        if state.zoom {
2055            if let Some(window) = state.get_mut() {
2056                window.draw(area, buf, self.focused, self.store);
2057            }
2058
2059            return;
2060        }
2061
2062        let focused = self.focused.then_some(state.focused);
2063
2064        state.info.area = area;
2065        state.root.set_area(area, &state.info.resized);
2066
2067        if let Some(root) = &mut state.root {
2068            self._draw(root.as_mut(), focused, area, buf);
2069        }
2070    }
2071}
2072
2073#[cfg(test)]
2074mod tests {
2075    use super::*;
2076    use crate::TerminalCursor;
2077    use modalkit::editing::{completion::CompletionList, store::Store};
2078    use modalkit::errors::EditError;
2079    use rand::Rng;
2080    use ratatui::text::Line;
2081
2082    macro_rules! fc {
2083        ($n: expr) => {
2084            FocusChange::Offset(Count::Exact($n), false)
2085        };
2086        ($n: expr, $b: expr) => {
2087            FocusChange::Offset(Count::Exact($n), $b)
2088        };
2089    }
2090
2091    macro_rules! window_close {
2092        ($tree: expr, $ct: expr, $flags: expr, $ctx: expr, $store: expr) => {
2093            $tree.window_close(&$ct, $flags, $ctx, &mut $store).unwrap()
2094        };
2095    }
2096
2097    macro_rules! window_exchange {
2098        ($tree: expr, $fc: expr, $ctx: expr, $store: expr) => {
2099            $tree.window_exchange(&$fc, $ctx, &mut $store).unwrap()
2100        };
2101    }
2102
2103    macro_rules! window_focus {
2104        ($tree: expr, $fc: expr, $ctx: expr, $store: expr) => {
2105            $tree.window_focus(&$fc, $ctx, &mut $store).unwrap()
2106        };
2107    }
2108
2109    macro_rules! window_focus_off {
2110        ($tree: expr, $c: expr, $ctx: expr, $store: expr) => {
2111            window_focus!($tree, fc!($c), $ctx, $store)
2112        };
2113    }
2114
2115    macro_rules! window_focus_1d {
2116        ($tree: expr, $dir: expr, $c: expr, $ctx: expr, $store: expr) => {
2117            window_focus!($tree, FocusChange::Direction1D($dir, $c, true), $ctx, $store)
2118        };
2119    }
2120
2121    macro_rules! window_focus_2d {
2122        ($tree: expr, $dir: expr, $c: expr, $ctx: expr, $store: expr) => {
2123            window_focus!($tree, FocusChange::Direction2D($dir, $c), $ctx, $store)
2124        };
2125    }
2126
2127    macro_rules! window_move_side {
2128        ($tree: expr, $dir: expr, $ctx: expr, $store: expr) => {
2129            $tree.window_move_side($dir, $ctx, &mut $store).unwrap()
2130        };
2131    }
2132
2133    macro_rules! window_resize {
2134        ($tree: expr, $change: expr, $axis: expr, $szch: expr, $ctx: expr, $store: expr) => {
2135            $tree.window_resize(&$change, $axis, &$szch, $ctx, &mut $store).unwrap()
2136        };
2137    }
2138
2139    macro_rules! window_switch {
2140        ($tree: expr, $target: expr, $ctx: expr, $store: expr) => {
2141            $tree.window_switch(&$target, $ctx, &mut $store).unwrap()
2142        };
2143    }
2144
2145    macro_rules! window_split {
2146        ($tree: expr, $axis: expr, $dir: expr, $count: expr, $ctx: expr, $store: expr) => {
2147            $tree
2148                .window_split(&OpenTarget::Current, $axis, $dir, &$count, $ctx, &mut $store)
2149                .unwrap()
2150        };
2151    }
2152
2153    #[derive(Clone, Debug, Eq, PartialEq)]
2154    enum TestApp {}
2155
2156    impl ApplicationInfo for TestApp {
2157        type Error = String;
2158        type Action = ();
2159        type Store = ();
2160        type WindowId = Option<usize>;
2161        type ContentId = Option<usize>;
2162
2163        fn content_of_command(_: CommandType) -> Option<usize> {
2164            None
2165        }
2166    }
2167
2168    #[derive(Clone, Debug, Eq, PartialEq)]
2169    pub struct TestWindow {
2170        dirty: bool,
2171        term_area: Rect,
2172        id: Option<usize>,
2173    }
2174
2175    impl TestWindow {
2176        fn new() -> Self {
2177            TestWindow { dirty: true, term_area: Rect::default(), id: None }
2178        }
2179    }
2180
2181    impl From<Option<usize>> for TestWindow {
2182        fn from(id: Option<usize>) -> Self {
2183            TestWindow { dirty: true, term_area: Rect::default(), id }
2184        }
2185    }
2186
2187    impl TerminalCursor for TestWindow {
2188        fn get_term_cursor(&self) -> Option<(u16, u16)> {
2189            (self.term_area.left(), self.term_area.top()).into()
2190        }
2191    }
2192
2193    impl WindowOps<TestApp> for TestWindow {
2194        fn dup(&self, _: &mut Store<TestApp>) -> Self {
2195            self.clone()
2196        }
2197
2198        fn close(&mut self, flags: CloseFlags, store: &mut Store<TestApp>) -> bool {
2199            if !flags.contains(CloseFlags::WRITE) {
2200                return true;
2201            }
2202
2203            let flags = if flags.contains(CloseFlags::FORCE) {
2204                WriteFlags::FORCE
2205            } else {
2206                WriteFlags::NONE
2207            };
2208
2209            self.write(None, flags, store).is_ok()
2210        }
2211
2212        fn write(
2213            &mut self,
2214            _: Option<&str>,
2215            flags: WriteFlags,
2216            _: &mut Store<TestApp>,
2217        ) -> UIResult<EditInfo, TestApp> {
2218            if flags.contains(WriteFlags::FORCE) {
2219                self.dirty = false;
2220                Ok(None)
2221            } else {
2222                Err(UIError::Failure("Cannot write".into()))
2223            }
2224        }
2225
2226        fn draw(&mut self, area: Rect, _: &mut Buffer, _: bool, _: &mut Store<TestApp>) {
2227            self.term_area = area;
2228        }
2229
2230        fn get_completions(&self) -> Option<CompletionList> {
2231            None
2232        }
2233
2234        fn get_cursor_word(&self, _: &WordStyle) -> Option<String> {
2235            None
2236        }
2237
2238        fn get_selected_word(&self) -> Option<String> {
2239            None
2240        }
2241    }
2242
2243    impl Window<TestApp> for TestWindow {
2244        fn id(&self) -> Option<usize> {
2245            self.id
2246        }
2247
2248        fn get_win_title(&self, _: &mut Store<TestApp>) -> Line {
2249            Line::from("Window Title")
2250        }
2251
2252        fn open(id: Option<usize>, _: &mut Store<TestApp>) -> UIResult<Self, TestApp> {
2253            Ok(TestWindow::from(id))
2254        }
2255
2256        fn find(name: String, _: &mut Store<TestApp>) -> UIResult<Self, TestApp> {
2257            match name.parse::<usize>() {
2258                Ok(n) => Ok(TestWindow::from(Some(n))),
2259                Err(e) => Err(EditError::from(e).into()),
2260            }
2261        }
2262
2263        fn posn(index: usize, _: &mut Store<TestApp>) -> UIResult<Self, TestApp> {
2264            Ok(TestWindow::from(Some(index)))
2265        }
2266
2267        fn unnamed(_: &mut Store<TestApp>) -> UIResult<Self, TestApp> {
2268            Ok(TestWindow::from(None))
2269        }
2270    }
2271
2272    impl<C> Jumpable<C, TestApp> for TestWindow {
2273        fn jump(
2274            &mut self,
2275            _: PositionList,
2276            _: MoveDir1D,
2277            count: usize,
2278            _: &C,
2279        ) -> UIResult<usize, TestApp> {
2280            return Ok(count);
2281        }
2282    }
2283
2284    fn mkstorectx() -> (Store<TestApp>, EditContext) {
2285        (Store::default(), EditContext::default())
2286    }
2287
2288    fn mktree() -> (WindowLayoutState<TestWindow, TestApp>, Store<TestApp>, EditContext) {
2289        let (store, ctx) = mkstorectx();
2290        let tree = WindowLayoutState::new(TestWindow::new());
2291
2292        return (tree, store, ctx);
2293    }
2294
2295    fn three_by_three() -> (WindowLayoutState<TestWindow, TestApp>, Store<TestApp>, EditContext) {
2296        /*
2297         * Set up a 3x3 grid, and start in bottom right. The splitting order is important here, to
2298         * create a more interesting tree. The final result (and their window indexes) looks like:
2299         *
2300         * +---+---+---+
2301         * | 0 | 1 | 6 |
2302         * +---+---+---+
2303         * | 2 | 3 | 7 |
2304         * +---+---+---+
2305         * | 4 | 5 | 8 |
2306         * +---+---+---+
2307         */
2308        let (mut tree, mut store, ctx) = mktree();
2309
2310        window_split!(tree, Axis::Vertical, MoveDir1D::Previous, Count::Exact(1), &ctx, store);
2311        window_split!(tree, Axis::Horizontal, MoveDir1D::Previous, Count::Exact(2), &ctx, store);
2312        window_split!(tree, Axis::Vertical, MoveDir1D::Previous, Count::Exact(1), &ctx, store);
2313        window_focus_off!(tree, 3, &ctx, store);
2314        window_split!(tree, Axis::Vertical, MoveDir1D::Previous, Count::Exact(1), &ctx, store);
2315        window_focus_off!(tree, 5, &ctx, store);
2316        window_split!(tree, Axis::Vertical, MoveDir1D::Previous, Count::Exact(1), &ctx, store);
2317        window_focus_off!(tree, 7, &ctx, store);
2318        window_split!(tree, Axis::Horizontal, MoveDir1D::Next, Count::Exact(2), &ctx, store);
2319
2320        let mut idx = 0;
2321
2322        while let Some(slot) = tree.root.get_mut(idx) {
2323            slot.get_mut().id = Some(idx);
2324            idx += 1;
2325        }
2326
2327        return (tree, store, ctx);
2328    }
2329
2330    #[test]
2331    fn test_tree_get() {
2332        let (mut tree, _, _) = three_by_three();
2333
2334        assert_eq!(tree.root.get(10), None);
2335        assert_eq!(tree.root.get(100), None);
2336
2337        assert_eq!(tree.root.get_mut(10), None);
2338        assert_eq!(tree.root.get_mut(100), None);
2339
2340        assert_eq!(tree.root.get(0).unwrap().get().id, Some(0));
2341        assert_eq!(tree.root.get(8).unwrap().get().id, Some(8));
2342
2343        assert_eq!(tree.root.get_mut(0).unwrap().get().id, Some(0));
2344        assert_eq!(tree.root.get_mut(8).unwrap().get().id, Some(8));
2345    }
2346
2347    #[test]
2348    fn test_tree_rotations() {
2349        let (mut store, ctx) = mkstorectx();
2350        let flags = CloseFlags::QUIT;
2351
2352        // Repeatedly delete the last window.
2353        let mut tree = WindowLayoutState::new(TestWindow::new());
2354        window_split!(tree, Axis::Horizontal, MoveDir1D::Next, Count::Exact(99), &ctx, store);
2355        assert_eq!(tree.root.size(), 100);
2356
2357        for n in 0..100 {
2358            window_close!(tree, WindowTarget::Single(fc!(100 - n)), flags, &ctx, store);
2359            assert_eq!(tree.root.size(), 99 - n);
2360        }
2361
2362        // Repeatedly delete the first window.
2363        let mut tree = WindowLayoutState::new(TestWindow::new());
2364        window_split!(tree, Axis::Horizontal, MoveDir1D::Next, Count::Exact(99), &ctx, store);
2365        assert_eq!(tree.root.size(), 100);
2366
2367        for n in 0..100 {
2368            window_close!(tree, WindowTarget::Single(fc!(1)), flags, &ctx, store);
2369            assert_eq!(tree.root.size(), 99 - n);
2370        }
2371
2372        // Generate random insertions and then deletions.
2373        let mut tree = WindowLayoutState::new(TestWindow::new());
2374        let mut rng = rand::thread_rng();
2375
2376        assert_eq!(tree.root.size(), 1);
2377
2378        for n in 0..999 {
2379            let size = tree.root.size();
2380            let target = rng.gen_range(1..=size);
2381            window_focus_off!(tree, target, &ctx, store);
2382            window_split!(tree, Axis::Horizontal, MoveDir1D::Next, Count::Exact(1), &ctx, store);
2383            assert_eq!(tree.root.size(), 2 + n);
2384        }
2385
2386        for n in 0..1000 {
2387            let size = tree.root.size();
2388            let target = rng.gen_range(1..=size);
2389            window_close!(tree, WindowTarget::Single(fc!(target)), flags, &ctx, store);
2390            assert_eq!(tree.root.size(), 999 - n);
2391        }
2392    }
2393
2394    #[test]
2395    fn test_window_split() {
2396        let (mut store, ctx) = mkstorectx();
2397        let mut tree = WindowLayoutState::new(TestWindow::new());
2398
2399        assert_eq!(tree.root.size(), 1);
2400        assert_eq!(tree.focused, 0);
2401
2402        window_split!(tree, Axis::Horizontal, MoveDir1D::Previous, Count::Contextual, &ctx, store);
2403        assert_eq!(tree.root.size(), 2);
2404        assert_eq!(tree.focused, 0);
2405
2406        window_split!(tree, Axis::Vertical, MoveDir1D::Previous, Count::Contextual, &ctx, store);
2407        assert_eq!(tree.root.size(), 3);
2408        assert_eq!(tree.focused, 0);
2409
2410        window_split!(tree, Axis::Vertical, MoveDir1D::Next, Count::Contextual, &ctx, store);
2411        assert_eq!(tree.root.size(), 4);
2412        assert_eq!(tree.focused, 1);
2413    }
2414
2415    #[test]
2416    fn test_window_nav_1d() {
2417        let (mut store, ctx) = mkstorectx();
2418        let mut tree = WindowLayoutState::new(TestWindow::new());
2419
2420        window_split!(tree, Axis::Horizontal, MoveDir1D::Previous, Count::Exact(5), &ctx, store);
2421        assert_eq!(tree.root.size(), 6);
2422        assert_eq!(tree.focused, 0);
2423
2424        window_focus_1d!(tree, MoveDir1D::Next, Count::Exact(2), &ctx, store);
2425        assert_eq!(tree.focused, 2);
2426
2427        window_focus_1d!(tree, MoveDir1D::Previous, Count::Exact(1), &ctx, store);
2428        assert_eq!(tree.focused, 1);
2429
2430        window_focus_1d!(tree, MoveDir1D::Previous, Count::Exact(2), &ctx, store);
2431        assert_eq!(tree.focused, 5);
2432
2433        window_focus_1d!(tree, MoveDir1D::Next, Count::Exact(1), &ctx, store);
2434        assert_eq!(tree.focused, 0);
2435    }
2436
2437    #[test]
2438    fn test_window_nav_2d() {
2439        let (mut tree, mut store, ctx) = three_by_three();
2440
2441        assert_eq!(tree.root.size(), 9);
2442        assert_eq!(tree.focused, 8);
2443
2444        // Draw so we know where the windows are inside the terminal.
2445        let mut buffer = Buffer::empty(Rect::new(0, 0, 100, 100));
2446        let area = Rect::new(0, 0, 100, 100);
2447        let widget = WindowLayout::new(&mut store);
2448        widget.render(area, &mut buffer, &mut tree);
2449
2450        // Move left 2 windows.
2451        window_focus_2d!(tree, MoveDir2D::Left, Count::Exact(2), &ctx, store);
2452        assert_eq!(tree.focused, 4);
2453
2454        // Move right 1 window.
2455        window_focus_2d!(tree, MoveDir2D::Right, Count::Exact(1), &ctx, store);
2456        assert_eq!(tree.focused, 5);
2457
2458        // Move up 2 windows.
2459        window_focus_2d!(tree, MoveDir2D::Up, Count::Exact(2), &ctx, store);
2460        assert_eq!(tree.focused, 1);
2461
2462        // We're already at the top, so we can't move up anymore.
2463        window_focus_2d!(tree, MoveDir2D::Up, Count::Exact(1), &ctx, store);
2464        assert_eq!(tree.focused, 1);
2465
2466        // Move down 1 window.
2467        window_focus_2d!(tree, MoveDir2D::Down, Count::Exact(1), &ctx, store);
2468        assert_eq!(tree.focused, 3);
2469
2470        // Try to move left 3 windows, stopping at the left side.
2471        window_focus_2d!(tree, MoveDir2D::Left, Count::Exact(3), &ctx, store);
2472        assert_eq!(tree.focused, 2);
2473    }
2474
2475    #[test]
2476    fn test_window_close() {
2477        let (mut tree, mut store, ctx) = three_by_three();
2478        let flags = CloseFlags::NONE;
2479
2480        let target = WindowTarget::Single(fc!(8));
2481        assert!(window_close!(tree, target, flags, &ctx, store).is_none());
2482        assert_eq!(tree.root.size(), 8);
2483
2484        let target = WindowTarget::Single(fc!(6));
2485        assert!(window_close!(tree, target, flags, &ctx, store).is_none());
2486        assert_eq!(tree.root.size(), 7);
2487
2488        let target = WindowTarget::Single(fc!(3));
2489        assert!(window_close!(tree, target, flags, &ctx, store).is_none());
2490        assert_eq!(tree.root.size(), 6);
2491
2492        let target = WindowTarget::Single(fc!(2));
2493        assert!(window_close!(tree, target, flags, &ctx, store).is_none());
2494        assert_eq!(tree.root.size(), 5);
2495
2496        /*
2497         * We should now have a tree that visually looks like:
2498         *
2499         * +---+---+
2500         * | 0 | 6 |
2501         * +---|   |
2502         * | 3 +---+
2503         * +---|   |
2504         * | 4 | 8 |
2505         * +---+---+
2506         */
2507        assert_eq!(tree.root.get(0).unwrap().get().id, Some(0));
2508        assert_eq!(tree.root.get(1).unwrap().get().id, Some(3));
2509        assert_eq!(tree.root.get(2).unwrap().get().id, Some(4));
2510        assert_eq!(tree.root.get(3).unwrap().get().id, Some(6));
2511        assert_eq!(tree.root.get(4).unwrap().get().id, Some(8));
2512
2513        let target = WindowTarget::Single(fc!(2));
2514        assert!(window_close!(tree, target, flags, &ctx, store).is_none());
2515        assert_eq!(tree.root.size(), 4);
2516
2517        let target = WindowTarget::Single(fc!(3));
2518        assert!(window_close!(tree, target, flags, &ctx, store).is_none());
2519        assert_eq!(tree.root.size(), 3);
2520
2521        let target = WindowTarget::Single(fc!(3));
2522        assert!(window_close!(tree, target, flags, &ctx, store).is_none());
2523        assert_eq!(tree.root.size(), 2);
2524
2525        let target = WindowTarget::Single(fc!(1));
2526        assert!(window_close!(tree, target, flags, &ctx, store).is_none());
2527        assert_eq!(tree.root.size(), 1);
2528
2529        // Can't close last window because flags are NONE.
2530        let target = WindowTarget::Single(fc!(1));
2531        assert!(window_close!(tree, target, flags, &ctx, store).is_none());
2532        assert_eq!(tree.root.size(), 1);
2533
2534        // Passing QUIT does the job.
2535        let target = WindowTarget::Single(fc!(1));
2536        let flags = CloseFlags::QUIT;
2537        assert!(window_close!(tree, target, flags, &ctx, store).is_none());
2538        assert_eq!(tree.root.size(), 0);
2539    }
2540
2541    #[test]
2542    fn test_window_close_allbut() {
2543        for idx in 1usize..=9usize {
2544            let (mut tree, mut store, ctx) = three_by_three();
2545            let target = WindowTarget::AllBut(fc!(idx));
2546            assert!(window_close!(tree, target, CloseFlags::NONE, &ctx, store).is_none());
2547            assert_eq!(tree.root.size(), 1);
2548            assert_eq!(tree.get().unwrap().id, Some(idx.saturating_sub(1)));
2549        }
2550    }
2551
2552    #[test]
2553    fn test_window_close_allbut_resize_lens() {
2554        let (mut tree, mut store, ctx) = mktree();
2555        window_split!(tree, Axis::Horizontal, MoveDir1D::Previous, Count::Exact(1), &ctx, store);
2556        let mut buffer = Buffer::empty(Rect::new(0, 0, 100, 100));
2557        let area = Rect::new(0, 0, 100, 100);
2558        let idx = 1;
2559
2560        // Draw so that everything gets an initial area.
2561        WindowLayout::new(&mut store).render(area, &mut buffer, &mut tree);
2562
2563        // Resize the tree so we have lengths in ResizeInfo.
2564        tree.resize(idx, Axis::Horizontal, SizeChange::Increase(10));
2565        assert!(tree.info.resized.lengths.is_some());
2566
2567        // Now close, which should reset the ResizeInfo.
2568        let target = WindowTarget::AllBut(fc!(idx + 1));
2569        assert!(window_close!(tree, target, CloseFlags::NONE, &ctx, store).is_none());
2570        assert!(tree.info.resized.lengths.is_none());
2571        assert_eq!(tree.root.size(), 1);
2572        assert_eq!(tree.focused, 0);
2573
2574        // Now draw again, which shouldn't trip our assertions.
2575        WindowLayout::new(&mut store).render(area, &mut buffer, &mut tree);
2576    }
2577
2578    #[test]
2579    fn test_window_close_all() {
2580        let (mut tree, mut store, ctx) = three_by_three();
2581        let target = WindowTarget::All;
2582        assert!(window_close!(tree, target, CloseFlags::NONE, &ctx, store).is_none());
2583        assert_eq!(tree.root.size(), 0);
2584    }
2585
2586    #[test]
2587    fn test_window_write() {
2588        let (mut tree, mut store, ctx) = mktree();
2589        let target = WindowTarget::All;
2590
2591        // Window starts out with a dirty state.
2592        assert_eq!(tree.get().unwrap().dirty, true);
2593
2594        // Write without FORCE fails.
2595        let res = tree.window_write(&target, None, WriteFlags::NONE, &ctx, &mut store);
2596        assert!(res.is_err());
2597
2598        // Write with FORCE succeeds.
2599        tree.window_write(&target, None, WriteFlags::FORCE, &ctx, &mut store)
2600            .unwrap();
2601        assert_eq!(tree.get().unwrap().dirty, false);
2602    }
2603
2604    #[test]
2605    fn test_window_exchange() {
2606        let (mut tree, mut store, ctx) = three_by_three();
2607
2608        // Verify starting conditions.
2609        assert_eq!(tree.root.size(), 9);
2610        assert_eq!(tree.focused, 8);
2611        assert_eq!(tree.root.get(0).unwrap().get().id, Some(0));
2612        assert_eq!(tree.root.get(8).unwrap().get().id, Some(8));
2613
2614        // Swap window 9 with window 1.
2615        window_exchange!(tree, &fc!(1), &ctx, store);
2616        assert_eq!(tree.root.size(), 9);
2617        assert_eq!(tree.focused, 8);
2618        assert_eq!(tree.root.get(0).unwrap().get().id, Some(8));
2619        assert_eq!(tree.root.get(8).unwrap().get().id, Some(0));
2620
2621        // Swap the new window 9 with window 8.
2622        window_exchange!(tree, &fc!(8), &ctx, store);
2623        assert_eq!(tree.root.size(), 9);
2624        assert_eq!(tree.focused, 8);
2625        assert_eq!(tree.root.get(0).unwrap().get().id, Some(8));
2626        assert_eq!(tree.root.get(7).unwrap().get().id, Some(0));
2627        assert_eq!(tree.root.get(8).unwrap().get().id, Some(7));
2628
2629        // Focus on window 1, and swap with window 6.
2630        window_focus_off!(tree, 1, &ctx, store);
2631        assert_eq!(tree.focused, 0);
2632
2633        window_exchange!(tree, &fc!(6), &ctx, store);
2634        assert_eq!(tree.root.size(), 9);
2635        assert_eq!(tree.focused, 0);
2636        assert_eq!(tree.root.get(0).unwrap().get().id, Some(5));
2637        assert_eq!(tree.root.get(5).unwrap().get().id, Some(8));
2638        assert_eq!(tree.root.get(7).unwrap().get().id, Some(0));
2639        assert_eq!(tree.root.get(8).unwrap().get().id, Some(7));
2640    }
2641
2642    #[test]
2643    fn test_window_move_side() {
2644        let (mut tree, mut store, ctx) = three_by_three();
2645
2646        assert_eq!(tree.root.size(), 9);
2647        assert_eq!(tree.root.dimensions(), (3, 3));
2648        assert_eq!(tree.focused, 8);
2649
2650        window_move_side!(tree, MoveDir2D::Left, &ctx, store);
2651        assert_eq!(tree.root.size(), 9);
2652        assert_eq!(tree.root.dimensions(), (4, 3));
2653        assert_eq!(tree.focused, 0);
2654
2655        window_move_side!(tree, MoveDir2D::Right, &ctx, store);
2656        assert_eq!(tree.root.size(), 9);
2657        assert_eq!(tree.root.dimensions(), (4, 3));
2658        assert_eq!(tree.focused, 8);
2659
2660        window_move_side!(tree, MoveDir2D::Up, &ctx, store);
2661        assert_eq!(tree.root.size(), 9);
2662        assert_eq!(tree.root.dimensions(), (3, 4));
2663        assert_eq!(tree.focused, 0);
2664
2665        window_move_side!(tree, MoveDir2D::Down, &ctx, store);
2666        assert_eq!(tree.root.size(), 9);
2667        assert_eq!(tree.focused, 8);
2668        assert_eq!(tree.root.dimensions(), (3, 4));
2669    }
2670
2671    #[test]
2672    fn test_window_resize_vertical_increase() {
2673        let (mut tree, mut store, ctx) = three_by_three();
2674        let mut buffer = Buffer::empty(Rect::new(0, 0, 100, 100));
2675        let area = Rect::new(0, 0, 100, 100);
2676
2677        // Draw so that everything gets an initial area.
2678        WindowLayout::new(&mut store).render(area, &mut buffer, &mut tree);
2679
2680        assert_eq!(tree.root.size(), 9);
2681        assert_eq!(tree.root.dimensions(), (3, 3));
2682        assert_eq!(tree.focused, 8);
2683
2684        // Top row before resizing.
2685        assert_eq!(tree.root.get(0).unwrap().get().term_area, Rect::new(0, 0, 34, 34));
2686        assert_eq!(tree.root.get(1).unwrap().get().term_area, Rect::new(34, 0, 33, 34));
2687        assert_eq!(tree.root.get(6).unwrap().get().term_area, Rect::new(67, 0, 33, 34));
2688
2689        // Middle row before resizing.
2690        assert_eq!(tree.root.get(2).unwrap().get().term_area, Rect::new(0, 34, 34, 33));
2691        assert_eq!(tree.root.get(3).unwrap().get().term_area, Rect::new(34, 34, 33, 33));
2692        assert_eq!(tree.root.get(7).unwrap().get().term_area, Rect::new(67, 34, 33, 33));
2693
2694        // Bottom row before resizing.
2695        assert_eq!(tree.root.get(4).unwrap().get().term_area, Rect::new(0, 67, 34, 33));
2696        assert_eq!(tree.root.get(5).unwrap().get().term_area, Rect::new(34, 67, 33, 33));
2697        assert_eq!(tree.root.get(8).unwrap().get().term_area, Rect::new(67, 67, 33, 33));
2698
2699        // Resize window 8.
2700        window_resize!(
2701            tree,
2702            FocusChange::Offset(8.into(), false),
2703            Vertical,
2704            SizeChange::Increase(5.into()),
2705            &ctx,
2706            store
2707        );
2708
2709        // Draw again so that we update the saved term_area.
2710        WindowLayout::new(&mut store).render(area, &mut buffer, &mut tree);
2711
2712        // Top row after resizing.
2713        assert_eq!(tree.root.get(0).unwrap().get().term_area, Rect::new(0, 0, 34, 34));
2714        assert_eq!(tree.root.get(1).unwrap().get().term_area, Rect::new(34, 0, 28, 34));
2715        assert_eq!(tree.root.get(6).unwrap().get().term_area, Rect::new(62, 0, 38, 34));
2716
2717        // Middle row after resizing.
2718        assert_eq!(tree.root.get(2).unwrap().get().term_area, Rect::new(0, 34, 34, 33));
2719        assert_eq!(tree.root.get(3).unwrap().get().term_area, Rect::new(34, 34, 28, 33));
2720        assert_eq!(tree.root.get(7).unwrap().get().term_area, Rect::new(62, 34, 38, 33));
2721
2722        // Bottom row after resizing has changed 5 and 8.
2723        assert_eq!(tree.root.get(4).unwrap().get().term_area, Rect::new(0, 67, 34, 33));
2724        assert_eq!(tree.root.get(5).unwrap().get().term_area, Rect::new(34, 67, 28, 33));
2725        assert_eq!(tree.root.get(8).unwrap().get().term_area, Rect::new(62, 67, 38, 33));
2726    }
2727
2728    #[test]
2729    fn test_window_open_vertical_size() {
2730        let (mut tree, mut store, _) = three_by_three();
2731        let mut buffer = Buffer::empty(Rect::new(0, 0, 100, 100));
2732        let area = Rect::new(0, 0, 100, 100);
2733
2734        // Draw so that everything gets an initial area.
2735        WindowLayout::new(&mut store).render(area, &mut buffer, &mut tree);
2736
2737        assert_eq!(tree.root.size(), 9);
2738        assert_eq!(tree.root.dimensions(), (3, 3));
2739        assert_eq!(tree.focused, 8);
2740
2741        // Top row before opening window.
2742        assert_eq!(tree.root.get(0).unwrap().get().term_area, Rect::new(0, 0, 34, 34));
2743        assert_eq!(tree.root.get(1).unwrap().get().term_area, Rect::new(34, 0, 33, 34));
2744        assert_eq!(tree.root.get(6).unwrap().get().term_area, Rect::new(67, 0, 33, 34));
2745
2746        // Middle row before opening window.
2747        assert_eq!(tree.root.get(2).unwrap().get().term_area, Rect::new(0, 34, 34, 33));
2748        assert_eq!(tree.root.get(3).unwrap().get().term_area, Rect::new(34, 34, 33, 33));
2749        assert_eq!(tree.root.get(7).unwrap().get().term_area, Rect::new(67, 34, 33, 33));
2750
2751        // Bottom row before opening window.
2752        assert_eq!(tree.root.get(4).unwrap().get().term_area, Rect::new(0, 67, 34, 33));
2753        assert_eq!(tree.root.get(5).unwrap().get().term_area, Rect::new(34, 67, 33, 33));
2754        assert_eq!(tree.root.get(8).unwrap().get().term_area, Rect::new(67, 67, 33, 33));
2755
2756        // Open a window that is 5 columns wide.
2757        let w = TestWindow::new();
2758        tree.open(w, Some(5), Vertical, MoveDir1D::Previous);
2759
2760        // We should now have one more window.
2761        assert_eq!(tree.root.size(), 10);
2762
2763        // Draw again so that we update the saved term_area.
2764        WindowLayout::new(&mut store).render(area, &mut buffer, &mut tree);
2765
2766        // Top row after opening the window remains the same.
2767        assert_eq!(tree.root.get(0).unwrap().get().term_area, Rect::new(0, 0, 34, 34));
2768        assert_eq!(tree.root.get(1).unwrap().get().term_area, Rect::new(34, 0, 33, 34));
2769        assert_eq!(tree.root.get(6).unwrap().get().term_area, Rect::new(67, 0, 33, 34));
2770
2771        // Middle row after opening the window remains the same.
2772        assert_eq!(tree.root.get(2).unwrap().get().term_area, Rect::new(0, 34, 34, 33));
2773        assert_eq!(tree.root.get(3).unwrap().get().term_area, Rect::new(34, 34, 33, 33));
2774        assert_eq!(tree.root.get(7).unwrap().get().term_area, Rect::new(67, 34, 33, 33));
2775
2776        // Bottom row after opening window has changed widths.
2777        assert_eq!(tree.root.get(4).unwrap().get().term_area, Rect::new(0, 67, 34, 33));
2778        assert_eq!(tree.root.get(5).unwrap().get().term_area, Rect::new(34, 67, 33, 33));
2779        assert_eq!(tree.root.get(8).unwrap().get().term_area, Rect::new(67, 67, 5, 33));
2780        assert_eq!(tree.root.get(9).unwrap().get().term_area, Rect::new(72, 67, 28, 33));
2781    }
2782
2783    #[test]
2784    fn test_window_open_vertical_no_size() {
2785        let (mut tree, mut store, _) = three_by_three();
2786        let mut buffer = Buffer::empty(Rect::new(0, 0, 100, 100));
2787        let area = Rect::new(0, 0, 100, 100);
2788
2789        // Draw so that everything gets an initial area.
2790        WindowLayout::new(&mut store).render(area, &mut buffer, &mut tree);
2791
2792        assert_eq!(tree.root.size(), 9);
2793        assert_eq!(tree.root.dimensions(), (3, 3));
2794        assert_eq!(tree.focused, 8);
2795
2796        // Top row before opening window.
2797        assert_eq!(tree.root.get(0).unwrap().get().term_area, Rect::new(0, 0, 34, 34));
2798        assert_eq!(tree.root.get(1).unwrap().get().term_area, Rect::new(34, 0, 33, 34));
2799        assert_eq!(tree.root.get(6).unwrap().get().term_area, Rect::new(67, 0, 33, 34));
2800
2801        // Middle row before opening window.
2802        assert_eq!(tree.root.get(2).unwrap().get().term_area, Rect::new(0, 34, 34, 33));
2803        assert_eq!(tree.root.get(3).unwrap().get().term_area, Rect::new(34, 34, 33, 33));
2804        assert_eq!(tree.root.get(7).unwrap().get().term_area, Rect::new(67, 34, 33, 33));
2805
2806        // Bottom row before opening window.
2807        assert_eq!(tree.root.get(4).unwrap().get().term_area, Rect::new(0, 67, 34, 33));
2808        assert_eq!(tree.root.get(5).unwrap().get().term_area, Rect::new(34, 67, 33, 33));
2809        assert_eq!(tree.root.get(8).unwrap().get().term_area, Rect::new(67, 67, 33, 33));
2810
2811        // Open a new window without specifying a height.
2812        let w = TestWindow::new();
2813        tree.open(w, None, Vertical, MoveDir1D::Previous);
2814
2815        // We should now have one more window.
2816        assert_eq!(tree.root.size(), 10);
2817
2818        // Draw again so that we update the saved term_area.
2819        WindowLayout::new(&mut store).render(area, &mut buffer, &mut tree);
2820
2821        // Top row after opening the window remains the same.
2822        assert_eq!(tree.root.get(0).unwrap().get().term_area, Rect::new(0, 0, 25, 34));
2823        assert_eq!(tree.root.get(1).unwrap().get().term_area, Rect::new(25, 0, 25, 34));
2824        assert_eq!(tree.root.get(6).unwrap().get().term_area, Rect::new(50, 0, 50, 34));
2825
2826        // Middle row after opening the window remains the same.
2827        assert_eq!(tree.root.get(2).unwrap().get().term_area, Rect::new(0, 34, 25, 33));
2828        assert_eq!(tree.root.get(3).unwrap().get().term_area, Rect::new(25, 34, 25, 33));
2829        assert_eq!(tree.root.get(7).unwrap().get().term_area, Rect::new(50, 34, 50, 33));
2830
2831        // Bottom row after opening window has changed widths.
2832        assert_eq!(tree.root.get(4).unwrap().get().term_area, Rect::new(0, 67, 25, 33));
2833        assert_eq!(tree.root.get(5).unwrap().get().term_area, Rect::new(25, 67, 25, 33));
2834        assert_eq!(tree.root.get(8).unwrap().get().term_area, Rect::new(50, 67, 25, 33));
2835        assert_eq!(tree.root.get(9).unwrap().get().term_area, Rect::new(75, 67, 25, 33));
2836    }
2837
2838    #[test]
2839    fn test_window_switch_and_jump() {
2840        let (mut store, ctx) = mkstorectx();
2841        let mut tree = WindowLayoutState::new(TestWindow::new());
2842        let next = MoveDir1D::Next;
2843        let prev = MoveDir1D::Previous;
2844        let jl = PositionList::JumpList;
2845
2846        window_switch!(tree, OpenTarget::Name("1".into()), &ctx, store);
2847        assert_eq!(tree.get().unwrap().id, Some(1));
2848
2849        window_switch!(tree, OpenTarget::Name("2".into()), &ctx, store);
2850        assert_eq!(tree.get().unwrap().id, Some(2));
2851
2852        window_switch!(tree, OpenTarget::Application(3.into()), &ctx, store);
2853        assert_eq!(tree.get().unwrap().id, Some(3));
2854
2855        window_switch!(tree, OpenTarget::Alternate, &ctx, store);
2856        assert_eq!(tree.get().unwrap().id, Some(2));
2857
2858        let count = tree.jump(jl, prev, 1, &ctx).unwrap();
2859        assert_eq!(count, 0);
2860        assert_eq!(tree.get().unwrap().id, Some(3));
2861
2862        let count = tree.jump(jl, prev, 1, &ctx).unwrap();
2863        assert_eq!(count, 0);
2864        assert_eq!(tree.get().unwrap().id, Some(1));
2865
2866        let count = tree.jump(jl, prev, 1, &ctx).unwrap();
2867        assert_eq!(count, 0);
2868        assert_eq!(tree.get().unwrap().id, None);
2869
2870        let count = tree.jump(jl, next, 2, &ctx).unwrap();
2871        assert_eq!(count, 0);
2872        assert_eq!(tree.get().unwrap().id, Some(3));
2873
2874        let count = tree.jump(jl, next, 2, &ctx).unwrap();
2875        assert_eq!(count, 1);
2876        assert_eq!(tree.get().unwrap().id, Some(2));
2877    }
2878
2879    #[test]
2880    fn test_layout_as_description() {
2881        use WindowLayoutDescription::{Split, Window};
2882
2883        let (mut tree, mut store, _) = three_by_three();
2884        let mut buffer = Buffer::empty(Rect::new(0, 0, 60, 60));
2885        let area = Rect::new(0, 0, 60, 60);
2886        tree._focus(3);
2887
2888        // Draw so that everything gets an initial area.
2889        WindowLayout::new(&mut store).render(area, &mut buffer, &mut tree);
2890
2891        let desc1 = tree.as_description();
2892        let exp = WindowLayoutDescription::<TestApp>::Split {
2893            children: vec![Split {
2894                children: vec![
2895                    Split {
2896                        children: vec![
2897                            Split {
2898                                children: vec![
2899                                    Window { window: Some(0), length: Some(20) },
2900                                    Window { window: Some(1), length: Some(20) },
2901                                ],
2902                                length: Some(20),
2903                            },
2904                            Split {
2905                                children: vec![
2906                                    Window { window: Some(2), length: Some(20) },
2907                                    Window { window: Some(3), length: Some(20) },
2908                                ],
2909                                length: Some(20),
2910                            },
2911                            Split {
2912                                children: vec![
2913                                    Window { window: Some(4), length: Some(20) },
2914                                    Window { window: Some(5), length: Some(20) },
2915                                ],
2916                                length: Some(20),
2917                            },
2918                        ],
2919                        length: Some(40),
2920                    },
2921                    Split {
2922                        children: vec![
2923                            Window { window: Some(6), length: Some(20) },
2924                            Window { window: Some(7), length: Some(20) },
2925                            Window { window: Some(8), length: Some(20) },
2926                        ],
2927                        length: Some(20),
2928                    },
2929                ],
2930                length: Some(60),
2931            }],
2932            length: None,
2933        };
2934        assert_eq!(desc1.layout, exp);
2935        assert_eq!(desc1.focused, 3);
2936        assert_eq!(desc1.zoomed, false);
2937
2938        // Turn back into a layout, and then generate a new description to show it's the same.
2939        let tree = desc1
2940            .clone()
2941            .to_layout::<TestWindow>(tree.info.area.into(), &mut store)
2942            .unwrap();
2943        assert_eq!(tree.as_description(), desc1);
2944
2945        // Test against an example JSON serialization to test naming.
2946        let serialized = serde_json::to_string_pretty(&desc1).unwrap();
2947        let exp = include_str!("../../tests/window-layout.json");
2948        assert_eq!(serialized, exp.trim_end());
2949    }
2950}