1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
//! Workspace management: create/rename/remove/switch, and per-workspace window queries.
//! Split out of the original single `manager.rs` - see `super` (`mod.rs`) for
//! `WindowManager`'s field definitions; everything here is plain `impl WindowManager`
//! methods, unchanged from before the split.
use super::*;
impl WindowManager {
// ---- Workspaces -----------------------------------------------------
pub fn add_workspace(&mut self, name: impl Into<String>, layout: impl Into<String>) -> WorkspaceId {
let id = self.next_workspace_id;
self.next_workspace_id += 1;
self.workspaces.push(Workspace::new(id, name, layout));
id
}
/// Sets a workspace's display name - used to apply `workspace.names`
/// at startup (`crates/srdwm/src/main.rs`'s `apply_workspace_count`),
/// since `WindowManager::new`/`add_workspace` otherwise leave every
/// workspace named after its own 1-based index regardless of what a
/// config asked for. A no-op if `id` doesn't exist.
pub fn rename_workspace(&mut self, id: WorkspaceId, name: impl Into<String>) {
if let Some(w) = self.workspaces.iter_mut().find(|w| w.id == id) {
w.name = name.into();
}
}
pub fn remove_workspace(&mut self, id: WorkspaceId) {
if self.workspaces.len() <= 1 {
return;
}
let fallback = self.workspaces.iter().map(|w| w.id).find(|&w| w != id).unwrap_or(0);
for w in self.windows.values_mut().filter(|w| w.workspace == id) {
w.workspace = fallback;
}
self.workspaces.retain(|w| w.id != id);
if self.current_workspace == id {
self.current_workspace = fallback;
}
}
/// Switches to `id`, unless `auto_back_and_forth` is set and `id` is
/// already the current workspace - in which case this jumps to
/// `previous_workspace` instead, sway's `workspace_auto_back_and_forth`
/// behavior. `previous_workspace` itself always tracks "whatever was
/// current right before this call changed it", updated on every real
/// switch regardless of the setting, so turning the setting on later
/// (or a client-driven switch, e.g. `ext_workspace_v1`'s `activate`)
/// doesn't need its own separate bookkeeping.
pub fn switch_workspace(&mut self, id: WorkspaceId) {
let target = if self.auto_back_and_forth && id == self.current_workspace { self.previous_workspace } else { id };
if self.workspaces.iter().any(|w| w.id == target) && target != self.current_workspace {
self.previous_workspace = self.current_workspace;
self.current_workspace = target;
}
}
pub fn current_workspace(&self) -> WorkspaceId {
self.current_workspace
}
pub fn workspace(&self, id: WorkspaceId) -> Option<&Workspace> {
self.workspaces.iter().find(|w| w.id == id)
}
pub fn workspaces(&self) -> &[Workspace] {
&self.workspaces
}
pub fn move_window_to_workspace(&mut self, id: WindowId, workspace: WorkspaceId) {
if let Some(w) = self.windows.get_mut(&id) {
w.workspace = workspace;
}
}
/// Windows that should currently be shown to the user: those on the
/// current workspace, and not minimized.
///
/// `current_workspace` is a single value shared by every monitor --
/// srdwm does not have Hyprland-style independent per-monitor
/// workspaces, so switching workspace changes what's shown on every
/// screen at once. `w.monitor` plays no part in this filter at all.
pub fn visible_windows(&self) -> impl Iterator<Item = &Window> {
self.windows.values().filter(|w| w.workspace == self.current_workspace && !w.minimized)
}
/// Same windows as [`Self::visible_windows`], but in real front-to-back
/// stacking order (topmost first) instead of arbitrary `HashMap`
/// iteration order. Needed anywhere a backend composites more than one
/// window's elements (content, decoration, border) together and their
/// relative order across *different* windows actually matters - unlike
/// `visible_windows`, which is fine for anything per-window in
/// isolation (border color, geometry) where order never came up.
/// `self.order` reversed is the same "topmost first" convention
/// `hit_test`/`window_at` already use.
pub fn visible_windows_front_to_back(&self) -> impl Iterator<Item = &Window> {
self.order.iter().rev().filter_map(|id| self.windows.get(id)).filter(|w| w.workspace == self.current_workspace && !w.minimized)
}
}
|