1use std::collections::HashMap;
4
5use crate::{tag::TagHandle, util::Axis};
6
7use super::{Gaps, LayoutDir, LayoutGenerator, LayoutNode};
8
9#[derive(Debug, Clone, PartialEq)]
11pub struct Line {
12 pub outer_gaps: Gaps,
14 pub inner_gaps: Gaps,
16 pub direction: LayoutDir,
18 pub reversed: bool,
20}
21
22impl LayoutGenerator for Line {
23 fn layout(&self, window_count: u32) -> LayoutNode {
24 let root = LayoutNode::new_with_label("builtin.line");
25 root.set_gaps(self.outer_gaps);
26 root.set_dir(self.direction);
27
28 if window_count == 0 {
29 return root;
30 }
31
32 let children = match self.reversed {
33 false => (0..window_count)
34 .map(|idx| {
35 let node = LayoutNode::new_with_traversal_index(idx);
36 node.set_gaps(self.inner_gaps);
37 node
38 })
39 .collect::<Vec<_>>(),
40 true => (0..window_count)
41 .rev()
42 .map(|idx| {
43 let node = LayoutNode::new_with_traversal_index(idx);
44 node.set_gaps(self.inner_gaps);
45 node
46 })
47 .collect(),
48 };
49
50 root.set_children(children);
51
52 root
53 }
54}
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
58pub enum MasterSide {
59 Left,
61 Right,
63 Top,
65 Bottom,
67}
68
69#[derive(Clone, Copy, Debug, PartialEq)]
72pub struct MasterStack {
73 pub outer_gaps: Gaps,
75 pub inner_gaps: Gaps,
77 pub master_factor: f32,
81 pub master_side: MasterSide,
83 pub master_count: u32,
85 pub reversed: bool,
89}
90
91impl Default for MasterStack {
92 fn default() -> Self {
93 Self {
94 outer_gaps: Gaps::from(4.0),
95 inner_gaps: Gaps::from(4.0),
96 master_factor: 0.5,
97 master_side: MasterSide::Left,
98 master_count: 1,
99 reversed: false,
100 }
101 }
102}
103
104impl LayoutGenerator for MasterStack {
105 fn layout(&self, window_count: u32) -> LayoutNode {
106 let root = LayoutNode::new_with_label("builtin.master_stack");
107 root.set_gaps(self.outer_gaps);
108 root.set_dir(match self.master_side {
109 MasterSide::Left | MasterSide::Right => LayoutDir::Row,
110 MasterSide::Top | MasterSide::Bottom => LayoutDir::Column,
111 });
112
113 if window_count == 0 {
114 return root;
115 }
116
117 let master_factor = self.master_factor.clamp(0.1, 0.9);
118
119 let (master_tv_idx, stack_tv_idx) = match self.reversed {
120 true => (1, 0),
121 false => (0, 1),
122 };
123
124 let master_count = u32::min(self.master_count, window_count);
125
126 let line = Line {
127 outer_gaps: 0.0.into(),
128 inner_gaps: self.inner_gaps,
129 direction: match self.master_side {
130 MasterSide::Left | MasterSide::Right => LayoutDir::Column,
131 MasterSide::Top | MasterSide::Bottom => LayoutDir::Row,
132 },
133 reversed: self.reversed,
134 };
135
136 let master_side = line.layout(master_count);
137 master_side.set_traversal_index(master_tv_idx);
138 master_side.set_size_proportion(master_factor * 10.0);
139
140 if window_count <= self.master_count {
141 root.add_child(master_side);
142 return root;
143 }
144
145 let stack_count = window_count - u32::min(self.master_count, window_count);
146 let stack_side = line.layout(stack_count);
147 stack_side.set_traversal_index(stack_tv_idx);
148 stack_side.set_size_proportion((1.0 - master_factor) * 10.0);
149
150 match self.master_side {
151 MasterSide::Left | MasterSide::Top => {
152 root.set_children([master_side, stack_side]);
153 }
154 MasterSide::Right | MasterSide::Bottom => {
155 root.set_children([stack_side, master_side]);
156 }
157 }
158
159 root
160 }
161}
162
163#[derive(Clone, Debug, PartialEq)]
166pub struct Dwindle {
167 pub outer_gaps: Gaps,
169 pub inner_gaps: Gaps,
171}
172
173impl Default for Dwindle {
174 fn default() -> Self {
175 Self {
176 inner_gaps: 4.0.into(),
177 outer_gaps: 4.0.into(),
178 }
179 }
180}
181
182impl LayoutGenerator for Dwindle {
183 fn layout(&self, win_count: u32) -> LayoutNode {
184 let root = LayoutNode::new_with_label("builtin.dwindle");
185 root.set_gaps(self.outer_gaps);
186
187 if win_count == 0 {
188 return root;
189 }
190
191 if win_count == 1 {
192 let child = LayoutNode::new();
193 child.set_gaps(self.inner_gaps);
194 root.add_child(child);
195 return root;
196 }
197
198 let mut current_node = root.clone();
199
200 for i in 0..win_count - 1 {
201 if current_node != root {
202 current_node.set_label(Some("builtin.dwindle.split"));
203 current_node.set_gaps(0.0);
204 }
205
206 let child1 = LayoutNode::new_with_traversal_index(0);
207 child1.set_dir(match i % 2 == 0 {
208 true => LayoutDir::Column,
209 false => LayoutDir::Row,
210 });
211 child1.set_gaps(self.inner_gaps);
212 current_node.add_child(child1);
213
214 let child2 = LayoutNode::new_with_traversal_index(1);
215 child2.set_dir(match i % 2 == 0 {
216 true => LayoutDir::Column,
217 false => LayoutDir::Row,
218 });
219 child2.set_gaps(self.inner_gaps);
220 current_node.add_child(child2.clone());
221
222 current_node = child2;
223 }
224
225 root
226 }
227}
228
229#[derive(Clone, Debug, PartialEq)]
234pub struct Spiral {
235 pub outer_gaps: Gaps,
237 pub inner_gaps: Gaps,
239}
240
241impl Default for Spiral {
242 fn default() -> Self {
243 Self {
244 inner_gaps: 4.0.into(),
245 outer_gaps: 4.0.into(),
246 }
247 }
248}
249
250impl LayoutGenerator for Spiral {
251 fn layout(&self, win_count: u32) -> LayoutNode {
252 let root = LayoutNode::new_with_label("builtin.spiral");
253 root.set_gaps(self.outer_gaps);
254
255 if win_count == 0 {
256 return root;
257 }
258
259 if win_count == 1 {
260 let child = LayoutNode::new();
261 child.set_gaps(self.inner_gaps);
262 root.add_child(child);
263 return root;
264 }
265
266 let mut current_node = root.clone();
267
268 for i in 0..win_count - 1 {
269 if current_node != root {
270 current_node.set_label(Some("builtin.spiral.split"));
271 current_node.set_gaps(0.0);
272 }
273
274 let child1 = LayoutNode::new_with_traversal_index(0);
275 child1.set_dir(match i % 2 == 0 {
276 true => LayoutDir::Column,
277 false => LayoutDir::Row,
278 });
279 child1.set_gaps(self.inner_gaps);
280 current_node.add_child(child1.clone());
281
282 let child2 = LayoutNode::new_with_traversal_index(1);
283 child2.set_dir(match i % 2 == 0 {
284 true => LayoutDir::Column,
285 false => LayoutDir::Row,
286 });
287 child2.set_gaps(self.inner_gaps);
288 current_node.add_child(child2.clone());
289
290 current_node = match i % 4 {
291 0 | 1 => child2,
292 2 | 3 => child1,
293 _ => unreachable!(),
294 };
295 }
296
297 root
298 }
299}
300
301#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
303pub enum CornerLocation {
304 TopLeft,
306 TopRight,
308 BottomLeft,
310 BottomRight,
312}
313
314#[derive(Debug, Clone, Copy, PartialEq)]
317pub struct Corner {
318 pub outer_gaps: Gaps,
320 pub inner_gaps: Gaps,
322 pub corner_width_factor: f32,
324 pub corner_height_factor: f32,
326 pub corner_loc: CornerLocation,
328}
329
330impl Default for Corner {
331 fn default() -> Self {
332 Self {
333 inner_gaps: 4.0.into(),
334 outer_gaps: 4.0.into(),
335 corner_width_factor: 0.5,
336 corner_height_factor: 0.5,
337 corner_loc: CornerLocation::TopLeft,
338 }
339 }
340}
341
342impl LayoutGenerator for Corner {
343 fn layout(&self, win_count: u32) -> LayoutNode {
344 let root = LayoutNode::new_with_label("builtin.corner");
345 root.set_gaps(self.outer_gaps);
346
347 if win_count == 0 {
348 return root;
349 }
350
351 if win_count == 1 {
352 let child = LayoutNode::new();
353 child.set_gaps(self.inner_gaps);
354 root.add_child(child);
355 return root;
356 }
357
358 let corner_width_factor = self.corner_width_factor.clamp(0.1, 0.9);
359 let corner_height_factor = self.corner_height_factor.clamp(0.1, 0.9);
360
361 let corner_and_horiz_stack_node =
362 LayoutNode::new_with_label_and_index("builtin.corner.corner_and_stack", 0);
363 corner_and_horiz_stack_node.set_dir(LayoutDir::Column);
364 corner_and_horiz_stack_node.set_size_proportion(corner_width_factor * 10.0);
365
366 let vert_count = (win_count - 1).div_ceil(2);
367 let horiz_count = (win_count - 1) / 2;
368
369 let vert_stack = Line {
370 outer_gaps: 0.0.into(),
371 inner_gaps: self.inner_gaps,
372 direction: LayoutDir::Column,
373 reversed: false,
374 };
375
376 let vert_stack_node = vert_stack.layout(vert_count);
377 vert_stack_node.set_size_proportion((1.0 - corner_width_factor) * 10.0);
378 vert_stack_node.set_traversal_index(1);
379
380 root.set_children(match self.corner_loc {
381 CornerLocation::TopLeft | CornerLocation::BottomLeft => {
382 [corner_and_horiz_stack_node.clone(), vert_stack_node.clone()]
383 }
384 CornerLocation::TopRight | CornerLocation::BottomRight => {
385 [vert_stack_node.clone(), corner_and_horiz_stack_node.clone()]
386 }
387 });
388
389 if horiz_count == 0 {
390 corner_and_horiz_stack_node.set_gaps(self.inner_gaps);
391 return root;
392 }
393
394 let corner_node = LayoutNode::new_with_traversal_index(0);
395 corner_node.set_size_proportion(corner_height_factor * 10.0);
396 corner_node.set_gaps(self.inner_gaps);
397
398 let horiz_stack = Line {
399 outer_gaps: 0.0.into(),
400 inner_gaps: self.inner_gaps,
401 direction: LayoutDir::Row,
402 reversed: false,
403 };
404
405 let horiz_stack_node = horiz_stack.layout(horiz_count);
406 horiz_stack_node.set_size_proportion((1.0 - corner_height_factor) * 10.0);
407 horiz_stack_node.set_traversal_index(1);
408
409 corner_and_horiz_stack_node.set_children(match self.corner_loc {
410 CornerLocation::TopLeft | CornerLocation::TopRight => {
411 [corner_node, horiz_stack_node.clone()]
412 }
413 CornerLocation::BottomLeft | CornerLocation::BottomRight => {
414 [horiz_stack_node.clone(), corner_node]
415 }
416 });
417
418 let traversal_overrides = (0..win_count).map(|idx| (idx, vec![(idx % 2 == 1) as u32]));
419
420 root.set_traversal_overrides(traversal_overrides);
421
422 root
423 }
424}
425
426#[derive(Copy, Clone, Debug, PartialEq)]
429pub struct Fair {
430 pub outer_gaps: Gaps,
432 pub inner_gaps: Gaps,
434 pub axis: Axis,
436}
437
438impl Default for Fair {
439 fn default() -> Self {
440 Self {
441 inner_gaps: 4.0.into(),
442 outer_gaps: 4.0.into(),
443 axis: Axis::Vertical,
444 }
445 }
446}
447
448impl LayoutGenerator for Fair {
449 fn layout(&self, win_count: u32) -> LayoutNode {
450 let root = LayoutNode::new_with_label("builtin.fair");
451 root.set_gaps(self.outer_gaps);
452
453 if win_count == 0 {
454 return root;
455 }
456
457 if win_count == 1 {
458 let child = LayoutNode::new();
459 child.set_gaps(self.inner_gaps);
460 root.add_child(child);
461 return root;
462 }
463
464 if win_count == 2 {
465 let child = LayoutNode::new();
466 child.set_gaps(self.inner_gaps);
467 root.add_child(child);
468 let child2 = LayoutNode::new();
469 child2.set_gaps(self.inner_gaps);
470 root.add_child(child2);
471 return root;
472 }
473
474 let line_count = (win_count as f32).sqrt().round() as u32;
475
476 let mut wins_per_line = Vec::new();
477
478 let max_per_line = if win_count > line_count * line_count {
479 line_count + 1
480 } else {
481 line_count
482 };
483
484 for i in 1..=win_count {
485 let index = (i as f32 / max_per_line as f32).ceil() as usize - 1;
486 if wins_per_line.get(index).is_none() {
487 wins_per_line.push(0);
488 }
489 wins_per_line[index] += 1;
490 }
491
492 let line = Line {
493 outer_gaps: 0.0.into(),
494 inner_gaps: self.inner_gaps,
495 direction: match self.axis {
496 Axis::Horizontal => LayoutDir::Row,
497 Axis::Vertical => LayoutDir::Column,
498 },
499 reversed: false,
500 };
501
502 let lines = wins_per_line.into_iter().map(|win_ct| line.layout(win_ct));
503
504 root.set_children(lines);
505
506 root.set_dir(match self.axis {
507 Axis::Horizontal => LayoutDir::Column,
508 Axis::Vertical => LayoutDir::Row,
509 });
510
511 root
512 }
513}
514
515pub struct Cycle<T> {
518 pub layouts: Vec<T>,
520 tag_indices: HashMap<u32, usize>,
521 current_tag: Option<TagHandle>,
522}
523
524impl<T: LayoutGenerator + ?Sized> LayoutGenerator for Box<T> {
525 fn layout(&self, window_count: u32) -> LayoutNode {
526 (**self).layout(window_count)
527 }
528}
529
530impl<T: LayoutGenerator + ?Sized> LayoutGenerator for std::sync::Arc<T> {
531 fn layout(&self, window_count: u32) -> LayoutNode {
532 (**self).layout(window_count)
533 }
534}
535
536impl<T: LayoutGenerator + ?Sized> LayoutGenerator for std::rc::Rc<T> {
537 fn layout(&self, window_count: u32) -> LayoutNode {
538 (**self).layout(window_count)
539 }
540}
541
542impl<T: LayoutGenerator> Cycle<T> {
543 pub fn new(layouts: impl IntoIterator<Item = T>) -> Self {
560 Self {
561 layouts: layouts.into_iter().collect(),
562 tag_indices: HashMap::default(),
563 current_tag: None,
564 }
565 }
566
567 pub fn cycle_layout_forward(&mut self, tag: &TagHandle) {
569 let index = self.tag_indices.entry(tag.id).or_default();
570 *index += 1;
571 if *index >= self.layouts.len() {
572 *index = 0;
573 }
574 }
575
576 pub fn cycle_layout_backward(&mut self, tag: &TagHandle) {
578 let index = self.tag_indices.entry(tag.id).or_default();
579 if let Some(i) = index.checked_sub(1) {
580 *index = i;
581 } else {
582 *index = self.layouts.len().saturating_sub(1);
583 }
584 }
585
586 pub fn current_layout(&self, tag: &TagHandle) -> Option<&T> {
590 self.layouts
591 .get(self.tag_indices.get(&tag.id).copied().unwrap_or_default())
592 }
593
594 pub fn set_current_tag(&mut self, tag: TagHandle) {
596 self.current_tag = Some(tag);
597 }
598}
599
600impl<T: LayoutGenerator> LayoutGenerator for Cycle<T> {
601 fn layout(&self, window_count: u32) -> LayoutNode {
602 let Some(current_tag) = self.current_tag.as_ref() else {
603 return LayoutNode::new();
604 };
605 let Some(current_layout) = self.current_layout(current_tag) else {
606 return LayoutNode::new();
607 };
608 current_layout.layout(window_count)
609 }
610}