From 5f629e1f1af24a3ab92c3e819045704b13b18bbd Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Sun, 8 Mar 2026 19:49:48 -0700 Subject: [PATCH] feat(wm): cycle monocle container when focusing in direction This commit implements something I've found myself wanting while working on the Macbook. When I have a monocle container active, I want to quickly be able to switch back and forth with an adjacent window in the underlying layout without having to toggle monocle mode off and back on again. This is now accomplished by using focus left/down to promote the previous window in the Ring to monocle, and by using focus right/down to focus the next window in the Ring to monocle. Borders were being funny so I just ended up nuking them whenever we cycle. --- komorebi/src/window_manager.rs | 38 +++++++++++++++++++++++++++++----- komorebi/src/workspace.rs | 17 +++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index e85a7849..74387a3d 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -2103,12 +2103,19 @@ impl WindowManager { tracing::info!("focusing container"); - let new_idx = - if workspace.maximized_window.is_some() || workspace.monocle_container.is_some() { - None - } else { - workspace.new_idx_for_direction(direction) + if workspace.monocle_container.is_some() { + let cycle_direction = match direction { + OperationDirection::Left | OperationDirection::Down => CycleDirection::Previous, + OperationDirection::Right | OperationDirection::Up => CycleDirection::Next, }; + return self.cycle_monocle(cycle_direction); + } + + let new_idx = if workspace.maximized_window.is_some() { + None + } else { + workspace.new_idx_for_direction(direction) + }; let mut cross_monitor_monocle_or_max = false; @@ -3093,6 +3100,27 @@ impl WindowManager { workspace.reintegrate_monocle_container() } + #[tracing::instrument(skip(self))] + pub fn cycle_monocle(&mut self, direction: CycleDirection) -> eyre::Result<()> { + tracing::info!("cycling monocle container"); + + if self.focused_workspace()?.containers().is_empty() { + return Ok(()); + } + + self.focused_workspace_mut()? + .cycle_monocle_container(direction)?; + + for container in self.focused_workspace_mut()?.containers_mut() { + container.hide(None); + } + + // borders were getting funny during cycles, can't be bothered to root cause it + border_manager::destroy_all_borders()?; + + self.update_focused_workspace(true, true) + } + #[tracing::instrument(skip(self))] pub fn toggle_maximize(&mut self) -> eyre::Result<()> { self.handle_unmanaged_window_behaviour()?; diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index 0fda4704..dd395a6a 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -1515,6 +1515,23 @@ impl Workspace { Ok(()) } + pub fn cycle_monocle_container(&mut self, direction: CycleDirection) -> eyre::Result<()> { + if self.containers().is_empty() { + return Ok(()); + } + + self.reintegrate_monocle_container()?; + + let new_idx = self + .new_idx_for_cycle_direction(direction) + .ok_or_eyre("there is no container to cycle monocle to")?; + + self.focus_container(new_idx); + self.new_monocle_container()?; + + Ok(()) + } + pub fn new_maximized_window(&mut self) -> eyre::Result<()> { let focused_idx = self.focused_container_idx();