leftwm_core/handlers/
focus_handler.rs

1#![allow(clippy::wildcard_imports)]
2
3use super::*;
4use crate::models::{Handle, TagId};
5use crate::state::State;
6use crate::{display_action::DisplayAction, models::FocusBehaviour};
7
8impl<H: Handle> State<H> {
9    /// Focuses a window based upon the `FocusBehaviour`
10    pub fn handle_window_focus(&mut self, handle: &WindowHandle<H>) {
11        match self.focus_manager.behaviour {
12            FocusBehaviour::Sloppy if self.focus_manager.sloppy_mouse_follows_focus => {
13                let act = DisplayAction::MoveMouseOver(*handle, false);
14                self.actions.push_back(act);
15            }
16            _ => self.focus_window(handle),
17        }
18    }
19
20    /// Focuses the given window.
21    pub fn focus_window(&mut self, handle: &WindowHandle<H>) {
22        let Some(window) = self.focus_window_work(handle) else {
23            return;
24        };
25
26        // Make sure the focused window's workspace is focused.
27        if let Some(workspace_id) = self
28            .workspaces
29            .iter()
30            .find(|ws| ws.is_displaying(&window))
31            .map(|ws| ws.id)
32        {
33            _ = self.focus_workspace_work(workspace_id);
34        }
35
36        // Make sure the focused window's tag is focused.
37        if let Some(tag) = window.tag {
38            _ = self.focus_tag_work(tag, true);
39        }
40    }
41
42    /// Focuses the given workspace.
43    // NOTE: Should only be called externally from this file.
44    pub fn focus_workspace(&mut self, workspace: &Workspace) {
45        if self.focus_workspace_work(workspace.id) {
46            // Make sure this workspaces tag is focused.
47            workspace.tag.iter().for_each(|t| {
48                self.focus_tag_work(*t, false);
49
50                if let Some(handle) = self.focus_manager.tags_last_window.get(t).copied() {
51                    self.focus_window_work(&handle);
52                } else {
53                    self.unfocus_current_window();
54                }
55            });
56        }
57    }
58
59    /// Focuses the given tag.
60    // NOTE: Should only be called externally from this file.
61    pub fn focus_tag(&mut self, tag: &TagId) {
62        if !self.focus_tag_work(*tag, false) {
63            return;
64        }
65        // Check each workspace, if its displaying this tag it should be focused too.
66        let to_focus: Vec<Workspace> = self
67            .workspaces
68            .iter()
69            .filter(|w| w.has_tag(tag))
70            .cloned()
71            .collect();
72        for ws in &to_focus {
73            self.focus_workspace_work(ws.id);
74        }
75        // Make sure the focused window is on this workspace.
76        if self.focus_manager.behaviour.is_sloppy() && self.focus_manager.sloppy_mouse_follows_focus
77        {
78            let act = DisplayAction::FocusWindowUnderCursor;
79            self.actions.push_back(act);
80        } else if let Some(handle) = self.focus_manager.tags_last_window.get(tag).copied() {
81            self.focus_window_work(&handle);
82        } else if let Some(ws) = to_focus.first() {
83            let handle = self
84                .windows
85                .iter()
86                .find(|w| ws.is_managed(w))
87                .map(|w| w.handle);
88            if let Some(h) = handle {
89                self.focus_window_work(&h);
90            }
91        }
92
93        // Unfocus last window if the target tag is empty
94        if let Some(window) = self.focus_manager.window(&self.windows) {
95            if window.tag != Some(*tag) {
96                self.unfocus_current_window();
97            }
98        }
99    }
100
101    /// Focuses the workspace containing a given point.
102    pub fn focus_workspace_with_point(&mut self, x: i32, y: i32) {
103        let Some(focused_id) = self
104            .focus_manager
105            .workspace(&self.workspaces)
106            .map(|ws| ws.id)
107        else {
108            return;
109        };
110
111        if let Some(ws) = self
112            .workspaces
113            .iter()
114            .find(|ws| ws.contains_point(x, y) && ws.id != focused_id)
115            .cloned()
116        {
117            self.focus_workspace(&ws);
118        }
119    }
120
121    /// Focuses the window containing a given point.
122    pub fn focus_window_with_point(&mut self, x: i32, y: i32) {
123        let handle_found: Option<WindowHandle<H>> = self
124            .windows
125            .iter()
126            .filter(|x| x.can_focus())
127            .find(|w| w.contains_point(x, y))
128            .map(|w| w.handle);
129        match handle_found {
130            Some(found) => self.focus_window(&found),
131            // backup plan, move focus closest window in workspace
132            None => self.focus_closest_window(x, y),
133        }
134    }
135
136    /// Validates that the given window is focused.
137    pub fn validate_focus_at(&mut self, handle: &WindowHandle<H>) {
138        // If the window is already focused do nothing.
139        if let Some(current) = self.focus_manager.window(&self.windows) {
140            if &current.handle == handle {
141                return;
142            }
143        }
144        // Focus the window only if it is also focusable.
145        if self
146            .windows
147            .iter()
148            .any(|w| w.can_focus() && &w.handle == handle)
149        {
150            self.focus_window(handle);
151        }
152    }
153
154    // Helper function.
155
156    fn focus_closest_window(&mut self, x: i32, y: i32) {
157        let Some(ws) = self.workspaces.iter().find(|ws| ws.contains_point(x, y)) else {
158            return;
159        };
160        let mut dists: Vec<(i32, &Window<H>)> = self
161            .windows
162            .iter()
163            .filter(|x| ws.is_managed(x) && x.can_focus())
164            .map(|w| (distance(w, x, y), w))
165            .collect();
166        dists.sort_by(|a, b| (a.0).cmp(&b.0));
167        if let Some(first) = dists.first() {
168            let handle = first.1.handle;
169            self.focus_window(&handle);
170        }
171    }
172
173    fn focus_tag_work(&mut self, tag: TagId, update_workspace: bool) -> bool {
174        if let Some(current_tag) = self.focus_manager.tag(0) {
175            if current_tag == tag {
176                return false;
177            }
178        };
179        // Clean old history.
180        self.focus_manager.tag_history.truncate(10);
181        // Add this focus to the history.
182        self.focus_manager.tag_history.push_front(tag);
183
184        if update_workspace {
185            if let Some(ws) = self.focus_manager.workspace_mut(&mut self.workspaces) {
186                ws.tag = Some(tag);
187                self.update_static();
188            }
189        }
190
191        let act = DisplayAction::SetCurrentTags(Some(tag));
192        self.actions.push_back(act);
193        true
194    }
195
196    fn focus_window_work(&mut self, handle: &WindowHandle<H>) -> Option<Window<H>> {
197        if self.screens.iter().any(|s| &s.root == handle) {
198            let act = DisplayAction::Unfocus(None, false);
199            self.actions.push_back(act);
200            self.focus_manager.window_history.push_front(None);
201            return None;
202        }
203        // Find the handle in our managed windows.
204        let found: &Window<H> = self.windows.iter().find(|w| &w.handle == handle)?;
205        // Docks don't want to get focus. If they do weird things happen. They don't get events...
206        if !found.is_managed() {
207            return None;
208        }
209        let previous = self.focus_manager.window(&self.windows);
210        // No new history if no change.
211        if let Some(previous) = previous {
212            if &previous.handle == handle {
213                // Return some so we still update the visuals.
214                return Some(found.clone());
215            }
216            if let Some(tag_id) = &previous.tag {
217                self.focus_manager
218                    .tags_last_window
219                    .insert(*tag_id, previous.handle);
220            }
221        }
222
223        // Clean old history.
224        self.focus_manager.window_history.truncate(10);
225        // Add this focus change to the history.
226        self.focus_manager.window_history.push_front(Some(*handle));
227
228        let act = DisplayAction::WindowTakeFocus {
229            window: found.clone(),
230            previous_window: previous.cloned(),
231        };
232        self.actions.push_back(act);
233
234        Some(found.clone())
235    }
236
237    fn focus_workspace_work(&mut self, ws_id: usize) -> bool {
238        // no new history if no change
239        if let Some(fws) = self.focus_manager.workspace(&self.workspaces) {
240            if fws.id == ws_id {
241                return false;
242            }
243        }
244        // Clean old history.
245        self.focus_manager.workspace_history.truncate(10);
246        // Add this focus to the history.
247        if let Some(index) = self.workspaces.iter().position(|x| x.id == ws_id) {
248            self.focus_manager.workspace_history.push_front(index);
249            return true;
250        }
251        false
252    }
253
254    fn unfocus_current_window(&mut self) {
255        if let Some(window) = self.focus_manager.window(&self.windows) {
256            self.actions.push_back(DisplayAction::Unfocus(
257                Some(window.handle),
258                window.floating(),
259            ));
260            self.focus_manager.window_history.push_front(None);
261            if let Some(tag_id) = &window.tag {
262                self.focus_manager
263                    .tags_last_window
264                    .insert(*tag_id, window.handle);
265            }
266        }
267    }
268}
269
270// Square root not needed as we are only interested in the comparison.
271fn distance<H: Handle>(window: &Window<H>, x: i32, y: i32) -> i32 {
272    // (x_2-x_1)²+(y_2-y_1)²
273    let (wx, wy) = window.calculated_xyhw().center();
274    let xs = (wx - x) * (wx - x);
275    let ys = (wy - y) * (wy - y);
276    xs + ys
277}
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282    use crate::{models::MockHandle, Manager};
283
284    #[test]
285    fn focusing_a_workspace_should_make_it_active() {
286        let mut manager = Manager::new_test(vec![]);
287        manager.screen_create_handler(Screen::default());
288        manager.screen_create_handler(Screen::default());
289        let expected = manager.state.workspaces[0].clone();
290        manager.state.focus_workspace(&expected);
291        let actual = manager
292            .state
293            .focus_manager
294            .workspace(&manager.state.workspaces)
295            .unwrap();
296        assert_eq!(&expected, actual);
297    }
298
299    #[test]
300    fn focusing_a_workspace_should_focus_its_last_active_window() {
301        let mut manager = Manager::new_test(vec!["1".to_string(), "2".to_string()]);
302        manager.screen_create_handler(Screen::default());
303        manager.screen_create_handler(Screen::default());
304        manager
305            .state
306            .focus_workspace(&manager.state.workspaces[0].clone());
307        manager.window_created_handler(
308            Window::new(WindowHandle::<MockHandle>(1), None, None),
309            -1,
310            -1,
311        );
312        manager.window_created_handler(
313            Window::new(WindowHandle::<MockHandle>(2), None, None),
314            -1,
315            -1,
316        );
317
318        manager
319            .state
320            .focus_workspace(&manager.state.workspaces[0].clone());
321
322        let expected = manager.state.windows.get(1).map(|w| w.handle);
323        manager.state.focus_window(&expected.unwrap());
324
325        manager
326            .state
327            .focus_workspace(&manager.state.workspaces[1].clone());
328        manager
329            .state
330            .focus_workspace(&manager.state.workspaces[0].clone());
331
332        let actual = manager
333            .state
334            .focus_manager
335            .window(&manager.state.windows)
336            .map(|w| w.handle);
337
338        assert_eq!(expected, actual);
339    }
340
341    #[test]
342    fn focusing_the_same_workspace_shouldnt_add_to_the_history() {
343        let mut manager = Manager::new_test(vec![]);
344        manager.screen_create_handler(Screen::default());
345        manager.screen_create_handler(Screen::default());
346        let ws = manager.state.workspaces[0].clone();
347        manager.state.focus_workspace(&ws);
348        let start_length = manager.state.focus_manager.workspace_history.len();
349        manager.state.focus_workspace(&ws);
350        let end_length = manager.state.focus_manager.workspace_history.len();
351        assert_eq!(start_length, end_length, "expected no new history event");
352    }
353
354    #[test]
355    fn focusing_a_window_should_make_it_active() {
356        let mut manager = Manager::new_test(vec![]);
357        manager.screen_create_handler(Screen::default());
358        manager.window_created_handler(
359            Window::new(WindowHandle::<MockHandle>(1), None, None),
360            -1,
361            -1,
362        );
363        manager.window_created_handler(
364            Window::new(WindowHandle::<MockHandle>(2), None, None),
365            -1,
366            -1,
367        );
368        let expected = manager.state.windows[0].clone();
369        manager.state.focus_window(&expected.handle);
370        let actual = manager
371            .state
372            .focus_manager
373            .window(&manager.state.windows)
374            .unwrap()
375            .handle;
376        assert_eq!(expected.handle, actual);
377    }
378
379    #[test]
380    fn focusing_the_same_window_shouldnt_add_to_the_history() {
381        let mut manager = Manager::new_test(vec![]);
382        manager.screen_create_handler(Screen::default());
383        let window = Window::new(WindowHandle::<MockHandle>(1), None, None);
384        manager.window_created_handler(window.clone(), -1, -1);
385        manager.state.focus_window(&window.handle);
386        let start_length = manager.state.focus_manager.workspace_history.len();
387        manager.window_created_handler(window.clone(), -1, -1);
388        manager.state.focus_window(&window.handle);
389        let end_length = manager.state.focus_manager.workspace_history.len();
390        assert_eq!(start_length, end_length, "expected no new history event");
391    }
392
393    #[test]
394    fn focusing_a_tag_should_make_it_active() {
395        let mut manager = Manager::new_test(vec![]);
396        manager.screen_create_handler(Screen::default());
397        let state = &mut manager.state;
398        let expected: usize = 1;
399        state.focus_tag(&expected);
400        let actual = state.focus_manager.tag(0).unwrap();
401        assert_eq!(actual, expected);
402    }
403
404    #[test]
405    fn focusing_the_same_tag_shouldnt_add_to_the_history() {
406        let mut manager = Manager::new_test(vec![]);
407        manager.screen_create_handler(Screen::default());
408        let state = &mut manager.state;
409        let tag: usize = 1;
410        state.focus_tag(&tag);
411        let start_length = state.focus_manager.tag_history.len();
412        state.focus_tag(&tag);
413        let end_length = state.focus_manager.tag_history.len();
414        assert_eq!(start_length, end_length, "expected no new history event");
415    }
416
417    #[test]
418    fn focusing_a_tag_should_focus_its_workspace() {
419        let mut manager = Manager::new_test(vec!["1".to_string()]);
420        manager.screen_create_handler(Screen::default());
421        manager.screen_create_handler(Screen::default());
422        manager.state.focus_tag(&1);
423        let actual = manager
424            .state
425            .focus_manager
426            .workspace(&manager.state.workspaces)
427            .unwrap();
428        assert_eq!(actual.id, 1);
429    }
430
431    #[test]
432    fn focusing_a_workspace_should_focus_its_tag() {
433        let mut manager = Manager::new_test(vec![]);
434        manager.screen_create_handler(Screen::default());
435        manager.screen_create_handler(Screen::default());
436        manager.screen_create_handler(Screen::default());
437        let ws = manager.state.workspaces[1].clone();
438        manager.state.focus_workspace(&ws);
439        let actual = manager.state.focus_manager.tag(0).unwrap();
440        assert_eq!(2, actual);
441    }
442
443    #[test]
444    fn focusing_a_window_should_focus_its_tag() {
445        let mut manager = Manager::new_test(vec![]);
446        manager.screen_create_handler(Screen::default());
447        manager.screen_create_handler(Screen::default());
448        manager.screen_create_handler(Screen::default());
449        let mut window = Window::new(WindowHandle::<MockHandle>(1), None, None);
450        window.tag(&2);
451        manager.state.windows.push(window.clone());
452        manager.state.focus_window(&window.handle);
453        let actual = manager.state.focus_manager.tag(0).unwrap();
454        assert_eq!(2, actual);
455    }
456
457    #[test]
458    fn focusing_a_window_should_focus_workspace() {
459        let mut manager = Manager::new_test(vec![]);
460        manager.screen_create_handler(Screen::default());
461        manager.screen_create_handler(Screen::default());
462        manager.screen_create_handler(Screen::default());
463        let mut window = Window::new(WindowHandle::<MockHandle>(1), None, None);
464        window.tag(&2);
465        manager.state.windows.push(window.clone());
466        manager.state.focus_window(&window.handle);
467        let actual = manager
468            .state
469            .focus_manager
470            .workspace(&manager.state.workspaces)
471            .unwrap();
472        let expected = &manager.state.workspaces[1];
473        assert_eq!(expected, actual);
474    }
475
476    #[test]
477    fn focusing_an_empty_tag_should_unfocus_any_focused_window() {
478        let mut manager = Manager::new_test(vec![]);
479        manager.screen_create_handler(Screen::default());
480        let mut window = Window::new(WindowHandle::<MockHandle>(1), None, None);
481        window.tag(&1);
482        manager.state.windows.push(window.clone());
483        manager.state.focus_window(&window.handle);
484        manager.state.focus_tag(&2);
485        let focused = manager.state.focus_manager.window(&manager.state.windows);
486        assert!(focused.is_none());
487    }
488}