Skip to content

Commit

Permalink
feat: add flag to jump to next empty workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
clecompt-msft committed Mar 14, 2024
1 parent 668a71d commit 17285b8
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 13 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Options:
-p, --previous Go to the previous workspace instead of the next
-m, --move Move the active window to the dispatched workspace
-n, --no-empty Don't create empty workspaces in the given direction
-e, --empty Go to the next empty workspace
-k, --keep-special Don't auto-close special workspaces when switching workspaces
-c, --cycle Cycle between workspaces instead of creating new ones
-v, --verbose Print debugging information
Expand Down
12 changes: 10 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ struct Cli {
#[arg(short = 'N', long, default_value_t = false, hide = true)]
no_empty_after: bool,

/// Go to the next empty workspace
///
/// This flag will cause hyprnome to advance to the next empty workspace,
/// as if on the last occupied workspace.
#[arg(short, long, default_value_t = false)]
empty: bool,

/// Don't auto-close special workspaces when switching workspaces
///
/// With --move:
Expand Down Expand Up @@ -127,17 +134,18 @@ pub fn log(text: &str) {
}

/// Gets an ID to dispatch based on the current workspace state and cli options
pub fn get_options() -> [bool; 7] {
pub fn get_options() -> [bool; 8] {
let Cli {
_move,
keep_special,
previous,
no_empty,
no_empty_before,
no_empty_after,
empty,
cycle,
..
} = Cli::parse();

[_move, keep_special, previous, no_empty, no_empty_before, no_empty_after, cycle]
[_move, keep_special, previous, no_empty, no_empty_before, no_empty_after, empty, cycle]
}
27 changes: 17 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct WorkspaceState {
no_empty_before: bool,
no_empty_after: bool,
previous: bool,
empty: bool,
cycle: bool,
}

Expand All @@ -34,29 +35,30 @@ impl WorkspaceState {
no_empty_before: false,
no_empty_after: false,
previous: false,
empty: false,
cycle: false,
}
}

/// Gets the previous workspace on a monitor, or try to choose the next left-most empty workspace
///
/// 1) If we're the first workspace on a monitor:
/// 1) If we're the first workspace on a monitor or the previous empty workspace was requested:
/// 1.1) If we're at the lowest possible id 1 OR the user doesn't want empty workspaces OR the user wants to cycle:
/// 1.1.1) If the user wants to cycle, go to the last workspace. Otherwise return the current id
/// 1.2) Otherwise, return the first unoccupied workspace before the current id
/// 1.2.1) If all other workspaces before are occupied, return the current id instead
/// 2) Otherwise, since there are workspaces before on the same monitor, select the one before.
#[must_use]
pub fn get_previous_id(&self) -> i32 {
if self.monitor_ids[0] == self.current_id {
if self.monitor_ids[0] == self.current_id || self.empty {
if self.monitor_ids[0] == 1 || self.no_empty_before || self.cycle {
if self.cycle {
self.monitor_ids[self.monitor_ids.len() - 1]
} else {
self.current_id
self.monitor_ids[0]
}
} else {
let mut i = self.current_id - 1;
let mut i = self.monitor_ids[0] - 1;

while i > 0 {
if !self.occupied_ids.contains(&i) {
Expand All @@ -66,7 +68,7 @@ impl WorkspaceState {
i -= 1;
}

self.current_id
self.monitor_ids[0]
}
} else {
self.monitor_ids[self.monitor_ids.iter().position(|&x| x == self.current_id).unwrap() - 1]
Expand All @@ -75,23 +77,23 @@ impl WorkspaceState {

/// Gets the next workspace on a monitor, or try to choose the next right-most empty workspace
///
/// 1) If we're the last workspace on a monitor:
/// 1) If we're the last workspace on a monitor or the next empty workspace was requested:
/// 1.1) If we're at the MAX OR the user doesn't want empty workspaces OR the user wants to cycle:
/// 1.1.1) If the user wants to cycle, go to the first workspace. Otherwise return the current id
/// 1.2) Otherwise, return the first unoccupied workspace after the current id
/// 1.2.1) If all other workspaces after are occupied, return the current id instead
/// 2) Otherwise, since there are workspaces after on the same monitor, select the one after.
#[must_use]
pub fn get_next_id(&self) -> i32 {
if self.monitor_ids[self.monitor_ids.len() - 1] == self.current_id {
if self.monitor_ids[self.monitor_ids.len() - 1] == self.current_id || self.empty {
if self.monitor_ids[self.monitor_ids.len() - 1] == i32::MAX || self.no_empty_after || self.cycle {
if self.cycle {
self.monitor_ids[0]
} else {
self.current_id
self.monitor_ids[self.monitor_ids.len() - 1]
}
} else {
let mut i = self.current_id + 1;
let mut i = self.monitor_ids[self.monitor_ids.len() - 1] + 1;

while i < i32::MAX {
if !self.occupied_ids.contains(&i) {
Expand All @@ -104,7 +106,7 @@ impl WorkspaceState {
if !self.occupied_ids.contains(&i) {
i
} else {
self.current_id
self.monitor_ids[self.monitor_ids.len() - 1]
}
}
} else {
Expand All @@ -127,6 +129,11 @@ impl WorkspaceState {
self.previous = previous;
}

/// Sets `empty`
pub fn set_empty(&mut self, empty: bool) {
self.empty = empty;
}

/// Sets `cycle`
pub fn set_cycle(&mut self, cycle: bool) {
self.cycle = cycle;
Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ mod hyprland;
///
/// Specific features are abstracted into lib to make things testable.
fn main() {
let [_move, keep_special, previous, no_empty, no_empty_before, no_empty_after, cycle] = cli::get_options();
let [_move, keep_special, previous, no_empty, no_empty_before, no_empty_after, empty, cycle] = cli::get_options();

if let Ok(mut state) = hyprland::get_state() {
state.set_no_empty_before(no_empty || no_empty_before);
state.set_no_empty_after(no_empty || no_empty_after);
state.set_previous(previous);
state.set_empty(empty);
state.set_cycle(cycle);

cli::log(&format!("{state}"));
Expand Down
21 changes: 21 additions & 0 deletions tests/get_next_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,24 @@ fn cycle() {

assert_eq!(state.get_next_id(), 500, "should return the first workspace on monitor if last workspace on monitor");
}

#[test]
fn empty() {
let current_id = 500;
let monitor_ids = [500, 502].to_vec();
let occupied_ids = [500, 502].to_vec();
let mut state = WorkspaceState::new(current_id, monitor_ids, occupied_ids);

state.set_empty(true);

assert_eq!(state.get_next_id(), 503, "should return next empty workspace if requested");

let current_id = 500;
let monitor_ids = [500, 502].to_vec();
let occupied_ids = [500, 502, 503, 504].to_vec();
let mut state = WorkspaceState::new(current_id, monitor_ids, occupied_ids);

state.set_empty(true);

assert_eq!(state.get_next_id(), 505, "should return next empty workspace if requested with occupied workspaces on other monitors");
}
21 changes: 21 additions & 0 deletions tests/get_previous_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,24 @@ fn cycle() {

assert_eq!(state.get_previous_id(), 504, "should return the last workspace on monitor if first workspace on monitor");
}

#[test]
fn empty() {
let current_id = 501;
let monitor_ids = [500, 501].to_vec();
let occupied_ids = [500, 501].to_vec();
let mut state = WorkspaceState::new(current_id, monitor_ids, occupied_ids);

state.set_empty(true);

assert_eq!(state.get_previous_id(), 499, "should return previous empty workspace if requested");

let current_id = 501;
let monitor_ids = [500, 501].to_vec();
let occupied_ids = [499, 500, 501].to_vec();
let mut state = WorkspaceState::new(current_id, monitor_ids, occupied_ids);

state.set_empty(true);

assert_eq!(state.get_previous_id(), 498, "should return previous empty workspace if requested with occupied workspaces on other monitors");
}

0 comments on commit 17285b8

Please sign in to comment.