From 67cb5d044aab662fada84573f866081afe8903f0 Mon Sep 17 00:00:00 2001 From: alex-ds13 <145657253+alex-ds13@users.noreply.github.com> Date: Mon, 24 Feb 2025 02:05:52 +0000 Subject: [PATCH] feat(wm): move all windows on ws layer toggle This commit makes it so when you toggle between workspace layers it moves all windows of that layer to the top of the z-order. So if you move to `Floating` layer, then all floating windows are moved to the top, and if you go back to the `Tiling` layer, all tiled containers are moved to the top. --- komorebi/src/border_manager/mod.rs | 18 ++++++++++++++++-- komorebi/src/process_command.rs | 29 +++++++++++++++++++++++++---- komorebi/src/window.rs | 24 ++++++++++++++++++++++++ komorebi/src/windows_api.rs | 27 ++++++++++++++++++++++++--- 4 files changed, 89 insertions(+), 9 deletions(-) diff --git a/komorebi/src/border_manager/mod.rs b/komorebi/src/border_manager/mod.rs index a6422da5..3b636f98 100644 --- a/komorebi/src/border_manager/mod.rs +++ b/komorebi/src/border_manager/mod.rs @@ -5,6 +5,7 @@ use crate::core::BorderImplementation; use crate::core::BorderStyle; use crate::core::WindowKind; use crate::ring::Ring; +use crate::workspace::WorkspaceLayer; use crate::workspace_reconciliator::ALT_TAB_HWND; use crate::Colour; use crate::Rgb; @@ -166,6 +167,7 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result let mut previous_pending_move_op = None; let mut previous_is_paused = false; let mut previous_notification: Option = None; + let mut previous_layer = WorkspaceLayer::default(); 'receiver: for notification in receiver { // Check the wm state every time we receive a notification @@ -182,6 +184,9 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result .iter() .map(|w| w.hwnd) .collect::>(); + let workspace_layer = *state.monitors.elements()[focused_monitor_idx].workspaces() + [focused_workspace_idx] + .layer(); let foreground_window = WindowsApi::foreground_window().unwrap_or_default(); drop(state); @@ -507,9 +512,13 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result } }; + let layer_changed = previous_layer != workspace_layer; + let should_invalidate = match last_focus_state { None => true, - Some(last_focus_state) => last_focus_state != new_focus_state, + Some(last_focus_state) => { + (last_focus_state != new_focus_state) || layer_changed + } }; if new_border || should_invalidate { @@ -561,9 +570,13 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result let rect = WindowsApi::window_rect(window.hwnd)?; + let layer_changed = previous_layer != workspace_layer; + let should_invalidate = match last_focus_state { None => true, - Some(last_focus_state) => last_focus_state != new_focus_state, + Some(last_focus_state) => { + last_focus_state != new_focus_state || layer_changed + } }; if new_border { @@ -587,6 +600,7 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result previous_pending_move_op = pending_move_op; previous_is_paused = is_paused; previous_notification = Some(notification); + previous_layer = workspace_layer; } Ok(()) diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 500cd839..351a8d0f 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -1049,24 +1049,45 @@ impl WindowManager { let mouse_follows_focus = self.mouse_follows_focus; let workspace = self.focused_workspace_mut()?; + let mut to_focus = None; match workspace.layer() { WorkspaceLayer::Tiling => { workspace.set_layer(WorkspaceLayer::Floating); - if let Some(first) = workspace.floating_windows().first() { - first.focus(mouse_follows_focus)?; + for (i, window) in workspace.floating_windows().iter().enumerate() { + if i == 0 { + to_focus = Some(*window); + } + window.raise()?; + } + + for container in workspace.containers() { + if let Some(window) = container.focused_window() { + window.lower()?; + } } } WorkspaceLayer::Floating => { workspace.set_layer(WorkspaceLayer::Tiling); - if let Some(container) = workspace.focused_container() { + let focused_container_idx = workspace.focused_container_idx(); + for (i, container) in workspace.containers_mut().iter_mut().enumerate() { if let Some(window) = container.focused_window() { - window.focus(mouse_follows_focus)?; + if i == focused_container_idx { + to_focus = Some(*window); + } + window.raise()?; } } + + for window in workspace.floating_windows() { + window.lower()?; + } } }; + if let Some(window) = to_focus { + window.focus(mouse_follows_focus)?; + } } SocketMessage::Stop => { self.stop(false)?; diff --git a/komorebi/src/window.rs b/komorebi/src/window.rs index a191db50..91df0e37 100644 --- a/komorebi/src/window.rs +++ b/komorebi/src/window.rs @@ -736,6 +736,30 @@ impl Window { self.update_style(&style) } + /// Raise the window to the top of the Z order, but do not activate or focus + /// it. Use raise_and_focus_window to activate and focus a window. + /// It also checks if there is a border attached to this window and if it is + /// it raises it as well. + pub fn raise(self) -> Result<()> { + WindowsApi::raise_window(self.hwnd)?; + if let Some(border) = crate::border_manager::window_border(self.hwnd) { + WindowsApi::raise_window(border.hwnd)?; + } + Ok(()) + } + + /// Lower the window to the bottom of the Z order, but do not activate or focus + /// it. + /// It also checks if there is a border attached to this window and if it is + /// it lowers it as well. + pub fn lower(self) -> Result<()> { + WindowsApi::lower_window(self.hwnd)?; + if let Some(border) = crate::border_manager::window_border(self.hwnd) { + WindowsApi::lower_window(border.hwnd)?; + } + Ok(()) + } + #[tracing::instrument(fields(exe, title), skip(debug))] pub fn should_manage( self, diff --git a/komorebi/src/windows_api.rs b/komorebi/src/windows_api.rs index 54dd29ce..6ffd1508 100644 --- a/komorebi/src/windows_api.rs +++ b/komorebi/src/windows_api.rs @@ -109,6 +109,7 @@ use windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE; use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE; use windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT; use windows::Win32::UI::WindowsAndMessaging::HDEVNOTIFY; +use windows::Win32::UI::WindowsAndMessaging::HWND_BOTTOM; use windows::Win32::UI::WindowsAndMessaging::HWND_TOP; use windows::Win32::UI::WindowsAndMessaging::LWA_ALPHA; use windows::Win32::UI::WindowsAndMessaging::REGISTER_NOTIFICATION_FLAGS; @@ -477,10 +478,13 @@ impl WindowsApi { unsafe { BringWindowToTop(HWND(as_ptr!(hwnd))) }.process() } - // Raise the window to the top of the Z order, but do not activate or focus - // it. Use raise_and_focus_window to activate and focus a window. + /// Raise the window to the top of the Z order, but do not activate or focus + /// it. Use raise_and_focus_window to activate and focus a window. pub fn raise_window(hwnd: isize) -> Result<()> { - let flags = SetWindowPosition::NO_MOVE | SetWindowPosition::NO_ACTIVATE; + let flags = SetWindowPosition::NO_MOVE + | SetWindowPosition::NO_SIZE + | SetWindowPosition::NO_ACTIVATE + | SetWindowPosition::SHOW_WINDOW; let position = HWND_TOP; Self::set_window_pos( @@ -491,6 +495,23 @@ impl WindowsApi { ) } + /// Lower the window to the bottom of the Z order, but do not activate or focus + /// it. + pub fn lower_window(hwnd: isize) -> Result<()> { + let flags = SetWindowPosition::NO_MOVE + | SetWindowPosition::NO_SIZE + | SetWindowPosition::NO_ACTIVATE + | SetWindowPosition::SHOW_WINDOW; + + let position = HWND_BOTTOM; + Self::set_window_pos( + HWND(as_ptr!(hwnd)), + &Rect::default(), + position, + flags.bits(), + ) + } + pub fn set_border_pos(hwnd: isize, layout: &Rect, position: isize) -> Result<()> { let flags = { SetWindowPosition::NO_SEND_CHANGING