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