diff --git a/packages/wm-common/src/app_command.rs b/packages/wm-common/src/app_command.rs index 11c1e06f8..23f2e8c46 100644 --- a/packages/wm-common/src/app_command.rs +++ b/packages/wm-common/src/app_command.rs @@ -361,6 +361,12 @@ pub struct InvokeFocusCommand { #[clap(long)] pub recent_workspace: bool, + + #[clap(long)] + pub next_populated_workspace: bool, + + #[clap(long)] + pub prev_populated_workspace: bool, } #[derive(Args, Clone, Debug, PartialEq, Serialize)] @@ -399,6 +405,12 @@ pub struct InvokeMoveCommand { #[clap(long)] pub recent_workspace: bool, + + #[clap(long)] + pub next_populated_workspace: bool, + + #[clap(long)] + pub prev_populated_workspace: bool, } #[derive(Args, Clone, Debug, PartialEq, Serialize)] diff --git a/packages/wm/src/models/workspace_target.rs b/packages/wm/src/models/workspace_target.rs index dbfd62b1c..3195ffa89 100644 --- a/packages/wm/src/models/workspace_target.rs +++ b/packages/wm/src/models/workspace_target.rs @@ -9,6 +9,8 @@ pub enum WorkspaceTarget { PreviousActiveInMonitor, Next, Previous, + NextPopulated, + PreviousPopulated, #[allow(dead_code)] Direction(Direction), } diff --git a/packages/wm/src/wm.rs b/packages/wm/src/wm.rs index 1e64ee550..2c0aa1fa9 100644 --- a/packages/wm/src/wm.rs +++ b/packages/wm/src/wm.rs @@ -317,6 +317,22 @@ impl WindowManager { )?; } + if args.next_populated_workspace { + focus_workspace( + WorkspaceTarget::NextPopulated, + state, + config, + )?; + } + + if args.prev_populated_workspace { + focus_workspace( + WorkspaceTarget::PreviousPopulated, + state, + config, + )?; + } + Ok(()) } InvokeCommand::Ignore => { @@ -411,12 +427,30 @@ impl WindowManager { if args.prev_active_workspace_on_monitor { move_window_to_workspace( - window, + window.clone(), WorkspaceTarget::PreviousActiveInMonitor, state, config, )?; } + + if args.next_populated_workspace { + move_window_to_workspace( + window.clone(), + WorkspaceTarget::NextPopulated, + state, + config, + )?; + } + + if args.prev_populated_workspace { + move_window_to_workspace( + window, + WorkspaceTarget::PreviousPopulated, + state, + config, + )?; + } Ok(()) } diff --git a/packages/wm/src/wm_state.rs b/packages/wm/src/wm_state.rs index 4ade7b011..b928f6bb9 100644 --- a/packages/wm/src/wm_state.rs +++ b/packages/wm/src/wm_state.rs @@ -472,6 +472,102 @@ impl WmState { (previous_workspace_name, previous_workspace) } + WorkspaceTarget::NextPopulated => { + let all_sorted = self.sorted_workspaces(config); + let populated: Vec<_> = all_sorted + .iter() + .filter(|w| w.has_children()) + .cloned() + .collect(); + + let next = if populated.is_empty() { + None + } else { + match populated + .iter() + .position(|w| w.id() == origin_workspace.id()) + { + Some(i) => populated + .get(i + 1) + .or_else(|| populated.first()) + .cloned(), + None => { + // Origin is empty; find its position in the full + // sorted list and pick the next populated workspace. + let origin_pos = all_sorted + .iter() + .position(|w| w.id() == origin_workspace.id()) + .unwrap_or(0); + populated + .iter() + .find(|w| { + all_sorted + .iter() + .position(|s| s.id() == w.id()) + .unwrap_or(0) + > origin_pos + }) + .or_else(|| populated.first()) + .cloned() + } + } + }; + + ( + next.as_ref().map(|w| w.config().name), + next, + ) + } + WorkspaceTarget::PreviousPopulated => { + let all_sorted = self.sorted_workspaces(config); + let populated: Vec<_> = all_sorted + .iter() + .filter(|w| w.has_children()) + .cloned() + .collect(); + + let prev = if populated.is_empty() { + None + } else { + match populated + .iter() + .position(|w| w.id() == origin_workspace.id()) + { + Some(i) => populated + .get( + i.checked_sub(1) + .unwrap_or(populated.len() - 1), + ) + .cloned(), + None => { + // Origin is empty; find its position in the full + // sorted list and pick the previous populated workspace. + let origin_pos = all_sorted + .iter() + .position(|w| w.id() == origin_workspace.id()) + .unwrap_or(0); + populated + .iter() + .rev() + .find(|w| { + all_sorted + .iter() + .position(|s| s.id() == w.id()) + .unwrap_or(0) + < origin_pos + }) + .or_else(|| populated.last()) + .cloned() + } + } + }; + + ( + prev.as_ref().map(|w| w.config().name), + prev, + ) + } + WorkspaceTarget::Direction(direction) => { let origin_monitor = origin_workspace.monitor().context("No focused monitor.")?;