Skip to content

Commit 210f23d

Browse files
authored
Fixes #280 by adding two new methods to StackSet for cycling workspaces (#296)
* Fixes #280 by adding two new methods to StackSet for cycling workspaces * splitting new quickcheck test into two
1 parent fbcde44 commit 210f23d

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

src/pure/stack_set.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,37 @@ where
412412
wss.into_iter()
413413
}
414414

415+
fn focus_adjacent_workspace(&mut self, tags: Vec<String>) {
416+
let cur_tag = self.current_tag();
417+
let mut it = tags.iter().skip_while(|t| *t != cur_tag);
418+
// We should never be without at least our focused tag as a Screen requires a Workspace to
419+
// be present on it, but handling things this way means we avoid having any unwrapping or
420+
// raw indexing into vecs.
421+
if let Some(new_tag) = it.nth(1).or(tags.first()) {
422+
self.focus_tag(new_tag)
423+
}
424+
}
425+
426+
/// Move focus to the [Workspace] after the currently focused workspace as defined by their
427+
/// position in [StackSet::ordered_workspaces].
428+
///
429+
/// As with the behaviour of [StackSet::focus_tag], if the next tag is on another screen then
430+
/// focus will move to that screen rather than pulling the workspace to the active screen.
431+
pub fn focus_next_workspace(&mut self) {
432+
self.focus_adjacent_workspace(self.ordered_tags())
433+
}
434+
435+
/// Move focus to the [Workspace] before the currently focused workspace as defined by their
436+
/// position in [StackSet::ordered_workspaces].
437+
///
438+
/// As with the behaviour of [StackSet::focus_tag], if the next tag is on another screen then
439+
/// focus will move to that screen rather than pulling the workspace to the active screen.
440+
pub fn focus_previous_workspace(&mut self) {
441+
let mut tags = self.ordered_tags();
442+
tags.reverse();
443+
self.focus_adjacent_workspace(tags)
444+
}
445+
415446
/// Find the tag of the [Workspace] currently displayed on [Screen] `index`.
416447
///
417448
/// Returns [None] if the index is out of bounds
@@ -1210,6 +1241,28 @@ pub mod tests {
12101241
assert_eq!(s.previous_tag, "PREVIOUS");
12111242
}
12121243

1244+
#[test_case("2", true, "3"; "forward non-wrapping")]
1245+
#[test_case("5", true, "1"; "forward wrapping")]
1246+
#[test_case("3", false, "2"; "backward non-wrapping")]
1247+
#[test_case("1", false, "5"; "backward wrapping")]
1248+
#[test]
1249+
fn focus_next_prev_workspace_identifies_the_correct_tag(
1250+
initial_tag: &str,
1251+
next: bool,
1252+
expected_tag: &str,
1253+
) {
1254+
let mut s = test_stack_set(5, 3);
1255+
s.focus_tag(initial_tag);
1256+
1257+
if next {
1258+
s.focus_next_workspace();
1259+
} else {
1260+
s.focus_previous_workspace();
1261+
}
1262+
1263+
assert_eq!(s.current_tag(), expected_tag);
1264+
}
1265+
12131266
#[test]
12141267
fn floating_layer_clients_hold_focus() {
12151268
let mut s = test_stack_set(5, 3);
@@ -1461,4 +1514,28 @@ mod quickcheck_tests {
14611514

14621515
s.current_client() == Some(&c)
14631516
}
1517+
1518+
#[quickcheck]
1519+
fn focus_next_workspace_always_changes_workspace(mut s: StackSet<Xid>) -> bool {
1520+
if s.ordered_tags().len() == 1 {
1521+
return true; // need at least two tags to cycle
1522+
};
1523+
1524+
let current_tag = s.current_tag().to_string();
1525+
s.focus_next_workspace();
1526+
1527+
s.current_tag() != current_tag
1528+
}
1529+
1530+
#[quickcheck]
1531+
fn focus_previous_workspace_always_changes_workspace(mut s: StackSet<Xid>) -> bool {
1532+
if s.ordered_tags().len() == 1 {
1533+
return true; // need at least two tags to cycle
1534+
};
1535+
1536+
let current_tag = s.current_tag().to_string();
1537+
s.focus_previous_workspace();
1538+
1539+
s.current_tag() != current_tag
1540+
}
14641541
}

0 commit comments

Comments
 (0)