leftwm_core/
config.rs

1mod insert_behavior;
2mod window_hiding_strategy;
3mod workspace_config;
4
5use crate::display_servers::DisplayServer;
6use crate::layouts::LayoutMode;
7pub use crate::models::ScratchPad;
8pub use crate::models::{FocusBehaviour, Gutter, Margins, Size};
9use crate::models::{FocusOnActivationBehaviour, Handle, Manager, Window, WindowType};
10use crate::state::State;
11pub use insert_behavior::InsertBehavior;
12use leftwm_layouts::Layout;
13pub use window_hiding_strategy::WindowHidingStrategy;
14pub use workspace_config::Workspace;
15
16pub trait Config {
17    fn create_list_of_tag_labels(&self) -> Vec<String>;
18
19    fn workspaces(&self) -> Option<Vec<Workspace>>;
20
21    fn focus_behaviour(&self) -> FocusBehaviour;
22
23    fn focus_on_activation(&self) -> FocusOnActivationBehaviour;
24
25    fn mousekey(&self) -> Vec<String>;
26
27    fn create_list_of_scratchpads(&self) -> Vec<ScratchPad>;
28
29    fn layouts(&self) -> Vec<String>;
30
31    fn layout_definitions(&self) -> Vec<Layout>;
32
33    fn layout_mode(&self) -> LayoutMode;
34
35    fn insert_behavior(&self) -> InsertBehavior;
36
37    fn single_window_border(&self) -> bool;
38
39    fn focus_new_windows(&self) -> bool;
40
41    fn command_handler<H: Handle, SERVER>(
42        command: &str,
43        manager: &mut Manager<H, Self, SERVER>,
44    ) -> bool
45    where
46        SERVER: DisplayServer<H>,
47        Self: Sized;
48
49    fn always_float(&self) -> bool;
50    fn default_width(&self) -> i32;
51    fn default_height(&self) -> i32;
52    fn border_width(&self) -> i32;
53    fn margin(&self) -> Margins;
54    fn workspace_margin(&self) -> Option<Margins>;
55    fn gutter(&self) -> Option<Vec<Gutter>>;
56    fn default_border_color(&self) -> String;
57    fn floating_border_color(&self) -> String;
58    fn focused_border_color(&self) -> String;
59    fn background_color(&self) -> String;
60    fn on_new_window_cmd(&self) -> Option<String>;
61    fn get_list_of_gutters(&self) -> Vec<Gutter>;
62    fn auto_derive_workspaces(&self) -> bool;
63    fn disable_tile_drag(&self) -> bool;
64    fn disable_window_snap(&self) -> bool;
65    fn sloppy_mouse_follows_focus(&self) -> bool;
66    fn create_follows_cursor(&self) -> bool;
67    fn reposition_cursor_on_resize(&self) -> bool;
68    fn window_hiding_strategy(&self) -> WindowHidingStrategy;
69
70    /// Attempt to write current state to a file.
71    ///
72    /// It will be used to restore the state after soft reload.
73    ///
74    /// **Note:** this function cannot fail.
75    fn save_state<H: Handle>(&self, state: &State<H>);
76
77    /// Load saved state if it exists.
78    fn load_state<H: Handle>(&self, state: &mut State<H>);
79
80    /// Handle window placement based on `WM_CLASS`
81    fn setup_predefined_window<H: Handle>(
82        &self,
83        state: &mut State<H>,
84        window: &mut Window<H>,
85    ) -> bool;
86
87    fn load_window<H: Handle>(&self, window: &mut Window<H>) {
88        if window.r#type == WindowType::Normal {
89            window.margin = self.margin();
90            window.border = self.border_width();
91            window.must_float = self.always_float();
92        } else {
93            window.margin = Margins::new(0);
94            window.border = 0;
95        }
96    }
97}
98
99#[cfg(test)]
100pub(crate) mod tests {
101    use super::*;
102    use crate::models::MockHandle;
103    use crate::models::Screen;
104    use crate::models::Window;
105    use crate::models::WindowHandle;
106
107    #[allow(clippy::module_name_repetitions)]
108    #[derive(Default)]
109    pub struct TestConfig {
110        pub tags: Vec<String>,
111        pub layouts: Vec<String>,
112        pub layout_definitions: Vec<Layout>,
113        pub workspaces: Option<Vec<Workspace>>,
114        pub insert_behavior: InsertBehavior,
115        pub border_width: i32,
116        pub single_window_border: bool,
117    }
118
119    impl Config for TestConfig {
120        fn create_list_of_tag_labels(&self) -> Vec<String> {
121            self.tags.clone()
122        }
123        fn workspaces(&self) -> Option<Vec<Workspace>> {
124            self.workspaces.clone()
125        }
126        fn focus_behaviour(&self) -> FocusBehaviour {
127            FocusBehaviour::ClickTo
128        }
129        fn focus_on_activation(&self) -> FocusOnActivationBehaviour {
130            FocusOnActivationBehaviour::MarkUrgent
131        }
132        fn mousekey(&self) -> Vec<String> {
133            vec!["Mod4".to_owned()]
134        }
135        fn create_list_of_scratchpads(&self) -> Vec<ScratchPad> {
136            vec![]
137        }
138        fn layouts(&self) -> Vec<String> {
139            self.layouts.clone()
140        }
141        fn layout_definitions(&self) -> Vec<Layout> {
142            self.layout_definitions.clone()
143        }
144        fn layout_mode(&self) -> LayoutMode {
145            LayoutMode::Workspace
146        }
147
148        fn insert_behavior(&self) -> InsertBehavior {
149            self.insert_behavior
150        }
151
152        fn single_window_border(&self) -> bool {
153            self.single_window_border
154        }
155
156        fn focus_new_windows(&self) -> bool {
157            false
158        }
159        fn command_handler<H: Handle, SERVER>(
160            command: &str,
161            manager: &mut Manager<H, Self, SERVER>,
162        ) -> bool
163        where
164            SERVER: DisplayServer<H>,
165        {
166            match command {
167                "GoToTag2" => manager.command_handler(&crate::Command::GoToTag {
168                    tag: 2,
169                    swap: false,
170                }),
171                _ => unimplemented!("custom command handler: {:?}", command),
172            }
173        }
174        fn always_float(&self) -> bool {
175            false
176        }
177        fn default_width(&self) -> i32 {
178            1000
179        }
180        fn default_height(&self) -> i32 {
181            800
182        }
183        fn border_width(&self) -> i32 {
184            self.border_width
185        }
186        fn margin(&self) -> Margins {
187            Margins::new(0)
188        }
189        fn workspace_margin(&self) -> Option<Margins> {
190            None
191        }
192        fn gutter(&self) -> Option<Vec<Gutter>> {
193            unimplemented!()
194        }
195        fn default_border_color(&self) -> String {
196            unimplemented!()
197        }
198        fn floating_border_color(&self) -> String {
199            unimplemented!()
200        }
201        fn focused_border_color(&self) -> String {
202            unimplemented!()
203        }
204        fn background_color(&self) -> String {
205            unimplemented!()
206        }
207        fn on_new_window_cmd(&self) -> Option<String> {
208            None
209        }
210        fn get_list_of_gutters(&self) -> Vec<Gutter> {
211            Default::default()
212        }
213        fn disable_tile_drag(&self) -> bool {
214            false
215        }
216        fn disable_window_snap(&self) -> bool {
217            false
218        }
219        fn save_state<H: Handle>(&self, _state: &State<H>) {
220            unimplemented!()
221        }
222        fn load_state<H: Handle>(&self, _state: &mut State<H>) {
223            unimplemented!()
224        }
225        fn setup_predefined_window<H: Handle>(
226            &self,
227            _: &mut State<H>,
228            window: &mut Window<H>,
229        ) -> bool {
230            if window.res_class == Some("ShouldGoToTag2".to_string()) {
231                window.tag = Some(2);
232                true
233            } else {
234                false
235            }
236        }
237        fn sloppy_mouse_follows_focus(&self) -> bool {
238            true
239        }
240
241        fn auto_derive_workspaces(&self) -> bool {
242            true
243        }
244
245        fn reposition_cursor_on_resize(&self) -> bool {
246            true
247        }
248
249        fn create_follows_cursor(&self) -> bool {
250            false
251        }
252
253        fn window_hiding_strategy(&self) -> WindowHidingStrategy {
254            Default::default()
255        }
256    }
257
258    #[test]
259    fn ensure_command_handler_trait_boundary() {
260        let mut manager = Manager::new_test(vec!["1".to_string(), "2".to_string()]);
261        manager.screen_create_handler(Screen::default());
262        assert!(TestConfig::command_handler("GoToTag2", &mut manager));
263        assert_eq!(manager.state.focus_manager.tag_history, &[2, 1]);
264    }
265
266    #[test]
267    fn check_wm_class_is_associated_with_predefined_tag() {
268        let mut manager = Manager::new_test(vec!["1".to_string(), "2".to_string()]);
269        manager.screen_create_handler(Screen::default());
270        let mut subject = Window::new(WindowHandle::<MockHandle>(1), None, None);
271        subject.res_class = Some("ShouldGoToTag2".to_string());
272        manager.window_created_handler(subject, 0, 0);
273        assert!(manager.state.windows.iter().all(|w| w.has_tag(&2)));
274    }
275}