1use crate::{Rect, Tree, ViewId};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct Tabs {
8 tabs: Vec<Tree>,
9 current: usize,
10}
11
12impl Tabs {
13 pub fn new(view: ViewId) -> Self {
14 Self {
15 tabs: vec![Tree::new(view)],
16 current: 0,
17 }
18 }
19
20 pub fn current(&self) -> &Tree {
21 &self.tabs[self.current]
22 }
23
24 pub fn current_mut(&mut self) -> &mut Tree {
25 &mut self.tabs[self.current]
26 }
27
28 pub fn index(&self) -> usize {
29 self.current
30 }
31
32 pub fn len(&self) -> usize {
33 self.tabs.len()
34 }
35
36 pub fn is_empty(&self) -> bool {
37 false }
39
40 pub fn open(&mut self, view: ViewId) {
42 self.current += 1;
43 self.tabs.insert(self.current, Tree::new(view));
44 }
45
46 pub fn close(&mut self) -> bool {
49 if self.tabs.len() == 1 {
50 return false;
51 }
52 self.tabs.remove(self.current);
53 self.current = self.current.min(self.tabs.len() - 1);
55 true
56 }
57
58 pub fn next(&mut self) {
60 self.current = (self.current + 1) % self.tabs.len();
61 }
62
63 pub fn previous(&mut self) {
64 self.current = (self.current + self.tabs.len() - 1) % self.tabs.len();
65 }
66
67 pub fn views(&self) -> Vec<ViewId> {
69 self.tabs.iter().flat_map(|t| t.views()).collect()
70 }
71
72 pub fn layout(&self, area: Rect) -> Vec<crate::Pane> {
74 self.current().layout(area)
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::Axis;
82
83 fn v(n: u64) -> ViewId {
84 ViewId(n)
85 }
86
87 #[test]
88 fn a_session_starts_with_one_tab() {
89 let tabs = Tabs::new(v(1));
90 assert_eq!(tabs.len(), 1);
91 assert_eq!(tabs.index(), 0);
92 assert_eq!(tabs.current().focused(), v(1));
93 }
94
95 #[test]
96 fn opening_a_tab_switches_to_it_and_inserts_after_the_current_one() {
97 let mut tabs = Tabs::new(v(1));
98 tabs.open(v(2));
99 assert_eq!(tabs.len(), 2);
100 assert_eq!(tabs.index(), 1);
101 assert_eq!(tabs.current().focused(), v(2));
102
103 tabs.previous();
105 tabs.open(v(3));
106 assert_eq!(tabs.index(), 1);
107 assert_eq!(tabs.views(), [v(1), v(3), v(2)]);
108 }
109
110 #[test]
111 fn tabs_wrap_in_both_directions() {
112 let mut tabs = Tabs::new(v(1));
113 tabs.open(v(2));
114 tabs.open(v(3));
115 assert_eq!(tabs.index(), 2);
116
117 tabs.next();
118 assert_eq!(tabs.index(), 0, "past the end comes back to the first");
119 tabs.previous();
120 assert_eq!(tabs.index(), 2, "and before the first is the last");
121 }
122
123 #[test]
124 fn closing_a_tab_lands_on_the_one_that_took_its_place() {
125 let mut tabs = Tabs::new(v(1));
126 tabs.open(v(2));
127 tabs.open(v(3));
128 tabs.previous(); assert!(tabs.close());
131 assert_eq!(tabs.len(), 2);
132 assert_eq!(tabs.index(), 1);
133 assert_eq!(tabs.current().focused(), v(3));
134 }
135
136 #[test]
137 fn closing_the_last_tab_lands_on_the_new_last() {
138 let mut tabs = Tabs::new(v(1));
139 tabs.open(v(2));
140 assert!(tabs.close());
141 assert_eq!(tabs.index(), 0);
142 assert_eq!(tabs.current().focused(), v(1));
143 }
144
145 #[test]
146 fn the_final_tab_cannot_be_closed() {
147 let mut tabs = Tabs::new(v(1));
150 assert!(!tabs.close());
151 assert_eq!(tabs.len(), 1);
152 }
153
154 #[test]
155 fn each_tab_keeps_its_own_layout() {
156 let mut tabs = Tabs::new(v(1));
157 tabs.current_mut().split(Axis::Columns, v(2));
158 assert_eq!(tabs.current().len(), 2);
159
160 tabs.open(v(3));
161 assert_eq!(tabs.current().len(), 1, "a new tab starts with one window");
162
163 tabs.previous();
164 assert_eq!(tabs.current().len(), 2, "the split is still there");
165 }
166}