1use std::cmp;
2use std::vec;
3
4use geometry::Rect;
5use geometry::Split;
6use layouts::three_column;
7use layouts::two_column;
8pub use layouts::Layout;
9use layouts::Main;
10use layouts::SecondStack;
11
12pub mod geometry;
13pub mod layouts;
14
15pub fn apply(definition: &Layout, window_count: usize, container: &Rect) -> Vec<Rect> {
16 if window_count == 0 {
17 return vec![];
18 }
19
20 let mut rects = match (&definition.columns.main, &definition.columns.second_stack) {
21 (None, _) => stack(container, window_count, definition.columns.stack.split),
22 (Some(main), None) => main_stack(container, window_count, definition, main),
23 (Some(main), Some(alternate_stack)) => {
24 stack_main_stack(container, window_count, definition, main, alternate_stack)
25 }
26 };
27
28 geometry::flip(&mut rects, definition.flip, container);
30
31 geometry::rotate(&mut rects, definition.rotate, container);
33
34 rects
35}
36
37fn stack(container: &Rect, window_count: usize, split: Option<Split>) -> Vec<Rect> {
38 geometry::split(container, window_count, split)
39}
40
41fn main_stack(
42 container: &Rect,
43 window_count: usize,
44 definition: &Layout,
45 main: &Main,
46) -> Vec<Rect> {
47 let (mut main_tile, mut stack_tile) = two_column(
48 window_count,
49 container,
50 main.count,
51 main.size,
52 definition.reserve,
53 );
54
55 match (main_tile, stack_tile) {
57 (None, None) => {}
58 (None, Some(b)) => {
59 let mut v = vec![b];
60 geometry::rotate(&mut v, definition.columns.rotate, container);
61 geometry::flip(&mut v, definition.columns.flip, container);
62 stack_tile = Some(v[0]);
63 }
64 (Some(a), None) => {
65 let mut v = vec![a];
66 geometry::rotate(&mut v, definition.columns.rotate, container);
67 geometry::flip(&mut v, definition.columns.flip, container);
68 main_tile = Some(v[0]);
69 }
70 (Some(a), Some(b)) => {
71 let mut v = vec![a, b];
72 geometry::rotate(&mut v, definition.columns.rotate, container);
73 geometry::flip(&mut v, definition.columns.flip, container);
74 main_tile = Some(v[0]);
75 stack_tile = Some(v[1]);
76 }
77 }
78
79 let mut main_tiles = vec![];
82 if let Some(tile) = main_tile {
83 main_tiles.append(&mut geometry::split(
84 &tile,
85 usize::min(main.count, window_count),
86 main.split,
87 ));
88 geometry::rotate(&mut main_tiles, main.rotate, container);
89 geometry::flip(&mut main_tiles, main.flip, container);
90 }
91
92 let mut stack_tiles = vec![];
93 if let Some(tile) = stack_tile {
94 stack_tiles.append(&mut geometry::split(
95 &tile,
96 window_count.saturating_sub(main.count),
97 definition.columns.stack.split,
98 ));
99 geometry::rotate(&mut stack_tiles, definition.columns.stack.rotate, container);
100 geometry::flip(&mut stack_tiles, definition.columns.stack.flip, container);
101 }
102
103 let mut all = vec![];
104 all.append(&mut main_tiles);
105 all.append(&mut stack_tiles);
106 all
107}
108
109fn stack_main_stack(
110 container: &Rect,
111 window_count: usize,
112 definition: &Layout,
113 main: &Main,
114 alternate_stack: &SecondStack,
115) -> Vec<Rect> {
116 let main_window_count = cmp::min(main.count, window_count);
117 let stack_window_count = window_count.saturating_sub(main_window_count);
118 let balance_stacks = definition.columns.stack.split.is_some();
119 let (left_window_count, right_window_count) = if balance_stacks {
120 let counts = geometry::remainderless_division(stack_window_count, 2);
121 (counts[0], counts[1])
122 } else {
123 (1, cmp::max(0, stack_window_count.saturating_sub(1)))
124 };
125
126 let (mut left_column, mut main_column, mut right_column) = three_column(
127 window_count,
128 container,
129 main_window_count,
130 main.size,
131 definition.reserve,
132 balance_stacks,
133 );
134
135 let mut columns = vec![];
137 columns.push(left_column.unwrap_or(Rect::new(0, 0, 0, 0)));
138 columns.push(main_column.unwrap_or(Rect::new(0, 0, 0, 0)));
139 columns.push(right_column.unwrap_or(Rect::new(0, 0, 0, 0)));
140 geometry::rotate(&mut columns, definition.columns.rotate, container);
141 geometry::flip(&mut columns, definition.columns.flip, container);
142
143 let non_empty = |rect: &&Rect| rect.surface_area() > 0;
145 left_column = columns.first().filter(non_empty).copied();
146 main_column = columns.get(1).filter(non_empty).copied();
147 right_column = columns.get(2).filter(non_empty).copied();
148
149 let mut main_tiles = vec![];
150 if let Some(tile) = main_column {
151 main_tiles.append(&mut geometry::split(&tile, main_window_count, main.split));
152 geometry::rotate(&mut main_tiles, main.rotate, container);
153 geometry::flip(&mut main_tiles, main.flip, container);
154 }
155
156 let mut left_tiles = vec![];
157 if let Some(tile) = left_column {
158 left_tiles.append(&mut geometry::split(
159 &tile,
160 left_window_count,
161 definition.columns.stack.split,
162 ));
163 geometry::rotate(&mut left_tiles, definition.columns.stack.rotate, container);
164 geometry::flip(&mut left_tiles, definition.columns.stack.flip, container);
165 }
166
167 let mut right_tiles = vec![];
168 if let Some(tile) = right_column {
169 right_tiles.append(&mut geometry::split(
170 &tile,
171 right_window_count,
172 Some(alternate_stack.split),
173 ));
174 geometry::rotate(&mut right_tiles, alternate_stack.rotate, container);
175 geometry::flip(&mut right_tiles, alternate_stack.flip, container);
176 }
177
178 let mut tiles = vec![];
179 tiles.append(&mut main_tiles);
180 tiles.append(&mut left_tiles);
181 tiles.append(&mut right_tiles);
182 tiles
183}
184
185#[cfg(test)]
186mod tests {
187 use crate::{
188 apply,
189 geometry::{Rect, Split},
190 layouts::{Columns, Layouts, SecondStack, Stack},
191 Layout,
192 };
193
194 #[test]
195 fn single_column_works_with_offset() {
196 let layout = Layout {
197 columns: Columns {
198 main: None,
199 stack: Stack {
200 split: Some(Split::Horizontal),
201 ..Default::default()
202 },
203 ..Default::default()
204 },
205 ..Default::default()
206 };
207 let rect = Rect::new(2560, 1440, 2560, 1440);
208 let rects = apply(&layout, 3, &rect);
209
210 assert_eq!(Rect::new(2560, 1440, 2560, 480), rects[0]);
211 assert_eq!(Rect::new(2560, 1920, 2560, 480), rects[1]);
212 assert_eq!(Rect::new(2560, 2400, 2560, 480), rects[2]);
213 }
214
215 #[test]
216 fn main_stack_works_with_offset() {
217 let layout = Layout::default();
218 let rect = Rect::new(2560, 1440, 2560, 1440);
219 let rects = apply(&layout, 3, &rect);
220
221 assert_eq!(Rect::new(2560, 1440, 1280, 1440), rects[0]);
222 assert_eq!(Rect::new(3840, 1440, 1280, 720), rects[1]);
223 assert_eq!(Rect::new(3840, 2160, 1280, 720), rects[2]);
224 }
225
226 #[test]
227 fn stack_main_stack_works_with_offset() {
228 let layout = Layout {
229 columns: Columns {
230 second_stack: Some(SecondStack::default()),
231 ..Default::default()
232 },
233 ..Default::default()
234 };
235 let rect = Rect::new(2560, 1440, 2560, 1440);
236 let rects = apply(&layout, 3, &rect);
237 assert_eq!(Rect::new(3200, 1440, 1280, 1440), rects[0]);
238 assert_eq!(Rect::new(2560, 1440, 640, 1440), rects[1]);
239 assert_eq!(Rect::new(4480, 1440, 640, 1440), rects[2]);
240 }
241
242 #[test]
243 fn should_never_return_more_rects_than_windows_for_any_layout() {
244 let container = Rect::new(0, 0, 40, 20);
245 let mut layouts = Layouts::default().layouts;
246
247 layouts.push(Layout {
250 name: "MultiMain".to_string(),
251 columns: Columns {
252 main: Some(crate::layouts::Main {
253 count: 2,
254 ..Default::default()
255 }),
256 ..Default::default()
257 },
258 ..Default::default()
259 });
260
261 for layout in layouts {
262 for i in 0usize..6 {
263 let rects = apply(&layout, i, &container);
264 assert!(
265 rects.len() <= i,
266 "got {}, expected <= {}, layout {}",
267 rects.len(),
268 i,
269 &layout.name
270 );
271 }
272 }
273 }
274}