1use std::cmp;
2
3use serde::{Deserialize, Serialize};
4
5use crate::geometry::{Flip, Reserve, Rotation, Size, Split};
6
7use super::defaults::{
8 center_main, center_main_balanced, center_main_fluid, dwindle, even_horizontal, even_vertical,
9 fibonacci, grid, main_and_deck, main_and_horizontal_stack, main_and_vert_stack, monocle,
10 right_main_and_vert_stack,
11};
12
13const DEFAULT_MAIN_SIZE_CHANGE_PIXEL: i32 = 50;
14const DEFAULT_MAIN_SIZE_CHANGE_PERCENTAGE: i32 = 5;
15
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub struct Layouts {
20 pub layouts: Vec<Layout>,
21}
22
23impl Eq for Layouts {}
24
25impl Layouts {
26 pub fn get(&self, name: &str) -> Option<&Layout> {
27 self.layouts.iter().find(|&l| l.name.as_str() == name)
28 }
29
30 pub fn get_mut<'a>(&'a mut self, name: &str) -> Option<&'a mut Layout> {
31 self.layouts.iter_mut().find(|l| l.name.as_str() == name)
32 }
33
34 pub fn names(&self) -> Vec<String> {
35 self.layouts.iter().map(|x| x.name.clone()).collect()
36 }
37
38 pub fn len(&self) -> usize {
39 self.layouts.len()
40 }
41
42 pub fn is_empty(&self) -> bool {
43 self.layouts.is_empty()
44 }
45
46 pub fn get_index(&self, name: &str) -> Option<usize> {
47 self.layouts.iter().position(|l| l.name.as_str() == name)
48 }
49}
50
51impl Default for Layouts {
52 fn default() -> Self {
53 Self {
54 layouts: vec![
55 even_horizontal(),
56 even_vertical(),
57 monocle(),
58 grid(),
59 main_and_vert_stack(),
60 main_and_horizontal_stack(),
61 right_main_and_vert_stack(),
62 fibonacci(),
63 dwindle(),
64 main_and_deck(),
65 center_main(),
66 center_main_balanced(),
67 center_main_fluid(),
68 ],
69 }
70 }
71}
72
73type LayoutName = String;
74
75#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
79#[serde(default)]
80pub struct Layout {
81 pub name: LayoutName,
84
85 pub flip: Flip,
87
88 pub rotate: Rotation,
90
91 pub reserve: Reserve,
94
95 pub columns: Columns,
98}
99
100impl Layout {
101 pub fn is_monocle(&self) -> bool {
106 self.columns.main.is_none()
107 && self.columns.second_stack.is_none()
108 && self.columns.stack.split.is_none()
109 }
110
111 pub fn is_main_and_deck(&self) -> bool {
116 match &self.columns.main {
117 Some(main) => {
118 self.columns.second_stack.is_none()
119 && main.split.is_none()
120 && self.columns.stack.split.is_none()
121 }
122 None => false,
123 }
124 }
125
126 pub fn main_size(&self) -> Option<Size> {
129 self.columns.main.as_ref().map(|m| m.size)
130 }
131
132 pub fn main_window_count(&self) -> Option<usize> {
135 self.columns.main.as_ref().map(|m| m.count)
136 }
137
138 pub fn set_main_size(&mut self, size: Size) {
140 if let Some(main) = self.columns.main.as_mut() {
141 main.size = size;
142 }
143 }
144
145 pub fn increase_main_size(&mut self, upper_bound: i32) {
155 if let Some(main) = self.columns.main.as_mut() {
156 match main.size {
157 Size::Pixel(_) => {
158 self.change_main_size(DEFAULT_MAIN_SIZE_CHANGE_PIXEL, upper_bound);
159 }
160 Size::Ratio(_) => {
161 self.change_main_size(DEFAULT_MAIN_SIZE_CHANGE_PERCENTAGE, upper_bound);
162 }
163 };
164 };
165 }
166
167 pub fn decrease_main_size(&mut self) {
177 if let Some(main) = self.columns.main.as_mut() {
178 match main.size {
181 Size::Pixel(_) => self.change_main_size(-DEFAULT_MAIN_SIZE_CHANGE_PIXEL, i32::MAX),
182 Size::Ratio(_) => {
183 self.change_main_size(-DEFAULT_MAIN_SIZE_CHANGE_PERCENTAGE, i32::MAX);
184 }
185 };
186 };
187 }
188
189 pub fn change_main_size(&mut self, delta: i32, upper_bound: i32) {
222 if let Some(main) = self.columns.main.as_mut() {
223 main.size = match main.size {
224 Size::Pixel(px) => Size::Pixel(cmp::max(0, cmp::min(upper_bound, px + delta))),
225 Size::Ratio(ratio) => {
226 Size::Ratio(f32::max(0.0, f32::min(1.0, ratio + (delta as f32 * 0.01))))
227 }
228 }
229 }
230 }
231
232 pub fn set_main_window_count(&mut self, count: usize) {
248 if let Some(main) = self.columns.main.as_mut() {
249 main.count = cmp::max(0, count);
250 }
251 }
252
253 pub fn increase_main_window_count(&mut self) {
255 if let Some(main) = self.columns.main.as_mut() {
256 main.count = main.count.saturating_add(1);
257 }
258 }
259
260 pub fn decrease_main_window_count(&mut self) {
262 if let Some(main) = self.columns.main.as_mut() {
263 main.count = main.count.saturating_sub(1);
264 }
265 }
266
267 pub fn rotate(&mut self, clockwise: bool) {
270 self.rotate = if clockwise {
271 self.rotate.clockwise()
272 } else {
273 self.rotate.counter_clockwise()
274 }
275 }
276
277 pub fn check(&self) {
278 if self.columns.second_stack.is_some() && self.columns.main.is_none() {
279 }
281 }
282
283 pub fn update_defaults(custom: &Vec<Layout>) -> Vec<Layout> {
284 let mut layouts = Layouts::default().layouts;
285 for custom_layout in custom {
286 layouts.push(custom_layout.clone());
287 }
288 layouts
289 }
290}
291
292impl Default for Layout {
293 fn default() -> Self {
294 Self {
295 name: String::from("Default"),
296 flip: Flip::None,
297 rotate: Rotation::North,
298 reserve: Reserve::None,
299 columns: Columns::default(),
300 }
301 }
302}
303
304#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
324#[serde(default)]
325pub struct Columns {
326 pub flip: Flip,
328
329 pub rotate: Rotation,
331
332 pub main: Option<Main>,
338
339 pub stack: Stack,
343
344 pub second_stack: Option<SecondStack>,
353}
354
355impl Default for Columns {
356 fn default() -> Self {
357 Self {
358 flip: Flip::default(),
359 rotate: Rotation::default(),
360 main: Some(Main::default()),
361 stack: Stack::default(),
362 second_stack: None,
363 }
364 }
365}
366
367#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
369#[serde(default)]
370pub struct Main {
371 pub count: usize,
373
374 pub size: Size,
376
377 pub flip: Flip,
379
380 pub rotate: Rotation,
382
383 pub split: Option<Split>,
389}
390
391impl Default for Main {
392 fn default() -> Self {
393 Self {
394 count: 1,
395 size: Size::Ratio(0.5),
396 flip: Flip::default(),
397 rotate: Rotation::default(),
398 split: Some(Split::Vertical),
399 }
400 }
401}
402
403#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
405#[serde(default)]
406pub struct Stack {
407 pub flip: Flip,
409
410 pub rotate: Rotation,
412
413 pub split: Option<Split>,
419}
420
421impl Default for Stack {
422 fn default() -> Self {
423 Self {
424 flip: Flip::default(),
425 rotate: Rotation::default(),
426 split: Some(Split::Horizontal),
427 }
428 }
429}
430
431#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
433#[serde(default)]
434pub struct SecondStack {
435 pub flip: Flip,
437
438 pub rotate: Rotation,
440
441 pub split: Split,
444}
445
446impl Default for SecondStack {
447 fn default() -> Self {
448 Self {
449 flip: Flip::default(),
450 rotate: Rotation::default(),
451 split: Split::Horizontal,
452 }
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use crate::{
459 geometry::Size,
460 layouts::{
461 layout::{DEFAULT_MAIN_SIZE_CHANGE_PERCENTAGE, DEFAULT_MAIN_SIZE_CHANGE_PIXEL},
462 Layouts,
463 },
464 Layout,
465 };
466
467 #[test]
468 fn monocle_layout_is_monocle() {
469 let layouts = Layouts::default();
470 let layout = layouts.get("Monocle").unwrap();
471 assert!(layout.is_monocle());
472 }
473
474 #[test]
475 fn main_and_deck_layout_is_main_and_deck() {
476 let layouts = Layouts::default();
477 let layout = layouts.get("MainAndDeck").unwrap();
478 assert!(layout.is_main_and_deck());
479 }
480
481 #[test]
482 fn set_main_size_works() {
483 let mut layout = Layout::default();
484 layout.set_main_size(Size::Ratio(0.5));
485 assert_eq!(Some(Size::Ratio(0.5)), layout.main_size());
486 }
487
488 #[test]
489 fn increase_main_size_percentage_works() {
490 let mut layout = Layout::default();
491 layout.set_main_size(Size::Ratio(0.5));
492 layout.increase_main_size(500);
493 assert_eq!(
494 Some(Size::Ratio(
495 0.5 + (DEFAULT_MAIN_SIZE_CHANGE_PERCENTAGE as f32 * 0.01)
496 )),
497 layout.main_size()
498 );
499 }
500
501 #[test]
502 fn decrease_main_size_percentage_works() {
503 let mut layout = Layout::default();
504 layout.set_main_size(Size::Ratio(0.5));
505 layout.decrease_main_size();
506 assert_eq!(
507 Some(Size::Ratio(
508 0.5 - (DEFAULT_MAIN_SIZE_CHANGE_PERCENTAGE as f32 * 0.01)
509 )),
510 layout.main_size()
511 );
512 }
513
514 #[test]
515 fn increase_main_size_pixel_works() {
516 let mut layout = Layout::default();
517 layout.set_main_size(Size::Pixel(200));
518 layout.increase_main_size(500);
519 assert_eq!(
520 Some(Size::Pixel(200 + DEFAULT_MAIN_SIZE_CHANGE_PIXEL)),
521 layout.main_size()
522 );
523 }
524
525 #[test]
526 fn decrease_main_size_pixel_works() {
527 let mut layout = Layout::default();
528 layout.set_main_size(Size::Pixel(200));
529 layout.decrease_main_size();
530 assert_eq!(
531 Some(Size::Pixel(200 - DEFAULT_MAIN_SIZE_CHANGE_PIXEL)),
532 layout.main_size()
533 );
534 }
535
536 #[test]
537 fn change_main_size_percentage_negative_works() {
538 let mut layout = Layout::default();
539 layout.set_main_size(Size::Ratio(0.5));
540 layout.change_main_size(-5, 500);
541 assert_eq!(Some(Size::Ratio(0.45)), layout.main_size());
542 }
543
544 #[test]
545 fn change_main_size_percentage_positive_works() {
546 let mut layout = Layout::default();
547 layout.set_main_size(Size::Ratio(0.5));
548 layout.change_main_size(5, 500);
549 assert_eq!(Some(Size::Ratio(0.55)), layout.main_size());
550 }
551
552 #[test]
553 fn change_main_size_pixel_negative_works() {
554 let mut layout = Layout::default();
555 layout.set_main_size(Size::Pixel(200));
556 layout.change_main_size(-5, 500);
557 assert_eq!(Some(Size::Pixel(195)), layout.main_size());
558 }
559
560 #[test]
561 fn change_main_size_pixel_positive_works() {
562 let mut layout = Layout::default();
563 layout.set_main_size(Size::Pixel(200));
564 layout.change_main_size(5, 500);
565 assert_eq!(Some(Size::Pixel(205)), layout.main_size());
566 }
567
568 #[test]
569 fn decrease_main_size_does_not_go_below_zero() {
570 let mut layout = Layout::default();
571 layout.set_main_size(Size::Pixel(200));
572 layout.change_main_size(-200, 500);
573 assert_eq!(Some(Size::Pixel(0)), layout.main_size());
574 layout.change_main_size(-200, 500);
575 assert_eq!(Some(Size::Pixel(0)), layout.main_size());
576 }
577
578 #[test]
579 fn decrease_main_size_does_not_go_above_upper_bound() {
580 let mut layout = Layout::default();
581 layout.set_main_size(Size::Pixel(200));
582 layout.change_main_size(200, 500);
583 assert_eq!(Some(Size::Pixel(400)), layout.main_size());
584 layout.change_main_size(200, 500);
585 assert_eq!(Some(Size::Pixel(500)), layout.main_size());
586 }
587
588 #[test]
589 fn set_main_window_count_works() {
590 let mut layout = Layout::default();
591 layout.set_main_window_count(5);
592 assert_eq!(Some(5), layout.main_window_count());
593 }
594
595 #[test]
596 fn increase_main_window_count_works() {
597 let mut layout = Layout::default();
598 layout.set_main_window_count(5);
599 layout.increase_main_window_count();
600 assert_eq!(Some(6), layout.main_window_count());
601 }
602
603 #[test]
604 fn decrease_main_window_count_works() {
605 let mut layout = Layout::default();
606 layout.set_main_window_count(5);
607 layout.decrease_main_window_count();
608 assert_eq!(Some(4), layout.main_window_count());
609 }
610
611 #[test]
612 fn main_window_count_does_not_go_below_zero() {
613 let mut layout = Layout::default();
614 layout.set_main_window_count(1);
615 layout.decrease_main_window_count();
616 assert_eq!(Some(0), layout.main_window_count());
617 layout.decrease_main_window_count();
618 assert_eq!(Some(0), layout.main_window_count());
619 }
620}