1use super::Window;
2use crate::PaneGeometry;
3
4impl Window {
5 #[must_use]
7 pub const fn is_zoomed(&self) -> bool {
8 self.zoomed
9 }
10
11 pub(crate) fn toggle_zoom(&mut self, pane_index: u32) -> bool {
12 if self.pane(pane_index).is_none() {
13 return self.zoomed;
14 }
15
16 if self.zoomed {
17 self.auto_unzoom();
18 return false;
19 }
20
21 if self.panes.len() <= 1 {
22 return false;
23 }
24
25 let selected = self.select_pane(pane_index);
26 debug_assert!(selected, "validated zoom target pane must be selectable");
27 self.zoomed = true;
28 self.apply_zoom_geometry();
29 true
30 }
31
32 pub(crate) fn auto_unzoom(&mut self) -> bool {
33 if !self.zoomed {
34 return false;
35 }
36
37 self.zoomed = false;
38 self.recalculate_geometry();
39 true
40 }
41
42 pub(crate) fn push_zoom(&mut self, restore: bool) -> bool {
47 let was_zoomed = self.zoomed;
48 if was_zoomed {
49 self.auto_unzoom();
50 }
51 self.zoom_restore_pending = was_zoomed && restore;
52 was_zoomed
53 }
54
55 pub(crate) fn pop_zoom(&mut self) {
59 if self.zoom_restore_pending {
60 self.zoom_restore_pending = false;
61 if self.panes.len() > 1 {
62 self.zoomed = true;
63 self.apply_zoom_geometry();
64 }
65 }
66 }
67
68 pub(crate) fn apply_zoom_geometry(&mut self) {
69 let size = self.size;
70 let active_pane = self.active_pane;
71 if let Some(pane) = self.pane_mut(active_pane) {
72 pane.set_geometry(PaneGeometry::new(0, 0, size.cols, size.rows));
73 }
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use rmux_proto::{LayoutName, ResizePaneAdjustment, TerminalSize};
81
82 fn window_with_two_panes() -> Window {
83 let mut window = Window::new(TerminalSize { cols: 80, rows: 24 });
84 window.split_after_position(0);
85 window
86 }
87
88 #[test]
89 fn toggle_zoom_expands_the_target_pane_and_auto_unzoom_restores_layout() {
90 let mut window = window_with_two_panes();
91 let pane_zero = window.pane(0).expect("pane 0 exists").geometry();
92 let pane_one = window.pane(1).expect("pane 1 exists").geometry();
93
94 assert!(window.toggle_zoom(1));
95
96 assert!(window.is_zoomed());
97 assert_eq!(window.active_pane_index(), 1);
98 assert_eq!(
99 window.pane(1).expect("pane 1 exists").geometry(),
100 PaneGeometry::new(0, 0, 80, 24)
101 );
102
103 assert!(window.auto_unzoom());
104 assert!(!window.is_zoomed());
105 assert_eq!(window.pane(0).expect("pane 0 exists").geometry(), pane_zero);
106 assert_eq!(window.pane(1).expect("pane 1 exists").geometry(), pane_one);
107 }
108
109 #[test]
110 fn single_pane_zoom_toggle_is_a_noop() {
111 let mut window = Window::new(TerminalSize { cols: 80, rows: 24 });
112 let geometry = window.pane(0).expect("pane 0 exists").geometry();
113
114 assert!(!window.toggle_zoom(0));
115
116 assert!(!window.is_zoomed());
117 assert_eq!(window.pane(0).expect("pane 0 exists").geometry(), geometry);
118 }
119
120 #[test]
121 fn select_pane_unzooms_before_switching_active_panes() {
122 let mut window = window_with_two_panes();
123 assert!(window.toggle_zoom(1));
124
125 assert!(window.select_pane(0));
126
127 assert!(!window.is_zoomed());
128 assert_eq!(window.active_pane_index(), 0);
129 assert_ne!(
130 window.pane(1).expect("pane 1 exists").geometry(),
131 PaneGeometry::new(0, 0, 80, 24)
132 );
133 }
134
135 #[test]
136 fn absolute_resize_auto_unzooms_before_recalculating_layout() {
137 let mut window = window_with_two_panes();
138 assert!(window.toggle_zoom(1));
139
140 window.resize_main_pane(ResizePaneAdjustment::AbsoluteWidth { columns: 20 });
141
142 assert!(!window.is_zoomed());
143 assert_eq!(
144 window.pane(0).expect("pane 0 exists").geometry(),
145 PaneGeometry::new(0, 0, 20, 24)
146 );
147 }
148
149 #[test]
150 fn set_layout_auto_unzooms_before_recalculating_layout() {
151 let mut window = window_with_two_panes();
152 assert!(window.toggle_zoom(1));
153
154 window.set_layout(LayoutName::MainHorizontal);
155
156 assert!(!window.is_zoomed());
157 assert_eq!(
158 window.pane(0).expect("pane 0 exists").geometry(),
159 PaneGeometry::new(0, 0, 80, 22)
160 );
161 assert_eq!(
162 window.pane(1).expect("pane 1 exists").geometry(),
163 PaneGeometry::new(0, 23, 80, 1)
164 );
165 }
166
167 #[test]
168 fn set_even_layout_auto_unzooms_before_recalculating_layout() {
169 let mut window = window_with_two_panes();
170 assert!(window.toggle_zoom(1));
171
172 window.set_layout(LayoutName::EvenHorizontal);
173
174 assert!(!window.is_zoomed());
175 assert_eq!(
176 window.pane(0).expect("pane 0 exists").geometry(),
177 PaneGeometry::new(0, 0, 39, 24)
178 );
179 assert_eq!(
180 window.pane(1).expect("pane 1 exists").geometry(),
181 PaneGeometry::new(40, 0, 40, 24)
182 );
183 }
184
185 #[test]
186 fn push_zoom_without_restore_leaves_the_window_unzoomed_after_pop() {
187 let mut window = window_with_two_panes();
188 assert!(window.toggle_zoom(1));
189
190 assert!(window.push_zoom(false));
191 assert!(!window.is_zoomed());
192 assert!(!window.zoom_restore_pending);
193
194 window.select_pane(0);
195 window.pop_zoom();
196
197 assert!(!window.is_zoomed());
198 assert_eq!(window.active_pane_index(), 0);
199 assert_eq!(
200 window.pane(0).expect("pane 0 exists").geometry(),
201 PaneGeometry::new(0, 0, 40, 24)
202 );
203 }
204
205 #[test]
206 fn push_zoom_with_restore_rezooms_the_new_active_pane_on_pop() {
207 let mut window = window_with_two_panes();
208 assert!(window.toggle_zoom(1));
209
210 assert!(window.push_zoom(true));
211 assert!(!window.is_zoomed());
212 assert!(window.zoom_restore_pending);
213
214 assert!(window.select_pane(0));
215 window.pop_zoom();
216
217 assert!(window.is_zoomed());
218 assert!(!window.zoom_restore_pending);
219 assert_eq!(window.active_pane_index(), 0);
220 assert_eq!(
221 window.pane(0).expect("pane 0 exists").geometry(),
222 PaneGeometry::new(0, 0, 80, 24)
223 );
224 assert_eq!(
225 window.pane(1).expect("pane 1 exists").geometry(),
226 PaneGeometry::new(41, 0, 39, 24)
227 );
228 }
229}