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_label(Some("builtin.master_stack.master_side"));
138 master_side.set_traversal_index(master_tv_idx);
139 master_side.set_size_proportion(master_factor * 10.0);
140
141 if window_count <= self.master_count {
142 root.add_child(master_side);
143 return root;
144 }
145
146 let stack_count = window_count - u32::min(self.master_count, window_count);
147 let stack_side = line.layout(stack_count);
148 stack_side.set_label(Some("builtin.master_stack.stack_side"));
149 stack_side.set_traversal_index(stack_tv_idx);
150 stack_side.set_size_proportion((1.0 - master_factor) * 10.0);
151
152 match self.master_side {
153 MasterSide::Left | MasterSide::Top => {
154 root.set_children([master_side, stack_side]);
155 }
156 MasterSide::Right | MasterSide::Bottom => {
157 root.set_children([stack_side, master_side]);
158 }
159 }
160
161 root
162 }
163}
164
165#[derive(Clone, Debug, PartialEq)]
168pub struct Dwindle {
169 pub outer_gaps: Gaps,
171 pub inner_gaps: Gaps,
173}
174
175impl Default for Dwindle {
176 fn default() -> Self {
177 Self {
178 inner_gaps: 4.0.into(),
179 outer_gaps: 4.0.into(),
180 }
181 }
182}
183
184impl LayoutGenerator for Dwindle {
185 fn layout(&self, win_count: u32) -> LayoutNode {
186 let root = LayoutNode::new_with_label("builtin.dwindle");
187 root.set_gaps(self.outer_gaps);
188
189 if win_count == 0 {
190 return root;
191 }
192
193 if win_count == 1 {
194 let child = LayoutNode::new();
195 child.set_gaps(self.inner_gaps);
196 root.add_child(child);
197 return root;
198 }
199
200 let mut current_node = root.clone();
201
202 for i in 0..win_count - 1 {
203 if current_node != root {
204 current_node.set_gaps(0.0);
205 }
206
207 let child1 = LayoutNode::new_with_traversal_index(0);
208 child1.set_dir(match i % 2 == 0 {
209 true => LayoutDir::Column,
210 false => LayoutDir::Row,
211 });
212 child1.set_gaps(self.inner_gaps);
213 child1.set_label(Some(format!("builtin.dwindle.split.{i}.0")));
214 current_node.add_child(child1);
215
216 let child2 = LayoutNode::new_with_traversal_index(1);
217 child2.set_dir(match i % 2 == 0 {
218 true => LayoutDir::Column,
219 false => LayoutDir::Row,
220 });
221 child2.set_gaps(self.inner_gaps);
222 child2.set_label(Some(format!("builtin.dwindle.split.{i}.1")));
223 current_node.add_child(child2.clone());
224
225 current_node = child2;
226 }
227
228 root
229 }
230}
231
232#[derive(Clone, Debug, PartialEq)]
237pub struct Spiral {
238 pub outer_gaps: Gaps,
240 pub inner_gaps: Gaps,
242}
243
244impl Default for Spiral {
245 fn default() -> Self {
246 Self {
247 inner_gaps: 4.0.into(),
248 outer_gaps: 4.0.into(),
249 }
250 }
251}
252
253impl LayoutGenerator for Spiral {
254 fn layout(&self, win_count: u32) -> LayoutNode {
255 let root = LayoutNode::new_with_label("builtin.spiral");
256 root.set_gaps(self.outer_gaps);
257
258 if win_count == 0 {
259 return root;
260 }
261
262 if win_count == 1 {
263 let child = LayoutNode::new();
264 child.set_gaps(self.inner_gaps);
265 root.add_child(child);
266 return root;
267 }
268
269 let mut current_node = root.clone();
270
271 for i in 0..win_count - 1 {
272 if current_node != root {
273 current_node.set_gaps(0.0);
274 }
275
276 let child1 = LayoutNode::new();
277 child1.set_dir(match i % 2 == 0 {
278 true => LayoutDir::Column,
279 false => LayoutDir::Row,
280 });
281 child1.set_gaps(self.inner_gaps);
282 child1.set_label(Some(format!("builtin.spiral.split.{i}.0")));
283 current_node.add_child(child1.clone());
284
285 let child2 = LayoutNode::new_with_traversal_index(1);
286 child2.set_dir(match i % 2 == 0 {
287 true => LayoutDir::Column,
288 false => LayoutDir::Row,
289 });
290 child2.set_gaps(self.inner_gaps);
291 child2.set_label(Some(format!("builtin.spiral.split.{i}.1")));
292 current_node.add_child(child2.clone());
293
294 current_node = match i % 4 {
295 0 | 1 => {
296 child1.set_traversal_index(0);
297 child2.set_traversal_index(1);
298 child2
299 }
300 2 | 3 => {
301 child1.set_traversal_index(1);
302 child2.set_traversal_index(0);
303 child1
304 }
305 _ => unreachable!(),
306 };
307 }
308
309 root
310 }
311}
312
313#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
315pub enum CornerLocation {
316 TopLeft,
318 TopRight,
320 BottomLeft,
322 BottomRight,
324}
325
326#[derive(Debug, Clone, Copy, PartialEq)]
329pub struct Corner {
330 pub outer_gaps: Gaps,
332 pub inner_gaps: Gaps,
334 pub corner_width_factor: f32,
336 pub corner_height_factor: f32,
338 pub corner_loc: CornerLocation,
340}
341
342impl Default for Corner {
343 fn default() -> Self {
344 Self {
345 inner_gaps: 4.0.into(),
346 outer_gaps: 4.0.into(),
347 corner_width_factor: 0.5,
348 corner_height_factor: 0.5,
349 corner_loc: CornerLocation::TopLeft,
350 }
351 }
352}
353
354impl LayoutGenerator for Corner {
355 fn layout(&self, win_count: u32) -> LayoutNode {
356 let root = LayoutNode::new_with_label("builtin.corner");
357 root.set_gaps(self.outer_gaps);
358
359 if win_count == 0 {
360 return root;
361 }
362
363 if win_count == 1 {
364 let child = LayoutNode::new();
365 child.set_gaps(self.inner_gaps);
366 root.add_child(child);
367 return root;
368 }
369
370 let corner_width_factor = self.corner_width_factor.clamp(0.1, 0.9);
371 let corner_height_factor = self.corner_height_factor.clamp(0.1, 0.9);
372
373 let corner_and_horiz_stack_node =
374 LayoutNode::new_with_label_and_index("builtin.corner.corner_and_stack", 0);
375 corner_and_horiz_stack_node.set_dir(LayoutDir::Column);
376 corner_and_horiz_stack_node.set_size_proportion(corner_width_factor * 10.0);
377
378 let vert_count = (win_count - 1).div_ceil(2);
379 let horiz_count = (win_count - 1) / 2;
380
381 let vert_stack = Line {
382 outer_gaps: 0.0.into(),
383 inner_gaps: self.inner_gaps,
384 direction: LayoutDir::Column,
385 reversed: false,
386 };
387
388 let vert_stack_node = vert_stack.layout(vert_count);
389 vert_stack_node.set_size_proportion((1.0 - corner_width_factor) * 10.0);
390 vert_stack_node.set_traversal_index(1);
391
392 root.set_children(match self.corner_loc {
393 CornerLocation::TopLeft | CornerLocation::BottomLeft => {
394 [corner_and_horiz_stack_node.clone(), vert_stack_node.clone()]
395 }
396 CornerLocation::TopRight | CornerLocation::BottomRight => {
397 [vert_stack_node.clone(), corner_and_horiz_stack_node.clone()]
398 }
399 });
400
401 if horiz_count == 0 {
402 corner_and_horiz_stack_node.set_gaps(self.inner_gaps);
403 return root;
404 }
405
406 let corner_node = LayoutNode::new_with_traversal_index(0);
407 corner_node.set_size_proportion(corner_height_factor * 10.0);
408 corner_node.set_gaps(self.inner_gaps);
409
410 let horiz_stack = Line {
411 outer_gaps: 0.0.into(),
412 inner_gaps: self.inner_gaps,
413 direction: LayoutDir::Row,
414 reversed: false,
415 };
416
417 let horiz_stack_node = horiz_stack.layout(horiz_count);
418 horiz_stack_node.set_size_proportion((1.0 - corner_height_factor) * 10.0);
419 horiz_stack_node.set_traversal_index(1);
420
421 corner_and_horiz_stack_node.set_children(match self.corner_loc {
422 CornerLocation::TopLeft | CornerLocation::TopRight => {
423 [corner_node, horiz_stack_node.clone()]
424 }
425 CornerLocation::BottomLeft | CornerLocation::BottomRight => {
426 [horiz_stack_node.clone(), corner_node]
427 }
428 });
429
430 let traversal_overrides = (0..win_count).map(|idx| (idx, vec![(idx % 2 == 1) as u32]));
431
432 root.set_traversal_overrides(traversal_overrides);
433
434 root
435 }
436}
437
438#[derive(Copy, Clone, Debug, PartialEq)]
441pub struct Fair {
442 pub outer_gaps: Gaps,
444 pub inner_gaps: Gaps,
446 pub axis: Axis,
448}
449
450impl Default for Fair {
451 fn default() -> Self {
452 Self {
453 inner_gaps: 4.0.into(),
454 outer_gaps: 4.0.into(),
455 axis: Axis::Vertical,
456 }
457 }
458}
459
460impl LayoutGenerator for Fair {
461 fn layout(&self, win_count: u32) -> LayoutNode {
462 let root = LayoutNode::new_with_label("builtin.fair");
463 root.set_gaps(self.outer_gaps);
464
465 if win_count == 0 {
466 return root;
467 }
468
469 if win_count == 1 {
470 let child = LayoutNode::new();
471 child.set_gaps(self.inner_gaps);
472 child.set_label(Some("builtin.fair.line.0"));
473 root.add_child(child);
474 return root;
475 }
476
477 if win_count == 2 {
478 let child = LayoutNode::new();
479 child.set_gaps(self.inner_gaps);
480 child.set_label(Some("builtin.fair.line.0"));
481 root.add_child(child);
482 let child2 = LayoutNode::new();
483 child2.set_gaps(self.inner_gaps);
484 child2.set_label(Some("builtin.fair.line.1"));
485 root.add_child(child2);
486 return root;
487 }
488
489 let line_count = (win_count as f32).sqrt().round() as u32;
490
491 let mut wins_per_line = Vec::new();
492
493 let max_per_line = if win_count > line_count * line_count {
494 line_count + 1
495 } else {
496 line_count
497 };
498
499 for i in 1..=win_count {
500 let index = (i as f32 / max_per_line as f32).ceil() as usize - 1;
501 if wins_per_line.get(index).is_none() {
502 wins_per_line.push(0);
503 }
504 wins_per_line[index] += 1;
505 }
506
507 let line = Line {
508 outer_gaps: 0.0.into(),
509 inner_gaps: self.inner_gaps,
510 direction: match self.axis {
511 Axis::Horizontal => LayoutDir::Row,
512 Axis::Vertical => LayoutDir::Column,
513 },
514 reversed: false,
515 };
516
517 let lines = wins_per_line.into_iter().enumerate().map(|(i, win_ct)| {
518 let node = line.layout(win_ct);
519 node.set_label(Some(format!("builtin.fair.line.{i}")));
520 node
521 });
522
523 root.set_children(lines);
524
525 root.set_dir(match self.axis {
526 Axis::Horizontal => LayoutDir::Column,
527 Axis::Vertical => LayoutDir::Row,
528 });
529
530 root
531 }
532}
533
534#[derive(Copy, Clone, Debug, Default, PartialEq)]
541pub struct Floating {}
542
543impl LayoutGenerator for Floating {
544 fn layout(&self, _win_count: u32) -> LayoutNode {
545 LayoutNode::new_with_label("builtin.floating")
546 }
547}
548
549pub struct Cycle<T> {
552 pub layouts: Vec<T>,
554 tag_indices: HashMap<u32, usize>,
555 current_tag: Option<TagHandle>,
556}
557
558impl<T: LayoutGenerator + ?Sized> LayoutGenerator for Box<T> {
559 fn layout(&self, window_count: u32) -> LayoutNode {
560 (**self).layout(window_count)
561 }
562}
563
564impl<T: LayoutGenerator + ?Sized> LayoutGenerator for std::sync::Arc<T> {
565 fn layout(&self, window_count: u32) -> LayoutNode {
566 (**self).layout(window_count)
567 }
568}
569
570impl<T: LayoutGenerator + ?Sized> LayoutGenerator for std::rc::Rc<T> {
571 fn layout(&self, window_count: u32) -> LayoutNode {
572 (**self).layout(window_count)
573 }
574}
575
576impl<T> Cycle<T> {
577 pub fn new(layouts: impl IntoIterator<Item = T>) -> Self {
594 Self {
595 layouts: layouts.into_iter().collect(),
596 tag_indices: HashMap::default(),
597 current_tag: None,
598 }
599 }
600
601 pub fn cycle_layout_forward(&mut self, tag: &TagHandle) {
603 let index = self.tag_indices.entry(tag.id).or_default();
604 *index += 1;
605 if *index >= self.layouts.len() {
606 *index = 0;
607 }
608 }
609
610 pub fn cycle_layout_backward(&mut self, tag: &TagHandle) {
612 let index = self.tag_indices.entry(tag.id).or_default();
613 if let Some(i) = index.checked_sub(1) {
614 *index = i;
615 } else {
616 *index = self.layouts.len().saturating_sub(1);
617 }
618 }
619
620 pub fn current_layout(&self, tag: &TagHandle) -> Option<&T> {
624 self.layouts
625 .get(self.tag_indices.get(&tag.id).copied().unwrap_or_default())
626 }
627
628 pub fn set_current_tag(&mut self, tag: TagHandle) {
630 self.current_tag = Some(tag);
631 }
632
633 pub fn current_tree_id(&self) -> u32 {
636 let tag_id = self
637 .current_tag
638 .as_ref()
639 .map(|tag| tag.id)
640 .unwrap_or_default();
641 let layout_id = self.tag_indices.get(&tag_id).copied().unwrap_or_default();
642
643 ((tag_id & u16::MAX as u32) | ((layout_id as u32 & u16::MAX as u32) << 16)) + 1
644 }
645}
646
647impl<T: LayoutGenerator> LayoutGenerator for Cycle<T> {
648 fn layout(&self, window_count: u32) -> LayoutNode {
649 let Some(current_tag) = self.current_tag.as_ref() else {
650 return LayoutNode::new();
651 };
652 let Some(current_layout) = self.current_layout(current_tag) else {
653 return LayoutNode::new();
654 };
655 current_layout.layout(window_count)
656 }
657}