leftwm_core/models/
focus_manager.rs

1use crate::config::Config;
2use crate::{models::TagId, models::WindowHandle, Window, Workspace};
3
4use serde::{Deserialize, Serialize};
5use std::collections::{HashMap, VecDeque};
6
7use super::window::Handle;
8use super::MaybeWindowHandle;
9
10#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
11pub enum FocusBehaviour {
12    #[default]
13    Sloppy,
14    ClickTo,
15    Driven,
16}
17
18impl FocusBehaviour {
19    pub fn is_sloppy(self) -> bool {
20        self == FocusBehaviour::Sloppy
21    }
22
23    pub fn is_clickto(self) -> bool {
24        self == FocusBehaviour::ClickTo
25    }
26
27    pub fn is_driven(self) -> bool {
28        self == FocusBehaviour::Driven
29    }
30}
31
32/// Controls behaviour for window activation. Default is to mark the window as urgent.
33#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
34pub enum FocusOnActivationBehaviour {
35    /// Do nothing.
36    DoNothing,
37    /// Mark the window as urgent.
38    #[default]
39    MarkUrgent,
40    /// Switch to the window.
41    SwitchTo,
42}
43
44/// `FocusManager` stores the history of which workspaces, tags, and windows had focus.
45#[derive(Serialize, Deserialize, Debug, Clone)]
46pub struct FocusManager<H: Handle> {
47    pub workspace_history: VecDeque<usize>,
48    #[serde(bound = "")]
49    pub window_history: VecDeque<MaybeWindowHandle<H>>,
50    pub tag_history: VecDeque<TagId>,
51    #[serde(bound = "")]
52    pub tags_last_window: HashMap<TagId, WindowHandle<H>>,
53    pub last_mouse_position: Option<(i32, i32)>,
54    // entries below are configuration variables and are never changed
55    pub behaviour: FocusBehaviour,
56    pub focus_new_windows: bool,
57    pub sloppy_mouse_follows_focus: bool,
58    pub create_follows_cursor: bool,
59}
60
61impl<H: Handle> FocusManager<H> {
62    pub fn new(config: &impl Config) -> Self {
63        Self {
64            workspace_history: Default::default(),
65            window_history: Default::default(),
66            tag_history: Default::default(),
67            tags_last_window: Default::default(),
68            last_mouse_position: None,
69            behaviour: config.focus_behaviour(),
70            focus_new_windows: config.focus_new_windows(),
71            sloppy_mouse_follows_focus: config.sloppy_mouse_follows_focus(),
72            create_follows_cursor: config.create_follows_cursor(),
73        }
74    }
75
76    /// Return the currently focused workspace.
77    #[must_use]
78    pub fn workspace<'a, 'b>(&self, workspaces: &'a [Workspace]) -> Option<&'b Workspace>
79    where
80        'a: 'b,
81    {
82        let index = *self.workspace_history.front()?;
83        workspaces.get(index)
84    }
85
86    /// Return the currently focused workspace.
87    pub fn workspace_mut<'a, 'b>(
88        &self,
89        workspaces: &'a mut [Workspace],
90    ) -> Option<&'b mut Workspace>
91    where
92        'a: 'b,
93    {
94        let index = *self.workspace_history.front()?;
95        workspaces.get_mut(index)
96    }
97
98    /// Return the currently focused tag if the offset is 0.
99    /// Offset is used to reach further down the history.
100    pub fn tag(&self, offset: usize) -> Option<TagId> {
101        self.tag_history.get(offset).copied()
102    }
103
104    /// Return the currently focused window.
105    #[must_use]
106    pub fn window<'a, 'b>(&self, windows: &'a [Window<H>]) -> Option<&'b Window<H>>
107    where
108        'a: 'b,
109    {
110        let handle = *self.window_history.front()?;
111        if let Some(handle) = handle {
112            return windows.iter().find(|w| w.handle == handle);
113        }
114        None
115    }
116
117    /// Return the currently focused window.
118    pub fn window_mut<'a, 'b>(&self, windows: &'a mut [Window<H>]) -> Option<&'b mut Window<H>>
119    where
120        'a: 'b,
121    {
122        let handle = *self.window_history.front()?;
123        if let Some(handle) = handle {
124            return windows.iter_mut().find(|w| w.handle == handle);
125        }
126        None
127    }
128
129    // seems like duplicate code
130    pub fn create_follows_cursor(&self) -> bool {
131        self.create_follows_cursor
132    }
133}