diff options
Diffstat (limited to 'crates/core/src/manager/workspaces.rs')
| -rw-r--r-- | crates/core/src/manager/workspaces.rs | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/crates/core/src/manager/workspaces.rs b/crates/core/src/manager/workspaces.rs new file mode 100644 index 0000000..485ced0 --- /dev/null +++ b/crates/core/src/manager/workspaces.rs @@ -0,0 +1,96 @@ +//! 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 + /// active workspace of whichever monitor they're assigned to, and not minimized. + 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) + } + +} |