Skip to content

Commit

Permalink
feat: toggle_floating_state and is_floating (#306) (#307)
Browse files Browse the repository at this point in the history
* feat: toggle_floating_state and is_floating (#306)

* fix: remove unnecessary mut;

* fix: apply cargo fmt;

* fix: remove mutable borrow from is_floating; refactor: toggle_floating_state; testcase: floating_client_status tests is_floating;

* test: toggle_floating_state

* fix: apply cargo fmt;
  • Loading branch information
codybloemhard authored Aug 15, 2024
1 parent 3f6455b commit e0c0a2d
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 1 deletion.
20 changes: 19 additions & 1 deletion src/builtin/actions/floating.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub fn reposition<X: XConn>(dx: i32, dy: i32) -> Box<dyn KeyEventHandler<X>> {
})
}

/// Move the currently focused windo to the floating layer in its current on screen position
/// Move the currently focused window to the floating layer in its current on screen position
pub fn float_focused<X: XConn>() -> Box<dyn KeyEventHandler<X>> {
key_handler(|state, x: &X| {
let id = match state.client_set.current_client() {
Expand Down Expand Up @@ -82,6 +82,24 @@ pub fn sink_focused<X: XConn>() -> Box<dyn KeyEventHandler<X>> {
})
}

/// Sink the current window if it was floating, float it if it was tiled.
pub fn toggle_floating_focused<X: XConn>() -> Box<dyn KeyEventHandler<X>> {
key_handler(|state, x: &X| {
let id = match state.client_set.current_client() {
Some(&id) => id,
None => return Ok(()),
};

let r = x.client_geometry(id)?;

x.modify_and_refresh(state, |cs| {
if let Err(err) = cs.toggle_floating_state(id, r) {
error!(%err, %id, "unable to float requested client window");
}
})
})
}

/// Float all windows in their current tiled position
pub fn float_all<X: XConn>() -> Box<dyn KeyEventHandler<X>> {
key_handler(|state, x: &X| {
Expand Down
79 changes: 79 additions & 0 deletions src/pure/stack_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,11 @@ where
.map(|rr| rr.applied_to(&self.screens.focus.r))
}

/// Check whether a given client is currently floating.
pub fn is_floating(&self, client: &C) -> bool {
self.floating.contains_key(client)
}

/// Check whether a given tag currently has any floating windows present.
///
/// Returns false if the tag given is unknown to this StackSet.
Expand Down Expand Up @@ -799,6 +804,27 @@ impl StackSet<Xid> {
Ok(())
}

/// If a known client is floating, sink it and return its previous preferred screen position.
/// Otherwise, record it as floating with its preferred screen position.
///
/// # Errors
/// This method with return [Error::UnknownClient] if the given client is
/// not already managed in this stack_set.
///
/// This method with return [Error::ClientIsNotVisible] if the given client is
/// not currently mapped to a screen. This is required to determine the correct
/// relative positioning for the floating client as is it is moved between
/// screens.
pub fn toggle_floating_state(&mut self, client: Xid, r: Rect) -> Result<Option<Rect>> {
let rect = if self.is_floating(&client) {
self.sink(&client)
} else {
self.float(client, r)?;
None
};
Ok(rect)
}

pub(crate) fn update_screens(&mut self, rects: Vec<Rect>) -> Result<()> {
let n_old = self.screens.len();
let n_new = rects.len();
Expand Down Expand Up @@ -1280,6 +1306,59 @@ pub mod tests {
assert_eq!(s.current_client(), Some(&4));
}

#[test_case(&[]; "none")]
#[test_case(&[1]; "one")]
#[test_case(&[1, 2, 4]; "multiple")]
#[test]
fn floating_client_status(to_float: &[u8]) {
let mut s = test_stack_set(5, 3);
for n in 1..5 {
s.insert(n);
}

for c in to_float {
s.float_unchecked(*c, Rect::default());
}
for c in to_float {
assert!(s.is_floating(c));
}
for client in s.clients().copied() {

Check warning on line 1325 in src/pure/stack_set.rs

View workflow job for this annotation

GitHub Actions / clippy

unnecessary use of `copied`

warning: unnecessary use of `copied` --> src/pure/stack_set.rs:1325:23 | 1325 | for client in s.clients().copied() { | ^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned help: use | 1325 | for client in s.clients() { | ~~~~~~~~~~~ help: remove any references to the binding | 1326 - assert_eq!(to_float.contains(&client), s.is_floating(&client)); 1326 + assert_eq!(to_float.contains(client), s.is_floating(client)); |

Check warning on line 1325 in src/pure/stack_set.rs

View workflow job for this annotation

GitHub Actions / clippy

unnecessary use of `copied`

warning: unnecessary use of `copied` --> src/pure/stack_set.rs:1325:23 | 1325 | for client in s.clients().copied() { | ^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned help: use | 1325 | for client in s.clients() { | ~~~~~~~~~~~ help: remove any references to the binding | 1326 - assert_eq!(to_float.contains(&client), s.is_floating(&client)); 1326 + assert_eq!(to_float.contains(client), s.is_floating(client)); |

Check warning on line 1325 in src/pure/stack_set.rs

View workflow job for this annotation

GitHub Actions / clippy

unnecessary use of `copied`

warning: unnecessary use of `copied` --> src/pure/stack_set.rs:1325:23 | 1325 | for client in s.clients().copied() { | ^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned = note: `#[warn(clippy::unnecessary_to_owned)]` on by default help: use | 1325 | for client in s.clients() { | ~~~~~~~~~~~ help: remove any references to the binding | 1326 - assert_eq!(to_float.contains(&client), s.is_floating(&client)); 1326 + assert_eq!(to_float.contains(client), s.is_floating(client)); |
assert_eq!(to_float.contains(&client), s.is_floating(&client));
}
}

#[test]
fn toggle_floating_state() {
let mut ss: StackSet<Xid> = StackSet::try_new(
LayoutStack::default(),
["1", "2", "3"],
vec![Rect::default(); 2],
)
.expect("enough workspaces to cover the number of initial screens");

ss.insert(Xid(0));
ss.insert(Xid(1));

for i in 0..10 {
assert_eq!(ss.is_floating(&Xid(0)), i % 2 != 0);
let res = ss.toggle_floating_state(Xid(0), Rect::default());
assert!(res.is_ok());
}

assert!(matches!(
ss.toggle_floating_state(Xid(3), Rect::default()),
Err(Error::UnknownClient(_))
));

ss.insert(Xid(3));
ss.move_client_to_tag(&Xid(3), "3");

assert!(matches!(
ss.toggle_floating_state(Xid(3), Rect::default()),
Err(Error::ClientIsNotVisible(_))
));
}

#[test_case(1, "1"; "current focus to current tag")]
#[test_case(2, "1"; "from current tag to current tag")]
#[test_case(6, "1"; "from other tag to current tag")]
Expand Down

0 comments on commit e0c0a2d

Please sign in to comment.