1use crate::DisplayAction;
4use crate::child_process::ChildID;
5use crate::config::{Config, InsertBehavior, ScratchPad};
6use crate::layouts::LayoutManager;
7use crate::models::{
8 FocusManager, Handle, Mode, ResizeCorner, ScratchPadName, Screen, Tags, Window, WindowHandle,
9 WindowState, WindowType, Workspace,
10};
11use leftwm_layouts::Layout;
12use serde::{Deserialize, Serialize};
13use std::collections::{HashMap, VecDeque};
14
15#[derive(Serialize, Deserialize, Debug)]
16pub struct State<H: Handle> {
17 #[serde(bound = "")]
18 pub screens: Vec<Screen<H>>,
19 #[serde(bound = "")]
20 pub windows: Vec<Window<H>>,
21 pub workspaces: Vec<Workspace>,
22 #[serde(bound = "")]
23 pub focus_manager: FocusManager<H>,
24 pub layout_manager: LayoutManager,
25 #[serde(bound = "")]
26 pub mode: Mode<H>,
27 #[serde(default)]
30 pub resize_corner: ResizeCorner,
31 pub active_scratchpads: HashMap<ScratchPadName, VecDeque<ChildID>>,
32 #[serde(bound = "")]
33 pub actions: VecDeque<DisplayAction<H>>,
34 pub tags: Tags, pub scratchpads: Vec<ScratchPad>,
37 pub layout_definitions: Vec<Layout>,
38 pub mousekey: Vec<String>,
39 pub default_width: i32,
40 pub default_height: i32,
41 pub disable_tile_drag: bool,
42 pub reposition_cursor_on_resize: bool,
43 pub insert_behavior: InsertBehavior,
44 pub single_window_border: bool,
45}
46
47#[allow(clippy::explicit_iter_loop)]
50impl<H: Handle> State<H> {
51 pub(crate) fn new(config: &impl Config) -> Self {
52 let mut tags = Tags::new();
53 config.create_list_of_tag_labels().iter().for_each(|label| {
54 tags.add_new(label.as_str());
55 });
56 tags.add_new_hidden("NSP");
57
58 Self {
59 focus_manager: FocusManager::new(config),
60 layout_manager: LayoutManager::new(config),
61 screens: Default::default(),
62 windows: Default::default(),
63 workspaces: Default::default(),
64 mode: Default::default(),
65 resize_corner: Default::default(),
66 active_scratchpads: Default::default(),
67 actions: Default::default(),
68 tags,
69 scratchpads: config.create_list_of_scratchpads(),
70 layout_definitions: config.layout_definitions(),
71 mousekey: config.mousekey(),
72 default_width: config.default_width(),
73 default_height: config.default_height(),
74 disable_tile_drag: config.disable_tile_drag(),
75 reposition_cursor_on_resize: config.reposition_cursor_on_resize(),
76 insert_behavior: config.insert_behavior(),
77 single_window_border: config.single_window_border(),
78 }
79 }
80
81 pub fn sort_windows(&mut self) {
83 let mut sorter = WindowSorter::new(self.windows.iter().collect());
84
85 sorter.sort(|w| w.states.contains(&WindowState::Above) && w.floating());
87
88 sorter.sort(|w| {
90 w.transient.is_some_and(|trans| {
91 self.windows
92 .iter()
93 .any(|w| w.handle == trans && (w.is_fullscreen() || w.is_maximized()))
94 })
95 });
96
97 sorter.sort(|w| w.r#type.is_dialog_like());
99
100 sorter.sort(Window::is_fullscreen);
102
103 sorter.sort(|w| w.r#type == WindowType::Normal && w.floating());
105
106 sorter.sort(|w| w.r#type == WindowType::Normal && w.is_maximized());
108
109 sorter.sort(|w| w.r#type == WindowType::Normal);
111
112 sorter.sort(|w| w.r#type == WindowType::Dock);
114
115 let windows = sorter.finish();
117 let handles = windows.iter().map(|w| w.handle).collect();
118
119 let act = DisplayAction::SetWindowOrder(handles);
121 self.actions.push_back(act);
122 }
123
124 pub fn handle_single_border(&mut self, border_width: i32) {
127 if self.single_window_border {
128 return;
129 }
130
131 for tag in self.tags.normal() {
132 let mut windows_on_tag: Vec<&mut Window<H>> = self
133 .windows
134 .iter_mut()
135 .filter(|w| w.tag.unwrap_or(0) == tag.id && w.r#type == WindowType::Normal)
136 .collect();
137
138 let wsid = self
139 .workspaces
140 .iter()
141 .find(|ws| ws.has_tag(&tag.id))
142 .map(|w| w.id);
143 let layout = self.layout_manager.layout(wsid.unwrap_or(1), tag.id);
144
145 if layout.is_monocle() {
147 for w in &mut windows_on_tag.iter_mut() {
148 w.border = 0;
149 }
150 continue;
151 }
152
153 if windows_on_tag.len() == 1 {
154 if let Some(w) = windows_on_tag.first_mut() {
155 w.border = 0;
156 }
157 continue;
158 }
159
160 for w in &mut windows_on_tag.iter_mut() {
161 w.border = border_width;
162 }
163 }
164 }
165
166 pub fn move_to_top(&mut self, handle: &WindowHandle<H>) -> Option<()> {
169 let index = self.windows.iter().position(|w| &w.handle == handle)?;
170 let window = self.windows.remove(index);
171 self.windows.insert(0, window);
172 self.sort_windows();
173 Some(())
174 }
175
176 pub fn update_static(&mut self) {
177 self.windows
178 .iter_mut()
179 .filter(|w| w.strut.is_some() || w.is_sticky())
180 .for_each(|w| {
181 let (x, y) = match w.strut {
182 Some(strut) => strut.center(),
183 None => w.calculated_xyhw().center(),
184 };
185 if let Some(ws) = self.workspaces.iter().find(|ws| ws.contains_point(x, y)) {
186 w.tag = ws.tag;
187 }
188 });
189 }
190
191 pub(crate) fn load_theme_config(&mut self, config: &impl Config) {
192 for win in &mut self.windows {
193 config.load_window(win);
194 }
195 for ws in &mut self.workspaces {
196 ws.load_config(config);
197 }
198 self.default_height = config.default_height();
199 self.default_width = config.default_width();
200 }
201
202 pub fn restore_state(&mut self, old_state: &Self) {
204 tracing::debug!("Restoring old state");
205
206 for old_tag in old_state.tags.all() {
208 if let Some(tag) = self.tags.get_mut(old_tag.id) {
209 tag.hidden = old_tag.hidden;
210 }
211 }
212
213 let are_tags_equal = self.tags.all().eq(&old_state.tags.all());
214
215 let mut ordered = vec![];
217 let mut had_strut = false;
218 old_state.windows.iter().for_each(|old_window| {
219 if let Some((index, new_window)) = self
220 .windows
221 .clone()
222 .iter_mut()
223 .enumerate()
224 .find(|w| w.1.handle == old_window.handle)
225 {
226 had_strut = old_window.strut.is_some() || had_strut;
227
228 new_window.set_floating(old_window.floating());
229 new_window.set_floating_offsets(old_window.get_floating_offsets());
230 new_window.apply_margin_multiplier(old_window.margin_multiplier);
231 new_window.pid = old_window.pid;
232 new_window.normal = old_window.normal;
233 if are_tags_equal {
234 new_window.tag = old_window.tag;
235 } else {
236 let mut new_tag = old_window.tag;
237 match new_tag {
239 Some(tag) if self.tags.get(tag).is_some() => {}
240 _ => new_tag = Some(1),
241 }
242 new_window.untag();
243 for &tag_id in new_tag.iter() {
244 new_window.tag(&tag_id);
245 }
246 }
247 new_window.strut = old_window.strut;
248 new_window.states.clone_from(&old_window.states);
249 ordered.push(new_window.clone());
250 self.windows.remove(index);
251
252 let act = DisplayAction::SetWindowTag(new_window.handle, new_window.tag);
254 self.actions.push_back(act);
255 }
256 });
257 if had_strut {
258 self.update_static();
259 }
260 self.windows.append(&mut ordered);
261
262 let all_tags = &self.tags;
264
265 for workspace in &mut self.workspaces {
267 if let Some(old_workspace) = old_state.workspaces.iter().find(|w| w.id == workspace.id)
268 {
269 workspace.margin_multiplier = old_workspace.margin_multiplier;
270 if are_tags_equal {
271 workspace.tag = old_workspace.tag;
272 } else {
273 let mut new_tag = old_workspace.tag;
274 match new_tag {
276 Some(tag) if all_tags.get(tag).is_some() => {}
277 _ => new_tag = Some(1),
278 }
279 for &tag_id in new_tag.iter() {
280 workspace.tag = Some(tag_id);
281 }
282 }
283 }
284 }
285
286 for (scratchpad, id) in &old_state.active_scratchpads {
288 self.active_scratchpads
289 .insert(scratchpad.clone(), id.clone());
290 }
291
292 self.focus_manager
294 .tags_last_window
295 .clone_from(&old_state.focus_manager.tags_last_window);
296 self.focus_manager
297 .tags_last_window
298 .retain(|&id, _| all_tags.get(id).is_some());
299 let tag_id = match old_state.focus_manager.tag(0) {
300 Some(tag_id) if self.tags.get(tag_id).is_some() => tag_id,
302 Some(_) => 1,
304 None => match self.workspaces.first() {
306 Some(ws) => ws.tag.unwrap_or(1),
307 _ => 1,
309 },
310 };
311 self.focus_tag(&tag_id);
312
313 self.layout_manager.restore(&old_state.layout_manager);
315 }
316}
317
318struct WindowSorter<'a, H: Handle> {
322 stack: Vec<&'a Window<H>>,
323 unsorted: Vec<&'a Window<H>>,
324}
325
326impl<'a, H: Handle> WindowSorter<'a, H> {
327 pub fn new(windows: Vec<&'a Window<H>>) -> Self {
328 Self {
329 stack: Vec::with_capacity(windows.len()),
330 unsorted: windows,
331 }
332 }
333
334 pub fn sort<F: Fn(&Window<H>) -> bool>(&mut self, filter: F) {
335 self.unsorted.retain(|window| {
336 if filter(window) {
337 self.stack.push(window);
338 false
339 } else {
340 true
341 }
342 });
343 }
344
345 pub fn finish(mut self) -> Vec<&'a Window<H>> {
346 self.stack.append(&mut self.unsorted);
347 self.stack
348 }
349}