From 6b918dae7fcc001034765c8737a9b7f04b7bf450 Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Sun, 12 Feb 2023 15:18:14 -0800 Subject: [PATCH] feat(wm): add named workspace commands This commit introduces three new commands, ensure-named-workspaces, named-workspace-rule, and focus-named-workspace, which email to reduce the configuration complexity by allowing users to refer to workspace names instead of monitor and workspace indices. --- komorebi-core/src/lib.rs | 3 +++ komorebi/src/process_command.rs | 34 ++++++++++++++++++++++++ komorebi/src/window_manager.rs | 41 ++++++++++++++++++++++++++++ komorebi/src/workspace.rs | 2 +- komorebic/src/main.rs | 47 +++++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index 49fd9f9b..72978480 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -77,6 +77,7 @@ pub enum SocketMessage { // Monitor and Workspace Commands MonitorIndexPreference(usize, i32, i32, i32, i32), EnsureWorkspaces(usize, usize), + EnsureNamedWorkspaces(usize, Vec), NewWorkspace, ToggleTiling, Stop, @@ -91,6 +92,7 @@ pub enum SocketMessage { FocusMonitorNumber(usize), FocusWorkspaceNumber(usize), FocusMonitorWorkspaceNumber(usize, usize), + FocusNamedWorkspace(String), ContainerPadding(usize, usize, i32), WorkspacePadding(usize, usize, i32), WorkspaceTiling(usize, usize, bool), @@ -114,6 +116,7 @@ pub enum SocketMessage { MonitorWorkAreaOffset(usize, Rect), ResizeDelta(i32), WorkspaceRule(ApplicationIdentifier, String, usize, usize), + NamedWorkspaceRule(ApplicationIdentifier, String, String), FloatRule(ApplicationIdentifier, String), ManageRule(ApplicationIdentifier, String), IdentifyObjectNameChangeApplication(ApplicationIdentifier, String), diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 383d8a91..94d54d9a 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -208,6 +208,18 @@ impl WindowManager { self.enforce_workspace_rules()?; } + SocketMessage::NamedWorkspaceRule(_, ref id, ref workspace) => { + if let Some((monitor_idx, workspace_idx)) = + self.monitor_workspace_index_by_name(workspace) + { + { + let mut workspace_rules = WORKSPACE_RULES.lock(); + workspace_rules.insert(id.to_string(), (monitor_idx, workspace_idx)); + } + + self.enforce_workspace_rules()?; + } + } SocketMessage::ManageRule(_, ref id) => { let mut manage_identifiers = MANAGE_IDENTIFIERS.lock(); if !manage_identifiers.contains(id) { @@ -453,6 +465,25 @@ impl WindowManager { self.focus_monitor(monitor_idx)?; self.focus_workspace(workspace_idx)?; } + SocketMessage::FocusNamedWorkspace(ref name) => { + let reenable_border = if BORDER_ENABLED.load(Ordering::SeqCst) { + self.hide_border()?; + true + } else { + false + }; + + if let Some((monitor_idx, workspace_idx)) = + self.monitor_workspace_index_by_name(name) + { + self.focus_monitor(monitor_idx)?; + self.focus_workspace(workspace_idx)?; + } + + if reenable_border { + self.show_border()?; + } + } SocketMessage::Stop => { tracing::info!( "received stop command, restoring all hidden windows and terminating process" @@ -480,6 +511,9 @@ impl WindowManager { SocketMessage::EnsureWorkspaces(monitor_idx, workspace_count) => { self.ensure_workspaces_for_monitor(monitor_idx, workspace_count)?; } + SocketMessage::EnsureNamedWorkspaces(monitor_idx, ref names) => { + self.ensure_named_workspaces_for_monitor(monitor_idx, names)?; + } SocketMessage::NewWorkspace => { self.new_workspace()?; } diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index c4ebedf5..85cb1276 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -1876,6 +1876,30 @@ impl WindowManager { Ok(()) } + #[tracing::instrument(skip(self))] + pub fn ensure_named_workspaces_for_monitor( + &mut self, + monitor_idx: usize, + names: &Vec, + ) -> Result<()> { + tracing::info!("ensuring workspace count"); + + let monitor = self + .monitors_mut() + .get_mut(monitor_idx) + .ok_or_else(|| anyhow!("there is no monitor"))?; + + monitor.ensure_workspace_count(names.len()); + + for (workspace_idx, name) in names.iter().enumerate() { + if let Some(workspace) = monitor.workspaces_mut().get_mut(workspace_idx) { + workspace.set_name(Option::from(name.clone())); + } + } + + Ok(()) + } + #[tracing::instrument(skip(self))] pub fn set_workspace_padding( &mut self, @@ -2045,6 +2069,23 @@ impl WindowManager { self.update_focused_workspace(false) } + #[tracing::instrument(skip(self))] + pub fn monitor_workspace_index_by_name(&mut self, name: &str) -> Option<(usize, usize)> { + tracing::info!("looking up workspace by name"); + + for (monitor_idx, monitor) in self.monitors().iter().enumerate() { + for (workspace_idx, workspace) in monitor.workspaces().iter().enumerate() { + if let Some(workspace_name) = workspace.name() { + if workspace_name == name { + return Option::from((monitor_idx, workspace_idx)); + } + } + } + } + + None + } + #[tracing::instrument(skip(self))] pub fn new_workspace(&mut self) -> Result<()> { tracing::info!("adding new workspace"); diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index 24354b2c..21ac4c02 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -26,7 +26,7 @@ use crate::INITIAL_CONFIGURATION_LOADED; #[derive(Debug, Clone, Serialize, Getters, CopyGetters, MutGetters, Setters, JsonSchema)] pub struct Workspace { - #[getset(set = "pub")] + #[getset(get = "pub", set = "pub")] name: Option, containers: Ring, #[getset(get = "pub", get_mut = "pub", set = "pub")] diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index eac1572f..69ecec4f 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -318,6 +318,14 @@ struct EnsureWorkspaces { workspace_count: usize, } +#[derive(Parser, AhkFunction)] +struct EnsureNamedWorkspaces { + /// Monitor index (zero-indexed) + monitor: usize, + /// Names of desired workspaces + names: Vec, +} + #[derive(Parser, AhkFunction)] struct FocusMonitorWorkspace { /// Target monitor index (zero-indexed) @@ -326,6 +334,12 @@ struct FocusMonitorWorkspace { target_workspace: usize, } +#[derive(Parser, AhkFunction)] +struct FocusNamedWorkspace { + /// Target workspace name + name: String, +} + #[derive(Parser, AhkFunction)] pub struct SendToMonitorWorkspace { /// Target monitor index (zero-indexed) @@ -412,6 +426,16 @@ struct WorkspaceRule { workspace: usize, } +#[derive(Parser, AhkFunction)] +struct NamedWorkspaceRule { + #[clap(value_enum)] + identifier: ApplicationIdentifier, + /// Identifier as a string + id: String, + /// Name of a workspace + workspace: String, +} + #[derive(Parser, AhkFunction)] struct ToggleFocusFollowsMouse { #[clap(value_enum, short, long, default_value = "windows")] @@ -622,6 +646,9 @@ enum SubCommand { /// Focus the specified workspace on the target monitor #[clap(arg_required_else_help = true)] FocusMonitorWorkspace(FocusMonitorWorkspace), + /// Focus the specified workspace on the target monitor + #[clap(arg_required_else_help = true)] + FocusNamedWorkspace(FocusNamedWorkspace), /// Focus the monitor in the given cycle direction #[clap(arg_required_else_help = true)] CycleMonitor(CycleMonitor), @@ -673,6 +700,9 @@ enum SubCommand { /// Create at least this many workspaces for the specified monitor #[clap(arg_required_else_help = true)] EnsureWorkspaces(EnsureWorkspaces), + /// Create at least this many workspaces for the specified monitor + #[clap(arg_required_else_help = true)] + EnsureNamedWorkspaces(EnsureNamedWorkspaces), /// Set the container padding for the specified workspace #[clap(arg_required_else_help = true)] ContainerPadding(ContainerPadding), @@ -748,6 +778,9 @@ enum SubCommand { /// Add a rule to associate an application with a workspace #[clap(arg_required_else_help = true)] WorkspaceRule(WorkspaceRule), + /// Add a rule to associate an application with a named workspace + #[clap(arg_required_else_help = true)] + NamedWorkspaceRule(NamedWorkspaceRule), /// Identify an application that sends EVENT_OBJECT_NAMECHANGE on launch #[clap(arg_required_else_help = true)] IdentifyObjectNameChangeApplication(IdentifyObjectNameChangeApplication), @@ -1158,6 +1191,12 @@ fn main() -> Result<()> { .as_bytes()?, )?; } + SubCommand::NamedWorkspaceRule(arg) => { + send_message( + &SocketMessage::NamedWorkspaceRule(arg.identifier, arg.id, arg.workspace) + .as_bytes()?, + )?; + } SubCommand::Stack(arg) => { send_message(&SocketMessage::StackWindow(arg.operation_direction).as_bytes()?)?; } @@ -1193,6 +1232,9 @@ fn main() -> Result<()> { .as_bytes()?, )?; } + SubCommand::FocusNamedWorkspace(arg) => { + send_message(&SocketMessage::FocusNamedWorkspace(arg.name).as_bytes()?)?; + } SubCommand::CycleMonitor(arg) => { send_message(&SocketMessage::CycleFocusMonitor(arg.cycle_direction).as_bytes()?)?; } @@ -1226,6 +1268,11 @@ fn main() -> Result<()> { .as_bytes()?, )?; } + SubCommand::EnsureNamedWorkspaces(arg) => { + send_message( + &SocketMessage::EnsureNamedWorkspaces(arg.monitor, arg.names).as_bytes()?, + )?; + } SubCommand::State => { let home = DATA_DIR.clone(); let mut socket = home;