From d0ae92ca3a3271658bd248da181903402ca5a984 Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Sun, 2 Nov 2025 10:58:43 -0800 Subject: [PATCH] feat(wm): add visual feedback for preselection This commit adds visual feedback in the form of a ghost tile for preselections made by the preselect-direction command. A container with the id "PRESELECT" will be added to the workspace, and replaced when the next manage-able window is spawned. A new command, cancel-preselect, has been added to remove both the preselection index and the ghost tile if the user changes their mind. --- docs/cli/cancel-preselect.md | 12 ++++++++++++ komorebi/src/container.rs | 12 ++++++++++++ komorebi/src/core/mod.rs | 1 + komorebi/src/process_command.rs | 18 +++++++++++++++++- komorebi/src/window_manager.rs | 2 +- komorebi/src/workspace.rs | 18 +++++++++++++----- komorebic/src/main.rs | 5 +++++ mkdocs.yml | 1 + 8 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 docs/cli/cancel-preselect.md diff --git a/docs/cli/cancel-preselect.md b/docs/cli/cancel-preselect.md new file mode 100644 index 00000000..f0770633 --- /dev/null +++ b/docs/cli/cancel-preselect.md @@ -0,0 +1,12 @@ +# cancel-preselect + +``` +Cancel a workspace preselect set by the preselect-direction command, if one exists + +Usage: komorebic.exe cancel-preselect + +Options: + -h, --help + Print help + +``` diff --git a/komorebi/src/container.rs b/komorebi/src/container.rs index 2cd4651b..c2e5dd14 100644 --- a/komorebi/src/container.rs +++ b/komorebi/src/container.rs @@ -41,6 +41,18 @@ impl Lockable for Container { } impl Container { + pub fn preselect() -> Self { + Self { + id: "PRESELECT".to_string(), + locked: false, + windows: Default::default(), + } + } + + pub fn is_preselect(&self) -> bool { + self.id == "PRESELECT" + } + pub fn hide(&self, omit: Option) { for window in self.windows().iter().rev() { let mut should_hide = omit.is_none(); diff --git a/komorebi/src/core/mod.rs b/komorebi/src/core/mod.rs index 5cec545c..ff6eddf4 100644 --- a/komorebi/src/core/mod.rs +++ b/komorebi/src/core/mod.rs @@ -56,6 +56,7 @@ pub enum SocketMessage { FocusWindow(OperationDirection), MoveWindow(OperationDirection), PreselectDirection(OperationDirection), + CancelPreselect, CycleFocusWindow(CycleDirection), CycleMoveWindow(CycleDirection), StackWindow(OperationDirection), diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index fecc843e..b68eac28 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -305,9 +305,25 @@ impl WindowManager { } SocketMessage::PreselectDirection(direction) => { let focused_workspace = self.focused_workspace()?; - if matches!(focused_workspace.layer, WorkspaceLayer::Tiling) { + let mut update = false; + + if focused_workspace.preselected_container_idx.is_some() { + tracing::warn!( + "ignoring command as this workspace already has a direction preselect set" + ); + } else if matches!(focused_workspace.layer, WorkspaceLayer::Tiling) { self.preselect_container_in_direction(direction)?; + update = true; } + + if update { + self.focused_workspace_mut()?.update()?; + } + } + SocketMessage::CancelPreselect => { + let focused_workspace = self.focused_workspace_mut()?; + focused_workspace.cancel_preselect(); + focused_workspace.update()?; } SocketMessage::MoveWindow(direction) => { let focused_workspace = self.focused_workspace()?; diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 7732781b..295294eb 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -2077,7 +2077,7 @@ impl WindowManager { _ => new_idx, }; - workspace.preselect_container_index(adjusted_idx); + workspace.preselect_container_idx(adjusted_idx); } None => { tracing::debug!( diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index 75049454..525f401d 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -449,7 +449,8 @@ impl Workspace { } // make sure we are never holding on to empty containers - self.containers_mut().retain(|c| !c.windows().is_empty()); + self.containers_mut() + .retain(|c| c.is_preselect() || !c.windows().is_empty()); let container_padding = self .container_padding @@ -979,8 +980,16 @@ impl Workspace { container } - pub fn preselect_container_index(&mut self, insertion_index: usize) { - self.preselected_container_idx = Some(insertion_index); + pub fn preselect_container_idx(&mut self, insertion_idx: usize) { + self.preselected_container_idx = Some(insertion_idx); + self.insert_container_at_idx(insertion_idx, Container::preselect()); + } + + pub fn cancel_preselect(&mut self) { + if let Some(idx) = self.preselected_container_idx { + self.containers_mut().remove_respecting_locks(idx); + self.preselected_container_idx = None; + } } pub fn new_idx_for_direction(&self, direction: OperationDirection) -> Option { @@ -1084,9 +1093,8 @@ impl Workspace { pub fn new_container_for_window(&mut self, window: Window) { let next_idx = if let Some(idx) = self.preselected_container_idx { let next = idx; - self.preselected_container_idx = None; - + self.remove_container_by_idx(next); next } else if self.containers().is_empty() { 0 diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 060b0204..f8ec7ca9 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -1099,6 +1099,8 @@ enum SubCommand { /// Preselect the specified direction for the next window to be spawned on supported layouts #[clap(arg_required_else_help = true)] PreselectDirection(PreselectDirection), + /// Cancel a workspace preselect set by the preselect-direction command, if one exists + CancelPreselect, /// Minimize the focused window Minimize, /// Close the focused window @@ -2045,6 +2047,9 @@ fn main() -> eyre::Result<()> { SubCommand::PreselectDirection(arg) => { send_message(&SocketMessage::PreselectDirection(arg.operation_direction))?; } + SubCommand::CancelPreselect => { + send_message(&SocketMessage::CancelPreselect)?; + } SubCommand::CycleFocus(arg) => { send_message(&SocketMessage::CycleFocusWindow(arg.cycle_direction))?; } diff --git a/mkdocs.yml b/mkdocs.yml index 990546fd..8aaf7b1a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -112,6 +112,7 @@ nav: - cli/focus.md - cli/move.md - cli/preselect-direction.md + - cli/cancel-preselect.md - cli/minimize.md - cli/close.md - cli/force-focus.md