diff --git a/Cargo.lock b/Cargo.lock index ba002955..19637e89 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,9 +73,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" +checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" [[package]] name = "cfg-if" @@ -249,9 +249,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377c9b002a72a0b2c1a18c62e2f3864bdfea4a015e3683a96e24aa45dd6c02d1" +checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf" dependencies = [ "nix", "winapi 0.3.9", @@ -701,9 +701,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187" +checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188" dependencies = [ "bitflags", "cc", @@ -892,9 +892,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ "proc-macro2", ] @@ -1128,9 +1128,9 @@ checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" [[package]] name = "smallvec" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] name = "strsim" @@ -1161,9 +1161,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.77" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0" +checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" dependencies = [ "proc-macro2", "quote", @@ -1245,9 +1245,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f96e095c0c82419687c20ddf5cb3eadb61f4e1405923c9dc8e53a1adacbda8" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -1268,9 +1268,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98863d0dd09fa59a1b79c6750ad80dbda6b75f4e71c437a6a1a8cb91a8bcbd77" +checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" dependencies = [ "proc-macro2", "quote", @@ -1279,9 +1279,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46125608c26121c81b0c6d693eab5a420e416da7e43c426d2e8f7df8da8a3acf" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" dependencies = [ "lazy_static", ] @@ -1319,9 +1319,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd0568dbfe3baf7048b7908d2b32bca0d81cd56bec6d2a8f894b01d74f86be3" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" dependencies = [ "ansi_term", "chrono", diff --git a/komorebi-core/src/cycle_direction.rs b/komorebi-core/src/cycle_direction.rs index fafa76b2..9b448ec1 100644 --- a/komorebi-core/src/cycle_direction.rs +++ b/komorebi-core/src/cycle_direction.rs @@ -1,6 +1,7 @@ use clap::ArgEnum; use serde::Deserialize; use serde::Serialize; +use std::num::NonZeroUsize; use strum::Display; use strum::EnumString; @@ -13,17 +14,17 @@ pub enum CycleDirection { impl CycleDirection { #[must_use] - pub const fn next_idx(&self, idx: usize, len: usize) -> usize { + pub const fn next_idx(&self, idx: usize, len: NonZeroUsize) -> usize { match self { - CycleDirection::Previous => { + Self::Previous => { if idx == 0 { - len - 1 + len.get() - 1 } else { idx - 1 } } - CycleDirection::Next => { - if idx == len - 1 { + Self::Next => { + if idx == len.get() - 1 { 0 } else { idx + 1 diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index 32968226..2ee01c8d 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -27,6 +27,8 @@ pub enum SocketMessage { // Window / Container Commands FocusWindow(OperationDirection), MoveWindow(OperationDirection), + CycleFocusWindow(CycleDirection), + CycleMoveWindow(CycleDirection), StackWindow(OperationDirection), ResizeWindow(OperationDirection, Sizing), UnstackWindow, diff --git a/komorebi-core/src/operation_direction.rs b/komorebi-core/src/operation_direction.rs index 61bec052..c4f3826b 100644 --- a/komorebi-core/src/operation_direction.rs +++ b/komorebi-core/src/operation_direction.rs @@ -57,22 +57,22 @@ impl OperationDirection { len: usize, ) -> bool { match Self::flip_direction(self, layout_flip) { - OperationDirection::Up => match layout { + Self::Up => match layout { Layout::BSP => len > 2 && idx != 0 && idx != 1, Layout::Columns => false, Layout::Rows => idx != 0, }, - OperationDirection::Down => match layout { + Self::Down => match layout { Layout::BSP => len > 2 && idx != len - 1 && idx % 2 != 0, Layout::Columns => false, Layout::Rows => idx != len - 1, }, - OperationDirection::Left => match layout { + Self::Left => match layout { Layout::BSP => len > 1 && idx != 0, Layout::Columns => idx != 0, Layout::Rows => false, }, - OperationDirection::Right => match layout { + Self::Right => match layout { Layout::BSP => len > 1 && idx % 2 == 0 && idx != len - 1, Layout::Columns => idx != len - 1, Layout::Rows => false, diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 8df7d627..ab9cfd21 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -66,6 +66,12 @@ impl WindowManager { SocketMessage::MoveWindow(direction) => { self.move_container_in_direction(direction)?; } + SocketMessage::CycleFocusWindow(direction) => { + self.focus_container_in_cycle_direction(direction)?; + } + SocketMessage::CycleMoveWindow(direction) => { + self.move_container_in_cycle_direction(direction)?; + } SocketMessage::StackWindow(direction) => self.add_window_to_container(direction)?, SocketMessage::UnstackWindow => self.remove_window_from_container()?, SocketMessage::CycleStack(direction) => { diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 38a81b32..251057e5 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -739,18 +739,52 @@ impl WindowManager { self.update_focused_workspace(true) } + #[tracing::instrument(skip(self))] + pub fn focus_container_in_cycle_direction(&mut self, direction: CycleDirection) -> Result<()> { + tracing::info!("focusing container"); + let workspace = self.focused_workspace_mut()?; + + let new_idx = workspace + .new_idx_for_cycle_direction(direction) + .ok_or_else(|| anyhow!("this is not a valid direction from the current position"))?; + + workspace.focus_container(new_idx); + self.focused_window_mut()?.focus()?; + + Ok(()) + } + + #[tracing::instrument(skip(self))] + pub fn move_container_in_cycle_direction(&mut self, direction: CycleDirection) -> Result<()> { + tracing::info!("moving container"); + + let workspace = self.focused_workspace_mut()?; + + let current_idx = workspace.focused_container_idx(); + let new_idx = workspace + .new_idx_for_cycle_direction(direction) + .ok_or_else(|| anyhow!("this is not a valid direction from the current position"))?; + + workspace.swap_containers(current_idx, new_idx); + workspace.focus_container(new_idx); + self.update_focused_workspace(true) + } + #[tracing::instrument(skip(self))] pub fn cycle_container_window_in_direction(&mut self, direction: CycleDirection) -> Result<()> { tracing::info!("cycling container windows"); let container = self.focused_container_mut()?; - if container.windows().len() == 1 { + let len = NonZeroUsize::new(container.windows().len()) + .ok_or_else(|| anyhow!("there must be at least one window in a container"))?; + + if len.get() == 1 { return Err(anyhow!("there is only one window in this container")); } let current_idx = container.focused_window_idx(); - let next_idx = direction.next_idx(current_idx, container.windows().len()); + let next_idx = direction.next_idx(current_idx, len); container.focus_window(next_idx); container.load_focused_window(); diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index 4b6b559f..83c005c2 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -9,6 +9,7 @@ use getset::MutGetters; use getset::Setters; use serde::Serialize; +use komorebi_core::CycleDirection; use komorebi_core::Flip; use komorebi_core::Layout; use komorebi_core::OperationDirection; @@ -457,6 +458,12 @@ impl Workspace { None } } + pub fn new_idx_for_cycle_direction(&self, direction: CycleDirection) -> Option { + Option::from(direction.next_idx( + self.focused_container_idx(), + NonZeroUsize::new(self.containers().len())?, + )) + } pub fn move_window_to_container(&mut self, target_container_idx: usize) -> Result<()> { let focused_idx = self.focused_container_idx(); diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 68dff9db..122467f7 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -79,6 +79,8 @@ macro_rules! gen_enum_subcommand_args { gen_enum_subcommand_args! { Focus: OperationDirection, Move: OperationDirection, + CycleFocus: CycleDirection, + CycleMove: CycleDirection, Stack: OperationDirection, CycleStack: CycleDirection, FlipLayout: Flip, @@ -316,6 +318,12 @@ enum SubCommand { /// Move the focused window in the specified direction #[clap(setting = AppSettings::ArgRequiredElseHelp)] Move(Move), + /// Change focus to the window in the specified cycle direction + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + CycleFocus(CycleFocus), + /// Move the focused window in the specified cycle direction + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + CycleMove(CycleMove), /// Stack the focused window in the specified direction #[clap(setting = AppSettings::ArgRequiredElseHelp)] Stack(Stack), @@ -493,6 +501,12 @@ fn main() -> Result<()> { SubCommand::Move(arg) => { send_message(&*SocketMessage::MoveWindow(arg.operation_direction).as_bytes()?)?; } + SubCommand::CycleFocus(arg) => { + send_message(&*SocketMessage::CycleFocusWindow(arg.cycle_direction).as_bytes()?)?; + } + SubCommand::CycleMove(arg) => { + send_message(&*SocketMessage::CycleMoveWindow(arg.cycle_direction).as_bytes()?)?; + } SubCommand::MoveToMonitor(arg) => { send_message(&*SocketMessage::MoveContainerToMonitorNumber(arg.target).as_bytes()?)?; }