├── .travis.yml ├── README.md ├── src ├── lib.rs └── layout │ ├── no_borders_layout.rs │ ├── mod.rs │ ├── full_layout.rs │ ├── mirror_layout.rs │ ├── layout_collection.rs │ ├── gap_layout.rs │ ├── with_borders_layout.rs │ ├── center_layout.rs │ ├── avoid_struts_layout.rs │ ├── resizable_tall_layout.rs │ └── binary_space_partition.rs ├── .gitignore ├── Cargo.toml ├── LICENSE └── Cargo.lock /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wtftw-contrib 2 | A repository for additional wtftw modules 3 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | extern crate bitflags; 4 | extern crate wtftw; 5 | 6 | pub mod layout; 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | -------------------------------------------------------------------------------- /src/layout/no_borders_layout.rs: -------------------------------------------------------------------------------- 1 | extern crate wtftw; 2 | 3 | use self::wtftw::layout::Layout; 4 | use super::with_borders_layout::WithBordersLayout; 5 | 6 | pub struct NoBordersLayout; 7 | 8 | impl NoBordersLayout { 9 | pub fn boxed_new(layout: Box) -> Box { 10 | WithBordersLayout::boxed_new(0, layout) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | 3 | name = "wtftw_contrib" 4 | version = "0.0.2" 5 | authors = ["Simon Wollwage"] 6 | edition = "2018" 7 | 8 | [dependencies.wtftw] 9 | path = "../wtftw" 10 | 11 | [dependencies] 12 | bitflags = "1.2.1" 13 | rustc-serialize = "0.3.24" 14 | log = "0.4.8" 15 | num = "0.3.0" 16 | libc = "0.2.71" 17 | dylib = "0.0.3" 18 | 19 | [lib] 20 | name = "wtftw_contrib" 21 | path = "src/lib.rs" 22 | crate-type = ["rlib"] 23 | -------------------------------------------------------------------------------- /src/layout/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod avoid_struts_layout; 2 | pub mod binary_space_partition; 3 | pub mod center_layout; 4 | pub mod full_layout; 5 | pub mod gap_layout; 6 | pub mod layout_collection; 7 | pub mod mirror_layout; 8 | pub mod resizable_tall_layout; 9 | pub mod with_borders_layout; 10 | pub mod no_borders_layout; 11 | 12 | pub use self::avoid_struts_layout::*; 13 | pub use self::binary_space_partition::*; 14 | pub use self::center_layout::*; 15 | pub use self::full_layout::*; 16 | pub use self::gap_layout::*; 17 | pub use self::layout_collection::*; 18 | pub use self::mirror_layout::*; 19 | pub use self::resizable_tall_layout::*; 20 | pub use self::with_borders_layout::*; 21 | pub use self::no_borders_layout::*; 22 | -------------------------------------------------------------------------------- /src/layout/full_layout.rs: -------------------------------------------------------------------------------- 1 | extern crate wtftw; 2 | 3 | use self::wtftw::config::GeneralConfig; 4 | use self::wtftw::core::stack::Stack; 5 | use self::wtftw::layout::Layout; 6 | use self::wtftw::window_system::Rectangle; 7 | use self::wtftw::window_system::Window; 8 | use self::wtftw::window_system::WindowSystem; 9 | use std::borrow::ToOwned; 10 | 11 | #[derive(Copy, Clone)] 12 | pub struct FullLayout; 13 | 14 | impl Layout for FullLayout { 15 | fn apply_layout( 16 | &mut self, 17 | _: &dyn WindowSystem, 18 | screen: Rectangle, 19 | config: &GeneralConfig, 20 | stack: &Option>, 21 | ) -> Vec<(Window, Rectangle)> { 22 | match *stack { 23 | Some(ref st) => { 24 | let bw = 2 * config.border_width; 25 | let Rectangle(x, y, sw, sh) = screen; 26 | vec![(st.focus, Rectangle(x, y, sw + bw, sh + bw))] 27 | } 28 | None => Vec::new(), 29 | } 30 | } 31 | 32 | fn description(&self) -> String { 33 | "Full".to_owned() 34 | } 35 | 36 | fn copy(&self) -> Box { 37 | Box::new(*self) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Simon Wollwage 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of wtftw-contrib nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /src/layout/mirror_layout.rs: -------------------------------------------------------------------------------- 1 | extern crate wtftw; 2 | 3 | use self::wtftw::config::GeneralConfig; 4 | use self::wtftw::core::stack::Stack; 5 | use self::wtftw::layout::mirror_rect; 6 | use self::wtftw::layout::Layout; 7 | use self::wtftw::layout::LayoutMessage; 8 | use self::wtftw::window_system::Rectangle; 9 | use self::wtftw::window_system::Window; 10 | use self::wtftw::window_system::WindowSystem; 11 | 12 | /// A simple layout container that just 13 | /// rotates the layout of its contained layout 14 | /// by 90° clockwise 15 | pub struct MirrorLayout { 16 | pub layout: Box, 17 | } 18 | 19 | impl MirrorLayout { 20 | /// Create a new MirrorLayout containing the given layout 21 | pub fn boxed_new(layout: Box) -> Box { 22 | Box::new(MirrorLayout { layout }) 23 | } 24 | } 25 | 26 | impl Layout for MirrorLayout { 27 | fn apply_layout( 28 | &mut self, 29 | w: &dyn WindowSystem, 30 | screen: Rectangle, 31 | config: &GeneralConfig, 32 | stack: &Option>, 33 | ) -> Vec<(Window, Rectangle)> { 34 | // Rotate the screen, apply the layout, ... 35 | self.layout 36 | .apply_layout(w, mirror_rect(&screen), config, stack) 37 | .iter() 38 | // and then rotate all resulting windows by 90° clockwise 39 | .map(|&(w, r)| (w, mirror_rect(&r))) 40 | .collect() 41 | } 42 | 43 | fn apply_message( 44 | &mut self, 45 | message: LayoutMessage, 46 | window_system: &dyn WindowSystem, 47 | stack: &Option>, 48 | config: &GeneralConfig, 49 | ) -> bool { 50 | self.layout 51 | .apply_message(message, window_system, stack, config) 52 | } 53 | 54 | fn description(&self) -> String { 55 | self.layout.description() 56 | } 57 | 58 | fn copy(&self) -> Box { 59 | Box::new(MirrorLayout { 60 | layout: self.layout.copy(), 61 | }) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/layout/layout_collection.rs: -------------------------------------------------------------------------------- 1 | extern crate wtftw; 2 | 3 | use self::wtftw::config::GeneralConfig; 4 | use self::wtftw::core::stack::Stack; 5 | use self::wtftw::layout::Layout; 6 | use self::wtftw::layout::LayoutMessage; 7 | use self::wtftw::window_system::Rectangle; 8 | use self::wtftw::window_system::Window; 9 | use self::wtftw::window_system::WindowSystem; 10 | 11 | pub struct LayoutCollection { 12 | pub layouts: Vec>, 13 | pub current: usize, 14 | } 15 | 16 | impl LayoutCollection { 17 | pub fn boxed_new(layouts: Vec>) -> Box { 18 | Box::new(LayoutCollection { 19 | layouts, 20 | current: 0, 21 | }) 22 | } 23 | } 24 | 25 | impl Layout for LayoutCollection { 26 | fn apply_layout( 27 | &mut self, 28 | window_system: &dyn WindowSystem, 29 | screen: Rectangle, 30 | config: &GeneralConfig, 31 | stack: &Option>, 32 | ) -> Vec<(Window, Rectangle)> { 33 | self.layouts[self.current].apply_layout(window_system, screen, config, stack) 34 | } 35 | 36 | fn apply_message( 37 | &mut self, 38 | message: LayoutMessage, 39 | window_system: &dyn WindowSystem, 40 | stack: &Option>, 41 | config: &GeneralConfig, 42 | ) -> bool { 43 | match message { 44 | LayoutMessage::Next => { 45 | self.layouts[self.current].unhook(window_system, stack, config); 46 | self.current = (self.current + 1) % self.layouts.len(); 47 | true 48 | } 49 | LayoutMessage::Prev => { 50 | self.layouts[self.current].unhook(window_system, stack, config); 51 | self.current = (self.current + (self.layouts.len() - 1)) % self.layouts.len(); 52 | true 53 | } 54 | _ => self.layouts[self.current].apply_message(message, window_system, stack, config), 55 | } 56 | } 57 | 58 | fn description(&self) -> String { 59 | self.layouts[self.current].description() 60 | } 61 | 62 | fn copy(&self) -> Box { 63 | Box::new(LayoutCollection { 64 | current: self.current, 65 | layouts: self.layouts.iter().map(|x| x.copy()).collect(), 66 | }) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/layout/gap_layout.rs: -------------------------------------------------------------------------------- 1 | extern crate wtftw; 2 | 3 | use self::wtftw::config::GeneralConfig; 4 | use self::wtftw::core::stack::Stack; 5 | use self::wtftw::layout::Layout; 6 | use self::wtftw::layout::LayoutMessage; 7 | use self::wtftw::window_system::Rectangle; 8 | use self::wtftw::window_system::Window; 9 | use self::wtftw::window_system::WindowSystem; 10 | 11 | pub struct GapLayout { 12 | gap: u32, 13 | layout: Box, 14 | } 15 | 16 | impl GapLayout { 17 | pub fn boxed_new(gap: u32, layout: Box) -> Box { 18 | Box::new(GapLayout { 19 | gap, 20 | layout: layout.copy(), 21 | }) 22 | } 23 | } 24 | 25 | impl Layout for GapLayout { 26 | fn apply_layout( 27 | &mut self, 28 | window_system: &dyn WindowSystem, 29 | screen: Rectangle, 30 | config: &GeneralConfig, 31 | stack: &Option>, 32 | ) -> Vec<(Window, Rectangle)> { 33 | let layout = self 34 | .layout 35 | .apply_layout(window_system, screen, config, stack); 36 | 37 | let g = self.gap; 38 | layout 39 | .iter() 40 | .map(|&(win, Rectangle(x, y, w, h))| { 41 | ( 42 | win, 43 | Rectangle(x + g as i32, y + g as i32, w - 2 * g, h - 2 * g), 44 | ) 45 | }) 46 | .collect() 47 | } 48 | 49 | fn apply_message( 50 | &mut self, 51 | message: LayoutMessage, 52 | window_system: &dyn WindowSystem, 53 | stack: &Option>, 54 | config: &GeneralConfig, 55 | ) -> bool { 56 | match message { 57 | LayoutMessage::IncreaseGap => { 58 | self.gap += 1; 59 | true 60 | } 61 | LayoutMessage::DecreaseGap => { 62 | if self.gap > 0 { 63 | self.gap -= 1; 64 | } 65 | true 66 | } 67 | _ => self 68 | .layout 69 | .apply_message(message, window_system, stack, config), 70 | } 71 | } 72 | 73 | fn description(&self) -> String { 74 | self.layout.description() 75 | } 76 | 77 | fn copy(&self) -> Box { 78 | Box::new(GapLayout { 79 | gap: self.gap, 80 | layout: self.layout.copy(), 81 | }) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/layout/with_borders_layout.rs: -------------------------------------------------------------------------------- 1 | extern crate wtftw; 2 | 3 | use self::wtftw::config::GeneralConfig; 4 | use self::wtftw::core::stack::Stack; 5 | use self::wtftw::layout::Layout; 6 | use self::wtftw::layout::LayoutMessage; 7 | use self::wtftw::window_system::Rectangle; 8 | use self::wtftw::window_system::Window; 9 | use self::wtftw::window_system::WindowSystem; 10 | 11 | pub struct WithBordersLayout { 12 | border: u32, 13 | layout: Box, 14 | } 15 | 16 | impl WithBordersLayout { 17 | pub fn boxed_new(border: u32, layout: Box) -> Box { 18 | Box::new(WithBordersLayout { 19 | border, 20 | layout: layout.copy(), 21 | }) 22 | } 23 | } 24 | 25 | impl Layout for WithBordersLayout { 26 | fn apply_layout( 27 | &mut self, 28 | window_system: &dyn WindowSystem, 29 | screen: Rectangle, 30 | config: &GeneralConfig, 31 | stack: &Option>, 32 | ) -> Vec<(Window, Rectangle)> { 33 | if let Some(ref s) = *stack { 34 | for window in s.integrate().into_iter() { 35 | window_system.set_window_border_width(window, self.border); 36 | } 37 | } 38 | self.layout 39 | .apply_layout(window_system, screen, config, stack) 40 | } 41 | 42 | fn apply_message( 43 | &mut self, 44 | message: LayoutMessage, 45 | window_system: &dyn WindowSystem, 46 | stack: &Option>, 47 | config: &GeneralConfig, 48 | ) -> bool { 49 | self.layout 50 | .apply_message(message, window_system, stack, config) 51 | } 52 | 53 | fn description(&self) -> String { 54 | self.layout.description() 55 | } 56 | 57 | fn copy(&self) -> Box { 58 | Box::new(WithBordersLayout { 59 | border: self.border, 60 | layout: self.layout.copy(), 61 | }) 62 | } 63 | 64 | fn unhook( 65 | &self, 66 | window_system: &dyn WindowSystem, 67 | stack: &Option>, 68 | config: &GeneralConfig, 69 | ) { 70 | if let Some(ref s) = *stack { 71 | for window in s.integrate().into_iter() { 72 | window_system.set_window_border_width(window, config.border_width); 73 | let Rectangle(_, _, w, h) = window_system.get_geometry(window); 74 | window_system.resize_window( 75 | window, 76 | w + 2 * config.border_width, 77 | h + 2 * config.border_width, 78 | ); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/layout/center_layout.rs: -------------------------------------------------------------------------------- 1 | extern crate wtftw; 2 | 3 | use self::wtftw::config::GeneralConfig; 4 | use self::wtftw::core::stack::Stack; 5 | use self::wtftw::layout::Layout; 6 | use self::wtftw::layout::LayoutMessage; 7 | use self::wtftw::window_system::Rectangle; 8 | use self::wtftw::window_system::Window; 9 | use self::wtftw::window_system::WindowSystem; 10 | use std::borrow::ToOwned; 11 | 12 | pub struct CenterLayout { 13 | pub layout: Box, 14 | } 15 | 16 | impl CenterLayout { 17 | pub fn boxed_new(layout: Box) -> Box { 18 | Box::new(CenterLayout { 19 | layout: layout.copy(), 20 | }) 21 | } 22 | } 23 | 24 | impl Layout for CenterLayout { 25 | fn apply_layout( 26 | &mut self, 27 | window_system: &dyn WindowSystem, 28 | screen: Rectangle, 29 | config: &GeneralConfig, 30 | stack: &Option>, 31 | ) -> Vec<(Window, Rectangle)> { 32 | match *stack { 33 | Some(ref stack) => { 34 | if stack.len() == 1 { 35 | self.layout 36 | .apply_layout(window_system, screen, config, &Some(stack.clone())) 37 | } else { 38 | let new_stack = if !stack.up.is_empty() { 39 | Stack::::new( 40 | stack.up[0], 41 | stack.up.iter().skip(1).copied().collect(), 42 | stack.down.clone(), 43 | ) 44 | } else { 45 | Stack::::new( 46 | stack.down[0], 47 | Vec::new(), 48 | stack.down.iter().skip(1).copied().collect(), 49 | ) 50 | }; 51 | (vec![{ 52 | let x = screen.0 + ((screen.2 as f32 * 0.2) as i32 / 2); 53 | let y = screen.1 + ((screen.3 as f32 * 0.2) as i32 / 2); 54 | let w = (screen.2 as f32 * 0.8) as u32; 55 | let h = (screen.3 as f32 * 0.8) as u32; 56 | (stack.focus, Rectangle(x, y, w, h)) 57 | }] 58 | .into_iter()) 59 | .chain( 60 | self.layout 61 | .apply_layout(window_system, screen, config, &Some(new_stack)) 62 | .into_iter(), 63 | ) 64 | .collect() 65 | } 66 | } 67 | _ => Vec::new(), 68 | } 69 | } 70 | 71 | fn apply_message( 72 | &mut self, 73 | message: LayoutMessage, 74 | window_system: &dyn WindowSystem, 75 | stack: &Option>, 76 | config: &GeneralConfig, 77 | ) -> bool { 78 | self.layout 79 | .apply_message(message, window_system, stack, config) 80 | } 81 | 82 | fn description(&self) -> String { 83 | "Center".to_owned() 84 | } 85 | 86 | fn copy(&self) -> Box { 87 | CenterLayout::boxed_new(self.layout.copy()) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/layout/avoid_struts_layout.rs: -------------------------------------------------------------------------------- 1 | extern crate num; 2 | extern crate wtftw; 3 | 4 | use self::num::traits::Bounded; 5 | use self::wtftw::config::GeneralConfig; 6 | use self::wtftw::core::stack::Stack; 7 | use self::wtftw::layout::Direction; 8 | use self::wtftw::layout::Layout; 9 | use self::wtftw::layout::LayoutMessage; 10 | use self::wtftw::window_system::Rectangle; 11 | use self::wtftw::window_system::Window; 12 | use self::wtftw::window_system::WindowSystem; 13 | use std::collections::BTreeSet; 14 | 15 | #[derive(Clone, Copy)] 16 | pub struct Strut(Direction, u64, u64, u64); 17 | 18 | fn parse_strut_partial(x: Vec) -> Vec { 19 | if x.len() != 12 { 20 | return Vec::new(); 21 | } 22 | 23 | (vec![ 24 | Strut(Direction::Left, x[0], x[4], x[5]), 25 | Strut(Direction::Right, x[1], x[6], x[7]), 26 | Strut(Direction::Up, x[2], x[8], x[9]), 27 | Strut(Direction::Down, x[3], x[10], x[11]), 28 | ]) 29 | .into_iter() 30 | .filter(|&Strut(_, n, _, _)| n != 0) 31 | .collect::>() 32 | //match &x[..] { 33 | //[l, r, t, b, ly1, ly2, ry1, ry2, tx1, tx2, bx1, bx2] => { 34 | //(vec!(Strut(Direction::Left, l, ly1, ly2), 35 | //Strut(Direction::Right, r, ry1, ry2), 36 | //Strut(Direction::Up, t, tx1, tx2), 37 | //Strut(Direction::Down, b, bx1, bx2))).into_iter() 38 | //.filter(|&Strut(_, n, _, _)| n != 0) 39 | //.collect() 40 | //}, 41 | //_ => Vec::new() 42 | //} 43 | } 44 | 45 | pub fn get_strut(window_system: &dyn WindowSystem, window: Window) -> Vec { 46 | let partial_strut = window_system.get_partial_strut(window); 47 | 48 | fn parse_strut(x: Vec) -> Vec { 49 | if x.len() != 4 { 50 | return Vec::new(); 51 | } 52 | 53 | let s = vec![Bounded::min_value(), Bounded::max_value()]; 54 | let r: Vec = x.iter().chain(s.iter().cycle()).take(12).copied().collect(); 55 | parse_strut_partial(r) 56 | 57 | //match &x[..] { 58 | //[a, b, c, d] => { 59 | //let t = vec!(a, b, c, d); 60 | //let s = vec!(Bounded::min_value(), Bounded::max_value()); 61 | //let r : Vec = t.iter().chain(s.iter().cycle()).take(12).map(|&x| x).collect(); 62 | //parse_strut_partial(r) 63 | //} 64 | //_ => Vec::new() 65 | //} 66 | } 67 | 68 | match partial_strut { 69 | Some(ps) => parse_strut_partial(ps), 70 | None => { 71 | let strut = window_system.get_strut(window); 72 | match strut { 73 | Some(s) => parse_strut(s), 74 | None => Vec::new(), 75 | } 76 | } 77 | } 78 | } 79 | 80 | /// A layout that avoids dock like windows (e.g. dzen, xmobar, ...) 81 | /// to not overlap them. 82 | pub struct AvoidStrutsLayout { 83 | directions: BTreeSet, 84 | layout: Box, 85 | } 86 | 87 | impl AvoidStrutsLayout { 88 | /// Create a new AvoidStrutsLayout, containing the given layout 89 | /// and avoiding struts in the given directions. 90 | pub fn boxed_new(d: Vec, layout: Box) -> Box { 91 | Box::new(AvoidStrutsLayout { 92 | directions: d.iter().copied().collect(), 93 | layout: layout.copy(), 94 | }) 95 | } 96 | } 97 | 98 | impl Layout for AvoidStrutsLayout { 99 | fn apply_layout( 100 | &mut self, 101 | window_system: &dyn WindowSystem, 102 | screen: Rectangle, 103 | config: &GeneralConfig, 104 | stack: &Option>, 105 | ) -> Vec<(Window, Rectangle)> { 106 | let new_screen = stack.clone().map_or(screen, |_| { 107 | window_system 108 | .get_windows() 109 | .into_iter() 110 | .filter(|&w| { 111 | window_system.is_dock(w) && window_system.get_geometry(w).overlaps(&screen) 112 | }) 113 | .flat_map(|x| get_strut(window_system, x).into_iter()) 114 | .filter(|&Strut(s, _, _, _)| self.directions.contains(&s)) 115 | .fold(screen, |Rectangle(x, y, w, h), Strut(d, sw, _, _)| { 116 | let s = sw as u32; 117 | match d { 118 | Direction::Up => Rectangle(x, y + s as i32, w, h - s), 119 | Direction::Down => Rectangle(x, y, w, h - s), 120 | Direction::Left => Rectangle(x + s as i32, y, w - s, h), 121 | Direction::Right => Rectangle(x, y, w - s, h), 122 | } 123 | }) 124 | }); 125 | 126 | self.layout 127 | .apply_layout(window_system, new_screen, config, stack) 128 | } 129 | 130 | fn apply_message( 131 | &mut self, 132 | message: LayoutMessage, 133 | window_system: &dyn WindowSystem, 134 | stack: &Option>, 135 | config: &GeneralConfig, 136 | ) -> bool { 137 | self.layout 138 | .apply_message(message, window_system, stack, config) 139 | } 140 | 141 | fn description(&self) -> String { 142 | self.layout.description() 143 | } 144 | 145 | fn copy(&self) -> Box { 146 | Box::new(AvoidStrutsLayout { 147 | directions: self.directions.clone(), 148 | layout: self.layout.copy(), 149 | }) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/layout/resizable_tall_layout.rs: -------------------------------------------------------------------------------- 1 | extern crate wtftw; 2 | 3 | use self::wtftw::config::GeneralConfig; 4 | use self::wtftw::core::stack::Stack; 5 | use self::wtftw::layout::Layout; 6 | use self::wtftw::layout::LayoutMessage; 7 | use self::wtftw::window_manager::ScreenDetail; 8 | use self::wtftw::window_system::Rectangle; 9 | use self::wtftw::window_system::Window; 10 | use self::wtftw::window_system::WindowSystem; 11 | use std::borrow::ToOwned; 12 | use std::iter; 13 | 14 | #[derive(Clone)] 15 | pub struct ResizableTallLayout { 16 | pub num_master: u32, 17 | pub increment_ratio: f32, 18 | pub ratio: f32, 19 | pub slaves: Vec, 20 | } 21 | 22 | impl ResizableTallLayout { 23 | pub fn boxed_new() -> Box { 24 | Box::new(ResizableTallLayout { 25 | num_master: 1, 26 | increment_ratio: 0.03, 27 | ratio: 0.5, 28 | slaves: Vec::new(), 29 | }) 30 | } 31 | 32 | fn tile( 33 | ratio: f32, 34 | mf: U, 35 | screen: ScreenDetail, 36 | num_master: u32, 37 | num_windows: u32, 38 | ) -> Vec 39 | where 40 | U: Iterator, 41 | { 42 | if num_windows <= num_master || num_master == 0 { 43 | ResizableTallLayout::split_vertically(mf, num_windows, screen) 44 | } else { 45 | let v = mf.collect::>(); 46 | let (r1, r2) = ResizableTallLayout::split_horizontally_by(ratio, screen); 47 | let v1 = ResizableTallLayout::split_vertically(v.clone().into_iter(), num_master, r1); 48 | let v2 = ResizableTallLayout::split_vertically( 49 | v.into_iter().skip(num_master as usize), 50 | num_windows - num_master, 51 | r2, 52 | ); 53 | v1.iter().chain(v2.iter()).copied().collect() 54 | } 55 | } 56 | 57 | fn split_vertically(r: U, num: u32, screen: ScreenDetail) -> Vec 58 | where 59 | U: Iterator, 60 | { 61 | if r.size_hint().0 == 0 { 62 | return vec![screen]; 63 | } 64 | 65 | if num < 2 { 66 | return vec![screen]; 67 | } 68 | 69 | let Rectangle(sx, sy, sw, sh) = screen; 70 | let fxv = r.collect::>(); 71 | let f = fxv[0]; 72 | let smallh = ((sh / num) as f32 * f) as u32; 73 | (vec![Rectangle(sx, sy, sw, smallh)]) 74 | .iter() 75 | .chain( 76 | ResizableTallLayout::split_vertically( 77 | fxv.into_iter().skip(1), 78 | num - 1, 79 | Rectangle(sx, sy + smallh as i32, sw, sh - smallh), 80 | ) 81 | .iter(), 82 | ) 83 | .copied() 84 | .collect() 85 | } 86 | 87 | fn split_horizontally_by(ratio: f32, screen: ScreenDetail) -> (Rectangle, Rectangle) { 88 | let Rectangle(sx, sy, sw, sh) = screen; 89 | let leftw = (sw as f32 * ratio).floor() as u32; 90 | 91 | ( 92 | Rectangle(sx, sy, leftw, sh), 93 | Rectangle(sx + leftw as i32, sy, sw - leftw, sh), 94 | ) 95 | } 96 | 97 | fn resize(&mut self, stack: &Option>, d: f32) { 98 | fn modify(v: U, d: f32, n: usize) -> Vec 99 | where 100 | U: Iterator, 101 | { 102 | if v.size_hint().0 == 0 { 103 | return Vec::new(); 104 | } 105 | if n == 0 { 106 | let frac = v.collect::>(); 107 | (vec![frac[0] + d]) 108 | .into_iter() 109 | .chain(frac.into_iter().skip(1)) 110 | .collect() 111 | } else { 112 | let frac = v.collect::>(); 113 | (vec![frac[0]]) 114 | .into_iter() 115 | .chain(modify(frac.into_iter().skip(1), d, n - 1).into_iter()) 116 | .collect() 117 | } 118 | } 119 | 120 | if let Some(ref s) = *stack { 121 | let n = s.up.len(); 122 | let total = s.len(); 123 | let pos = if n as u32 == self.num_master - 1 || n == total - 1 { 124 | n - 1 125 | } else { 126 | n 127 | }; 128 | let mfrac = modify( 129 | self.slaves 130 | .clone() 131 | .into_iter() 132 | .chain(iter::repeat(1.0)) 133 | .take(total), 134 | d, 135 | pos, 136 | ); 137 | self.slaves = mfrac.into_iter().take(total).collect(); 138 | } 139 | } 140 | } 141 | 142 | impl Layout for ResizableTallLayout { 143 | fn apply_layout( 144 | &mut self, 145 | _: &dyn WindowSystem, 146 | screen: Rectangle, 147 | _: &GeneralConfig, 148 | stack: &Option>, 149 | ) -> Vec<(Window, Rectangle)> { 150 | match *stack { 151 | Some(ref s) => { 152 | let ws = s.integrate(); 153 | s.integrate() 154 | .iter() 155 | .zip( 156 | ResizableTallLayout::tile( 157 | self.ratio, 158 | self.slaves 159 | .clone() 160 | .into_iter() 161 | .chain(iter::repeat(1.0)) 162 | .take(ws.len()), 163 | screen, 164 | self.num_master, 165 | ws.len() as u32, 166 | ) 167 | .iter(), 168 | ) 169 | .map(|(&x, &y)| (x, y)) 170 | .collect() 171 | } 172 | _ => Vec::new(), 173 | } 174 | } 175 | 176 | fn apply_message( 177 | &mut self, 178 | message: LayoutMessage, 179 | _: &dyn WindowSystem, 180 | stack: &Option>, 181 | _: &GeneralConfig, 182 | ) -> bool { 183 | let d = self.increment_ratio; 184 | match message { 185 | LayoutMessage::Increase => { 186 | self.ratio += self.increment_ratio; 187 | true 188 | } 189 | LayoutMessage::Decrease => { 190 | self.ratio -= self.increment_ratio; 191 | true 192 | } 193 | LayoutMessage::IncreaseMaster => { 194 | self.num_master += 1; 195 | true 196 | } 197 | LayoutMessage::DecreaseMaster => { 198 | if self.num_master > 1 { 199 | self.num_master -= 1 200 | } 201 | true 202 | } 203 | LayoutMessage::IncreaseSlave => { 204 | self.resize(stack, d); 205 | true 206 | } 207 | LayoutMessage::DecreaseSlave => { 208 | self.resize(stack, -d); 209 | true 210 | } 211 | _ => false, 212 | } 213 | } 214 | 215 | fn description(&self) -> String { 216 | "ResizeTall".to_owned() 217 | } 218 | 219 | fn copy(&self) -> Box { 220 | Box::new(self.clone()) 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "arrayref" 5 | version = "0.3.6" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | 8 | [[package]] 9 | name = "arrayvec" 10 | version = "0.5.1" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | [[package]] 14 | name = "autocfg" 15 | version = "1.0.0" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | 18 | [[package]] 19 | name = "base64" 20 | version = "0.11.0" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | 23 | [[package]] 24 | name = "bitflags" 25 | version = "1.2.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | 28 | [[package]] 29 | name = "blake2b_simd" 30 | version = "0.5.10" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | dependencies = [ 33 | "arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 34 | "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 36 | ] 37 | 38 | [[package]] 39 | name = "cfg-if" 40 | version = "0.1.10" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | 43 | [[package]] 44 | name = "chrono" 45 | version = "0.4.13" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | dependencies = [ 48 | "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 49 | "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", 50 | "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 51 | ] 52 | 53 | [[package]] 54 | name = "constant_time_eq" 55 | version = "0.1.5" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | 58 | [[package]] 59 | name = "crossbeam-utils" 60 | version = "0.7.2" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | dependencies = [ 63 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 64 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 65 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 66 | ] 67 | 68 | [[package]] 69 | name = "dirs" 70 | version = "3.0.1" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | dependencies = [ 73 | "dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 74 | ] 75 | 76 | [[package]] 77 | name = "dirs-sys" 78 | version = "0.3.5" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | dependencies = [ 81 | "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 84 | ] 85 | 86 | [[package]] 87 | name = "dylib" 88 | version = "0.0.3" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | dependencies = [ 91 | "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", 92 | ] 93 | 94 | [[package]] 95 | name = "getopts" 96 | version = "0.2.21" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | dependencies = [ 99 | "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", 100 | ] 101 | 102 | [[package]] 103 | name = "getrandom" 104 | version = "0.1.14" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | dependencies = [ 107 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 108 | "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", 109 | "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", 110 | ] 111 | 112 | [[package]] 113 | name = "itoa" 114 | version = "0.4.6" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | 117 | [[package]] 118 | name = "lazy_static" 119 | version = "1.4.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | 122 | [[package]] 123 | name = "libc" 124 | version = "0.2.71" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | 127 | [[package]] 128 | name = "log" 129 | version = "0.4.8" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | dependencies = [ 132 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 133 | ] 134 | 135 | [[package]] 136 | name = "num" 137 | version = "0.3.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | dependencies = [ 140 | "num-bigint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 141 | "num-complex 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 142 | "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 143 | "num-iter 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", 144 | "num-rational 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 145 | "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", 146 | ] 147 | 148 | [[package]] 149 | name = "num-bigint" 150 | version = "0.3.0" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | dependencies = [ 153 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 154 | "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 155 | "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", 156 | ] 157 | 158 | [[package]] 159 | name = "num-complex" 160 | version = "0.3.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | dependencies = [ 163 | "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", 164 | ] 165 | 166 | [[package]] 167 | name = "num-integer" 168 | version = "0.1.43" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | dependencies = [ 171 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 172 | "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", 173 | ] 174 | 175 | [[package]] 176 | name = "num-iter" 177 | version = "0.1.41" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | dependencies = [ 180 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 181 | "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 182 | "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", 183 | ] 184 | 185 | [[package]] 186 | name = "num-rational" 187 | version = "0.3.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | dependencies = [ 190 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 191 | "num-bigint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 192 | "num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", 193 | "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", 194 | ] 195 | 196 | [[package]] 197 | name = "num-traits" 198 | version = "0.2.12" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | dependencies = [ 201 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 202 | ] 203 | 204 | [[package]] 205 | name = "pkg-config" 206 | version = "0.3.17" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | 209 | [[package]] 210 | name = "redox_syscall" 211 | version = "0.1.56" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | 214 | [[package]] 215 | name = "redox_users" 216 | version = "0.3.4" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | dependencies = [ 219 | "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", 220 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 221 | "rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", 222 | ] 223 | 224 | [[package]] 225 | name = "rust-argon2" 226 | version = "0.7.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | dependencies = [ 229 | "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 230 | "blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", 231 | "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 232 | "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 233 | ] 234 | 235 | [[package]] 236 | name = "rustc-serialize" 237 | version = "0.3.24" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | 240 | [[package]] 241 | name = "ryu" 242 | version = "1.0.5" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | 245 | [[package]] 246 | name = "serde" 247 | version = "1.0.114" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | 250 | [[package]] 251 | name = "serde_json" 252 | version = "1.0.56" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | dependencies = [ 255 | "itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", 256 | "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 257 | "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", 258 | ] 259 | 260 | [[package]] 261 | name = "simplelog" 262 | version = "0.8.0" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | dependencies = [ 265 | "chrono 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", 266 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 267 | "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 268 | ] 269 | 270 | [[package]] 271 | name = "termcolor" 272 | version = "1.1.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | dependencies = [ 275 | "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 276 | ] 277 | 278 | [[package]] 279 | name = "time" 280 | version = "0.1.43" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | dependencies = [ 283 | "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", 284 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 285 | ] 286 | 287 | [[package]] 288 | name = "unicode-width" 289 | version = "0.1.8" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | 292 | [[package]] 293 | name = "wasi" 294 | version = "0.9.0+wasi-snapshot-preview1" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | 297 | [[package]] 298 | name = "winapi" 299 | version = "0.3.9" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | dependencies = [ 302 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 303 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 304 | ] 305 | 306 | [[package]] 307 | name = "winapi-i686-pc-windows-gnu" 308 | version = "0.4.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | 311 | [[package]] 312 | name = "winapi-util" 313 | version = "0.1.5" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | dependencies = [ 316 | "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", 317 | ] 318 | 319 | [[package]] 320 | name = "winapi-x86_64-pc-windows-gnu" 321 | version = "0.4.0" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | 324 | [[package]] 325 | name = "wtftw" 326 | version = "0.4.4" 327 | dependencies = [ 328 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 329 | "dirs 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 330 | "dylib 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 331 | "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", 332 | "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", 333 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 334 | "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", 335 | "simplelog 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 336 | "wtftw_core 0.3.2", 337 | "wtftw_xlib 0.4.0", 338 | "zombie 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 339 | ] 340 | 341 | [[package]] 342 | name = "wtftw_contrib" 343 | version = "0.0.2" 344 | dependencies = [ 345 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 346 | "dylib 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 347 | "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", 348 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 349 | "num 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 350 | "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", 351 | "wtftw 0.4.4", 352 | ] 353 | 354 | [[package]] 355 | name = "wtftw_core" 356 | version = "0.3.2" 357 | dependencies = [ 358 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 359 | "dirs 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 360 | "dylib 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 361 | "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", 362 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 363 | "serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", 364 | ] 365 | 366 | [[package]] 367 | name = "wtftw_xlib" 368 | version = "0.4.0" 369 | dependencies = [ 370 | "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", 371 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 372 | "wtftw_core 0.3.2", 373 | "x11 2.18.2 (registry+https://github.com/rust-lang/crates.io-index)", 374 | ] 375 | 376 | [[package]] 377 | name = "x11" 378 | version = "2.18.2" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | dependencies = [ 381 | "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", 382 | "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", 383 | ] 384 | 385 | [[package]] 386 | name = "zombie" 387 | version = "0.0.4" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | dependencies = [ 390 | "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", 391 | ] 392 | 393 | [metadata] 394 | "checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 395 | "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 396 | "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 397 | "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 398 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 399 | "checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" 400 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 401 | "checksum chrono 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6" 402 | "checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 403 | "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 404 | "checksum dirs 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" 405 | "checksum dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" 406 | "checksum dylib 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f06c13073013a912b363eee1433572499a2028a6b05432dad09383124d64731e" 407 | "checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" 408 | "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 409 | "checksum itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 410 | "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 411 | "checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" 412 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 413 | "checksum num 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3e176191bc4faad357e3122c4747aa098ac880e88b168f106386128736cf4a" 414 | "checksum num-bigint 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b7f3fc75e3697059fb1bc465e3d8cca6cf92f56854f201158b3f9c77d5a3cfa0" 415 | "checksum num-complex 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b05ad05bd8977050b171b3f6b48175fea6e0565b7981059b486075e1026a9fb5" 416 | "checksum num-integer 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" 417 | "checksum num-iter 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f" 418 | "checksum num-rational 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138" 419 | "checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 420 | "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" 421 | "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 422 | "checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" 423 | "checksum rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" 424 | "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" 425 | "checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 426 | "checksum serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" 427 | "checksum serde_json 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" 428 | "checksum simplelog 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2b2736f58087298a448859961d3f4a0850b832e72619d75adc69da7993c2cd3c" 429 | "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 430 | "checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 431 | "checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 432 | "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 433 | "checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 434 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 435 | "checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 436 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 437 | "checksum x11 2.18.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77ecd092546cb16f25783a5451538e73afc8d32e242648d54f4ae5459ba1e773" 438 | "checksum zombie 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a2a923d29f39b60d437dc36e2f33e88493dd4386a397820105ac17100859c6e6" 439 | -------------------------------------------------------------------------------- /src/layout/binary_space_partition.rs: -------------------------------------------------------------------------------- 1 | extern crate wtftw; 2 | 3 | use self::wtftw::config::GeneralConfig; 4 | use self::wtftw::core::stack::Stack; 5 | use self::wtftw::layout::Direction; 6 | use self::wtftw::layout::Layout; 7 | use self::wtftw::layout::LayoutMessage; 8 | use self::wtftw::window_system::Rectangle; 9 | use self::wtftw::window_system::Window; 10 | use self::wtftw::window_system::WindowSystem; 11 | use std::borrow::ToOwned; 12 | use std::cmp::Ordering; 13 | use std::ops::Deref; 14 | 15 | #[derive(Clone, PartialEq, Eq)] 16 | pub enum Axis { 17 | Horizontal, 18 | Vertical, 19 | } 20 | 21 | impl Axis { 22 | pub fn from_direction(d: Direction) -> Axis { 23 | match d { 24 | Direction::Up => Axis::Horizontal, 25 | Direction::Down => Axis::Horizontal, 26 | Direction::Left => Axis::Vertical, 27 | Direction::Right => Axis::Vertical, 28 | } 29 | } 30 | 31 | pub fn opposite(&self) -> Axis { 32 | match *self { 33 | Axis::Horizontal => Axis::Vertical, 34 | Axis::Vertical => Axis::Horizontal, 35 | } 36 | } 37 | } 38 | 39 | #[derive(Clone)] 40 | pub enum Tree { 41 | Leaf, 42 | Node(T, Box>, Box>), 43 | } 44 | 45 | impl Tree { 46 | pub fn number_of_leaves(&self) -> usize { 47 | match *self { 48 | Tree::Leaf => 1, 49 | Tree::Node(_, ref l, ref r) => l.number_of_leaves() + r.number_of_leaves(), 50 | } 51 | } 52 | } 53 | 54 | #[derive(Clone)] 55 | pub struct Split { 56 | axis: Axis, 57 | ratio: f32, 58 | } 59 | 60 | impl Split { 61 | pub fn new(axis: Axis, r: f32) -> Split { 62 | Split { axis, ratio: r } 63 | } 64 | 65 | pub fn split(&self, Rectangle(x, y, w, h): Rectangle) -> (Rectangle, Rectangle) { 66 | match self.axis { 67 | Axis::Horizontal => { 68 | let hr = (h as f32 * self.ratio) as u32; 69 | ( 70 | Rectangle(x, y, w, hr), 71 | Rectangle(x, y + hr as i32, w, h - hr), 72 | ) 73 | } 74 | Axis::Vertical => { 75 | let wr = (w as f32 * self.ratio) as u32; 76 | ( 77 | Rectangle(x, y, wr, h), 78 | Rectangle(x + wr as i32, y, w - wr, h), 79 | ) 80 | } 81 | } 82 | } 83 | 84 | pub fn opposite(&self) -> Split { 85 | Split { 86 | axis: self.axis.opposite(), 87 | ratio: self.ratio, 88 | } 89 | } 90 | 91 | pub fn increase_ratio(&self, r: f32) -> Split { 92 | Split { 93 | axis: self.axis.clone(), 94 | ratio: self.ratio + r, 95 | } 96 | } 97 | } 98 | 99 | #[derive(Clone)] 100 | enum Crumb { 101 | LeftCrumb(T, Tree), 102 | RightCrumb(T, Tree), 103 | } 104 | 105 | impl Crumb { 106 | pub fn swap(&self) -> Crumb { 107 | match *self { 108 | Crumb::LeftCrumb(ref s, ref t) => Crumb::RightCrumb(s.clone(), t.clone()), 109 | Crumb::RightCrumb(ref s, ref t) => Crumb::LeftCrumb(s.clone(), t.clone()), 110 | } 111 | } 112 | 113 | pub fn parent(&self) -> T { 114 | match *self { 115 | Crumb::LeftCrumb(ref s, _) => s.clone(), 116 | Crumb::RightCrumb(ref s, _) => s.clone(), 117 | } 118 | } 119 | 120 | pub fn modify_parent(&self, f: F) -> Crumb 121 | where 122 | F: Fn(&T) -> T, 123 | { 124 | match *self { 125 | Crumb::LeftCrumb(ref s, ref t) => Crumb::LeftCrumb(f(s), t.clone()), 126 | Crumb::RightCrumb(ref s, ref t) => Crumb::RightCrumb(f(s), t.clone()), 127 | } 128 | } 129 | } 130 | 131 | #[derive(Clone)] 132 | pub struct Zipper { 133 | tree: Tree, 134 | crumbs: Vec>, 135 | } 136 | 137 | impl Zipper { 138 | fn left_append(x: S, v: Vec) -> Vec { 139 | (vec![x]).into_iter().chain(v.into_iter()).collect() 140 | } 141 | 142 | pub fn from_tree(tree: Tree) -> Zipper { 143 | Zipper { 144 | tree, 145 | crumbs: Vec::new(), 146 | } 147 | } 148 | 149 | pub fn go_left(&self) -> Option { 150 | match self.tree { 151 | Tree::Leaf => None, 152 | Tree::Node(ref x, ref l, ref r) => Some(Zipper { 153 | tree: l.deref().clone(), 154 | crumbs: Zipper::left_append::>( 155 | Crumb::LeftCrumb(x.clone(), r.deref().clone()), 156 | self.crumbs.clone(), 157 | ), 158 | }), 159 | } 160 | } 161 | 162 | pub fn go_right(&self) -> Option { 163 | match self.tree { 164 | Tree::Leaf => None, 165 | Tree::Node(ref x, ref l, ref r) => Some(Zipper { 166 | tree: r.deref().clone(), 167 | crumbs: Zipper::left_append::>( 168 | Crumb::RightCrumb(x.clone(), l.deref().clone()), 169 | self.crumbs.clone(), 170 | ), 171 | }), 172 | } 173 | } 174 | 175 | pub fn go_up(&self) -> Option { 176 | if self.crumbs.is_empty() { 177 | None 178 | } else { 179 | let head = self.crumbs[0].clone(); 180 | let rest = if self.crumbs.len() == 1 { 181 | Vec::new() 182 | } else { 183 | self.crumbs.clone().into_iter().skip(1).collect() 184 | }; 185 | 186 | match head { 187 | Crumb::LeftCrumb(x, r) => Some(Zipper { 188 | tree: Tree::Node(x, Box::new(self.tree.clone()), Box::new(r)), 189 | crumbs: rest, 190 | }), 191 | Crumb::RightCrumb(x, l) => Some(Zipper { 192 | tree: Tree::Node(x, Box::new(l), Box::new(self.tree.clone())), 193 | crumbs: rest, 194 | }), 195 | } 196 | } 197 | } 198 | 199 | pub fn go_sibling(&self) -> Option { 200 | if self.crumbs.is_empty() { 201 | return None; 202 | } 203 | 204 | let head = self.crumbs[0].clone(); 205 | 206 | match head { 207 | Crumb::LeftCrumb(_, _) => self.go_up().and_then(|x| x.go_right()), 208 | Crumb::RightCrumb(_, _) => self.go_up().and_then(|x| x.go_left()), 209 | } 210 | } 211 | 212 | pub fn go_to_nth_leaf(&self, n: usize) -> Option { 213 | match self.tree { 214 | Tree::Leaf => Some(self.clone()), 215 | Tree::Node(_, ref l, _) => { 216 | if l.number_of_leaves() > n { 217 | self.go_left().and_then(|x| x.go_to_nth_leaf(n)) 218 | } else { 219 | self.go_right() 220 | .and_then(|x| x.go_to_nth_leaf(n - l.number_of_leaves())) 221 | } 222 | } 223 | } 224 | } 225 | 226 | pub fn split_current_leaf(&self) -> Option { 227 | match self.tree { 228 | Tree::Leaf => { 229 | if self.crumbs.is_empty() { 230 | Some(Zipper { 231 | tree: Tree::Node( 232 | Split::new(Axis::Vertical, 0.5), 233 | Box::new(Tree::Leaf), 234 | Box::new(Tree::Leaf), 235 | ), 236 | crumbs: Vec::new(), 237 | }) 238 | } else { 239 | let head = self.crumbs[0].clone(); 240 | Some(Zipper { 241 | tree: Tree::Node( 242 | Split::new(head.parent().axis.opposite(), 0.5), 243 | Box::new(Tree::Leaf), 244 | Box::new(Tree::Leaf), 245 | ), 246 | crumbs: self.crumbs.clone(), 247 | }) 248 | } 249 | } 250 | _ => None, 251 | } 252 | } 253 | 254 | pub fn remove_current_leaf(&self) -> Option { 255 | match self.tree { 256 | Tree::Leaf => { 257 | if self.crumbs.is_empty() { 258 | None 259 | } else { 260 | let head = self.crumbs[0].clone(); 261 | let rest = if self.crumbs.len() == 1 { 262 | Vec::new() 263 | } else { 264 | self.crumbs.clone().into_iter().skip(1).collect() 265 | }; 266 | match head { 267 | Crumb::LeftCrumb(_, r) => Some(Zipper { 268 | tree: r, 269 | crumbs: rest, 270 | }), 271 | Crumb::RightCrumb(_, l) => Some(Zipper { 272 | tree: l, 273 | crumbs: rest, 274 | }), 275 | } 276 | } 277 | } 278 | _ => None, 279 | } 280 | } 281 | 282 | pub fn rotate_current_leaf(&self) -> Option { 283 | match self.tree { 284 | Tree::Leaf => { 285 | if self.crumbs.is_empty() { 286 | Some(Zipper { 287 | tree: Tree::Leaf, 288 | crumbs: Vec::new(), 289 | }) 290 | } else { 291 | let mut c = self.crumbs.clone(); 292 | c[0] = c[0].modify_parent(|x| x.opposite()); 293 | Some(Zipper { 294 | tree: Tree::Leaf, 295 | crumbs: c, 296 | }) 297 | } 298 | } 299 | _ => None, 300 | } 301 | } 302 | 303 | pub fn swap_current_leaf(&self) -> Option { 304 | match self.tree { 305 | Tree::Leaf => { 306 | if self.crumbs.is_empty() { 307 | Some(Zipper { 308 | tree: Tree::Leaf, 309 | crumbs: Vec::new(), 310 | }) 311 | } else { 312 | let mut c = self.crumbs.clone(); 313 | c[0] = c[0].swap(); 314 | Some(Zipper { 315 | tree: Tree::Leaf, 316 | crumbs: c, 317 | }) 318 | } 319 | } 320 | _ => None, 321 | } 322 | } 323 | 324 | pub fn is_all_the_way(&self, dir: Direction) -> bool { 325 | if self.crumbs.is_empty() { 326 | return true; 327 | } 328 | 329 | let head = self.crumbs[0].clone(); 330 | match (dir, head) { 331 | (Direction::Right, Crumb::LeftCrumb(ref s, _)) if s.axis == Axis::Vertical => false, 332 | (Direction::Left, Crumb::RightCrumb(ref s, _)) if s.axis == Axis::Vertical => false, 333 | (Direction::Down, Crumb::LeftCrumb(ref s, _)) if s.axis == Axis::Horizontal => false, 334 | (Direction::Up, Crumb::RightCrumb(ref s, _)) if s.axis == Axis::Horizontal => false, 335 | _ => self.go_up().map_or(false, |x| x.is_all_the_way(dir)), 336 | } 337 | } 338 | 339 | pub fn expand_towards(&self, dir: Direction) -> Option { 340 | if self.crumbs.is_empty() { 341 | return Some(self.clone()); 342 | } 343 | 344 | if self.is_all_the_way(dir) { 345 | return None; 346 | } 347 | 348 | let head = self.crumbs[0].clone(); 349 | let rest = if self.crumbs.len() == 1 { 350 | Vec::new() 351 | } else { 352 | self.crumbs.clone().into_iter().skip(1).collect() 353 | }; 354 | 355 | match (dir, head) { 356 | (Direction::Right, Crumb::LeftCrumb(ref s, ref r)) if s.axis == Axis::Vertical => { 357 | Some(Zipper { 358 | tree: self.tree.clone(), 359 | crumbs: Zipper::left_append( 360 | Crumb::LeftCrumb(s.increase_ratio(0.05), r.clone()), 361 | rest, 362 | ), 363 | }) 364 | } 365 | (Direction::Left, Crumb::RightCrumb(ref s, ref r)) if s.axis == Axis::Vertical => { 366 | Some(Zipper { 367 | tree: self.tree.clone(), 368 | crumbs: Zipper::left_append( 369 | Crumb::RightCrumb(s.increase_ratio(-0.05), r.clone()), 370 | rest, 371 | ), 372 | }) 373 | } 374 | (Direction::Down, Crumb::LeftCrumb(ref s, ref r)) if s.axis == Axis::Horizontal => { 375 | Some(Zipper { 376 | tree: self.tree.clone(), 377 | crumbs: Zipper::left_append( 378 | Crumb::LeftCrumb(s.increase_ratio(0.05), r.clone()), 379 | rest, 380 | ), 381 | }) 382 | } 383 | (Direction::Up, Crumb::RightCrumb(ref s, ref r)) if s.axis == Axis::Horizontal => { 384 | Some(Zipper { 385 | tree: self.tree.clone(), 386 | crumbs: Zipper::left_append( 387 | Crumb::RightCrumb(s.increase_ratio(-0.05), r.clone()), 388 | rest, 389 | ), 390 | }) 391 | } 392 | _ => self.go_up().and_then(|x| x.expand_towards(dir)), 393 | } 394 | } 395 | 396 | pub fn shrink_from(&self, dir: Direction) -> Option { 397 | if self.crumbs.is_empty() { 398 | return Some(self.clone()); 399 | } 400 | 401 | let head = self.crumbs[0].clone(); 402 | 403 | match (dir, head) { 404 | (Direction::Right, Crumb::LeftCrumb(ref s, _)) if s.axis == Axis::Vertical => self 405 | .go_sibling() 406 | .and_then(|x| x.expand_towards(Direction::Left)), 407 | (Direction::Left, Crumb::RightCrumb(ref s, _)) if s.axis == Axis::Vertical => self 408 | .go_sibling() 409 | .and_then(|x| x.expand_towards(Direction::Right)), 410 | (Direction::Down, Crumb::LeftCrumb(ref s, _)) if s.axis == Axis::Horizontal => self 411 | .go_sibling() 412 | .and_then(|x| x.expand_towards(Direction::Up)), 413 | (Direction::Up, Crumb::RightCrumb(ref s, _)) if s.axis == Axis::Horizontal => self 414 | .go_sibling() 415 | .and_then(|x| x.expand_towards(Direction::Down)), 416 | _ => self.go_up().and_then(|x| x.shrink_from(dir)), 417 | } 418 | } 419 | 420 | pub fn top(&self) -> Zipper { 421 | self.go_up().map_or(self.clone(), |x| x.top()) 422 | } 423 | 424 | pub fn to_tree(&self) -> Tree { 425 | self.top().tree 426 | } 427 | } 428 | 429 | #[derive(Clone)] 430 | pub struct BinarySpacePartition { 431 | tree: Option>, 432 | } 433 | 434 | impl BinarySpacePartition { 435 | pub fn boxed_new() -> Box { 436 | Box::new(BinarySpacePartition::empty()) 437 | } 438 | 439 | pub fn empty() -> BinarySpacePartition { 440 | BinarySpacePartition { tree: None } 441 | } 442 | 443 | fn make(tree: Tree) -> BinarySpacePartition { 444 | BinarySpacePartition { tree: Some(tree) } 445 | } 446 | 447 | fn make_zipper(&self) -> Option { 448 | self.tree.clone().map(Zipper::from_tree) 449 | } 450 | 451 | fn size(&self) -> usize { 452 | self.tree.clone().map_or(0, |x| x.number_of_leaves()) 453 | } 454 | 455 | fn from_zipper(zipper: Option) -> BinarySpacePartition { 456 | BinarySpacePartition { 457 | tree: zipper.map(|x| x.top().to_tree()), 458 | } 459 | } 460 | 461 | fn rectangles(&self, rect: Rectangle) -> Vec { 462 | self.tree.clone().map_or(Vec::new(), |t| match t { 463 | Tree::Leaf => vec![rect], 464 | Tree::Node(value, l, r) => { 465 | let (left_box, right_box) = value.split(rect); 466 | let left = BinarySpacePartition::make(l.deref().clone()).rectangles(left_box); 467 | let right = BinarySpacePartition::make(r.deref().clone()).rectangles(right_box); 468 | left.into_iter().chain(right.into_iter()).collect() 469 | } 470 | }) 471 | } 472 | 473 | pub fn do_to_nth(&self, n: usize, f: F) -> BinarySpacePartition 474 | where 475 | F: Fn(Zipper) -> Option, 476 | { 477 | BinarySpacePartition::from_zipper( 478 | self.make_zipper() 479 | .and_then(|x| x.go_to_nth_leaf(n)) 480 | .and_then(f), 481 | ) 482 | } 483 | 484 | pub fn split_nth(&self, n: usize) -> BinarySpacePartition { 485 | if self.tree.is_none() { 486 | BinarySpacePartition::make(Tree::Leaf) 487 | } else { 488 | self.do_to_nth(n, |x| x.split_current_leaf()) 489 | } 490 | } 491 | 492 | pub fn remove_nth(&self, n: usize) -> BinarySpacePartition { 493 | match self.tree { 494 | None => BinarySpacePartition::empty(), 495 | Some(ref tree) => match *tree { 496 | Tree::Leaf => BinarySpacePartition::empty(), 497 | _ => self.do_to_nth(n, |x| x.remove_current_leaf()), 498 | }, 499 | } 500 | } 501 | 502 | fn apply_to_leaf(&self, n: usize, f: F) -> BinarySpacePartition 503 | where 504 | F: Fn(Zipper) -> Option, 505 | { 506 | match self.tree { 507 | None => BinarySpacePartition::empty(), 508 | Some(ref tree) => match *tree { 509 | Tree::Leaf => self.clone(), 510 | _ => self.do_to_nth(n, |x| f(x)), 511 | }, 512 | } 513 | } 514 | 515 | pub fn rotate_nth(&self, n: usize) -> BinarySpacePartition { 516 | self.apply_to_leaf(n, |x| x.rotate_current_leaf()) 517 | } 518 | 519 | pub fn swap_nth(&self, n: usize) -> BinarySpacePartition { 520 | self.apply_to_leaf(n, |x| x.swap_current_leaf()) 521 | } 522 | 523 | pub fn grow_nth_towards(&self, dir: Direction, n: usize) -> BinarySpacePartition { 524 | self.apply_to_leaf(n, |x| x.expand_towards(dir)) 525 | } 526 | 527 | pub fn shrink_nth_from(&self, dir: Direction, n: usize) -> BinarySpacePartition { 528 | self.apply_to_leaf(n, |x| x.shrink_from(dir)) 529 | } 530 | 531 | fn maybe_index(s: Option>) -> (Vec, Option) { 532 | match s { 533 | None => (Vec::new(), None), 534 | Some(x) => (x.integrate(), Some(x.up.len())), 535 | } 536 | } 537 | 538 | fn stack_index(s: &Stack) -> usize { 539 | match BinarySpacePartition::maybe_index(Some(s.clone())) { 540 | (_, None) => 0, 541 | (_, Some(x)) => x, 542 | } 543 | } 544 | } 545 | 546 | impl Layout for BinarySpacePartition { 547 | fn apply_layout( 548 | &mut self, 549 | _: &dyn WindowSystem, 550 | screen: Rectangle, 551 | _: &GeneralConfig, 552 | stack: &Option>, 553 | ) -> Vec<(Window, Rectangle)> { 554 | match *stack { 555 | Some(ref st) => { 556 | debug!("{:?}", st.integrate()); 557 | let ws = st.integrate(); 558 | 559 | fn layout( 560 | bsp: BinarySpacePartition, 561 | l: usize, 562 | n: usize, 563 | ) -> Option { 564 | match l.cmp(&bsp.size()) { 565 | Ordering::Equal => Some(bsp), 566 | Ordering::Greater => layout(bsp.split_nth(n), l, n), 567 | Ordering::Less => layout(bsp.remove_nth(n), l, n), 568 | } 569 | } 570 | 571 | let bsp = layout( 572 | self.clone(), 573 | ws.len(), 574 | BinarySpacePartition::stack_index(st), 575 | ); 576 | 577 | let rs = match bsp { 578 | None => self.rectangles(screen), 579 | Some(ref b) => b.rectangles(screen), 580 | }; 581 | if let Some(ref t) = bsp { 582 | self.tree = t.tree.clone(); 583 | } 584 | 585 | ws.into_iter().zip(rs.into_iter()).collect() 586 | } 587 | None => Vec::new(), 588 | } 589 | } 590 | 591 | fn apply_message( 592 | &mut self, 593 | message: LayoutMessage, 594 | _: &dyn WindowSystem, 595 | stack: &Option>, 596 | _: &GeneralConfig, 597 | ) -> bool { 598 | match message { 599 | LayoutMessage::TreeRotate => { 600 | if let Some(ref s) = *stack { 601 | let index = BinarySpacePartition::stack_index(s); 602 | let r = self.rotate_nth(index); 603 | self.tree = r.tree; 604 | true 605 | } else { 606 | false 607 | } 608 | } 609 | LayoutMessage::TreeSwap => { 610 | if let Some(ref s) = *stack { 611 | let index = BinarySpacePartition::stack_index(s); 612 | let r = self.swap_nth(index); 613 | self.tree = r.tree; 614 | true 615 | } else { 616 | false 617 | } 618 | } 619 | LayoutMessage::TreeExpandTowards(dir) => { 620 | if let Some(ref s) = *stack { 621 | let index = BinarySpacePartition::stack_index(s); 622 | let r = self.grow_nth_towards(dir, index); 623 | self.tree = r.tree; 624 | true 625 | } else { 626 | false 627 | } 628 | } 629 | LayoutMessage::TreeShrinkFrom(dir) => { 630 | if let Some(ref s) = *stack { 631 | let index = BinarySpacePartition::stack_index(s); 632 | let r = self.shrink_nth_from(dir, index); 633 | self.tree = r.tree; 634 | true 635 | } else { 636 | false 637 | } 638 | } 639 | _ => false, 640 | } 641 | } 642 | 643 | fn description(&self) -> String { 644 | "BSP".to_owned() 645 | } 646 | 647 | fn copy(&self) -> Box { 648 | Box::new(self.clone()) 649 | } 650 | } 651 | --------------------------------------------------------------------------------