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 fn save_state<H: Handle>(&self, state: &State<H>);
76
77 fn load_state<H: Handle>(&self, state: &mut State<H>);
79
80 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}