From 50c850cb02c9196bfeb325b0487ce532aabc80dc Mon Sep 17 00:00:00 2001 From: alex-ds13 <145657253+alex-ds13@users.noreply.github.com> Date: Tue, 13 May 2025 20:01:37 +0100 Subject: [PATCH] feat(wm): floating over monocle This commit allows for floating windows on the floating workspace layer to be toggled on and off when monocle mode is enabled on a workspace. --- komorebi/src/border_manager/mod.rs | 187 ++++++++++++++++++----------- komorebi/src/process_command.rs | 57 ++++++--- komorebi/src/process_event.rs | 6 +- 3 files changed, 161 insertions(+), 89 deletions(-) diff --git a/komorebi/src/border_manager/mod.rs b/komorebi/src/border_manager/mod.rs index 3cd66255..39f8e4dc 100644 --- a/komorebi/src/border_manager/mod.rs +++ b/komorebi/src/border_manager/mod.rs @@ -6,6 +6,7 @@ use crate::core::BorderStyle; use crate::core::WindowKind; use crate::ring::Ring; use crate::windows_api; +use crate::workspace::Workspace; use crate::workspace::WorkspaceLayer; use crate::WindowManager; use crate::WindowsApi; @@ -212,6 +213,8 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result [focused_workspace_idx] .layer(); let foreground_window = WindowsApi::foreground_window().unwrap_or_default(); + let layer_changed = previous_layer != workspace_layer; + let forced_update = matches!(notification, Notification::ForceUpdate); drop(state); @@ -234,6 +237,17 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result .unwrap_or_default() .set_accent(window_kind_colour(window_kind))?; + if ws.layer() == &WorkspaceLayer::Floating { + for window in ws.floating_windows() { + let mut window_kind = WindowKind::Unfocused; + + if foreground_window == window.hwnd { + window_kind = WindowKind::Floating; + } + + window.set_accent(window_kind_colour(window_kind))?; + } + } continue 'monitors; } @@ -448,14 +462,40 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result windows_borders.insert(focused_window_hwnd, id); let border_hwnd = border.hwnd; - // Remove all borders on this monitor except monocle - remove_borders( - &mut borders, - &mut windows_borders, - monitor_idx, - |_, b| border_hwnd != b.hwnd, - )?; + if ws.layer() == &WorkspaceLayer::Floating { + handle_floating_borders( + &mut borders, + &mut windows_borders, + ws, + monitor_idx, + foreground_window, + layer_changed, + forced_update, + )?; + + // Remove all borders on this monitor except monocle and floating borders + remove_borders( + &mut borders, + &mut windows_borders, + monitor_idx, + |_, b| { + border_hwnd != b.hwnd + && !ws + .floating_windows() + .iter() + .any(|w| w.hwnd == b.tracking_hwnd) + }, + )?; + } else { + // Remove all borders on this monitor except monocle + remove_borders( + &mut borders, + &mut windows_borders, + monitor_idx, + |_, b| border_hwnd != b.hwnd, + )?; + } continue 'monitors; } @@ -569,9 +609,6 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result }; border.window_rect = rect; - let layer_changed = previous_layer != workspace_layer; - let forced_update = matches!(notification, Notification::ForceUpdate); - let should_invalidate = new_border || (last_focus_state != new_focus_state) || layer_changed @@ -591,65 +628,15 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result windows_borders.insert(focused_window_hwnd, id); } - { - for window in ws.floating_windows() { - let mut new_border = false; - let id = window.hwnd.to_string(); - let border = match borders.entry(id.clone()) { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => { - if let Ok(border) = Border::create( - &window.hwnd.to_string(), - window.hwnd, - monitor_idx, - ) { - new_border = true; - entry.insert(border) - } else { - continue 'monitors; - } - } - }; - - let last_focus_state = border.window_kind; - - let new_focus_state = if foreground_window == window.hwnd { - WindowKind::Floating - } else { - WindowKind::Unfocused - }; - - border.window_kind = new_focus_state; - - // Update the border's monitor idx in case it changed - border.monitor_idx = Some(monitor_idx); - - let rect = WindowsApi::window_rect(window.hwnd)?; - border.window_rect = rect; - - let layer_changed = previous_layer != workspace_layer; - let forced_update = - matches!(notification, Notification::ForceUpdate); - - let should_invalidate = new_border - || (last_focus_state != new_focus_state) - || layer_changed - || forced_update; - - if should_invalidate { - if forced_update && !new_border { - // Update the border brushes if there was a forced update - // notification and this is not a new border (new border's - // already have their brushes updated on creation) - border.update_brushes()?; - } - border.set_position(&rect, window.hwnd)?; - border.invalidate(); - } - - windows_borders.insert(window.hwnd, id); - } - } + handle_floating_borders( + &mut borders, + &mut windows_borders, + ws, + monitor_idx, + foreground_window, + layer_changed, + forced_update, + )?; } } } @@ -665,6 +652,68 @@ pub fn handle_notifications(wm: Arc>) -> color_eyre::Result Ok(()) } +fn handle_floating_borders( + borders: &mut HashMap>, + windows_borders: &mut HashMap, + ws: &Workspace, + monitor_idx: usize, + foreground_window: isize, + layer_changed: bool, + forced_update: bool, +) -> color_eyre::Result<()> { + for window in ws.floating_windows() { + let mut new_border = false; + let id = window.hwnd.to_string(); + let border = match borders.entry(id.clone()) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + if let Ok(border) = + Border::create(&window.hwnd.to_string(), window.hwnd, monitor_idx) + { + new_border = true; + entry.insert(border) + } else { + return Ok(()); + } + } + }; + + let last_focus_state = border.window_kind; + + let new_focus_state = if foreground_window == window.hwnd { + WindowKind::Floating + } else { + WindowKind::Unfocused + }; + + border.window_kind = new_focus_state; + + // Update the border's monitor idx in case it changed + border.monitor_idx = Some(monitor_idx); + + let rect = WindowsApi::window_rect(window.hwnd)?; + border.window_rect = rect; + + let should_invalidate = + new_border || (last_focus_state != new_focus_state) || layer_changed || forced_update; + + if should_invalidate { + if forced_update && !new_border { + // Update the border brushes if there was a forced update + // notification and this is not a new border (new border's + // already have their brushes updated on creation) + border.update_brushes()?; + } + border.set_position(&rect, window.hwnd)?; + border.invalidate(); + } + + windows_borders.insert(window.hwnd, id); + } + + Ok(()) +} + /// Removes all borders from monitor with index `monitor_idx` filtered by /// `condition`. This condition is a function that will take a reference to /// the container id and the border and returns a bool, if true that border diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 6a94c81e..d92cc97e 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -1281,6 +1281,7 @@ impl WindowManager { if i == focused_idx { to_focus = Some(*window); } else { + window.restore(); window.raise()?; } } @@ -1288,6 +1289,7 @@ impl WindowManager { if let Some(focused_window) = &to_focus { // The focused window should be the last one raised to make sure it is // on top + focused_window.restore(); focused_window.raise()?; } @@ -1296,34 +1298,51 @@ impl WindowManager { window.lower()?; } } + + if let Some(monocle) = workspace.monocle_container() { + if let Some(window) = monocle.focused_window() { + window.lower()?; + } + } } WorkspaceLayer::Floating => { workspace.set_layer(WorkspaceLayer::Tiling); - 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() { - if i == focused_container_idx { - to_focus = Some(*window); - } + if let Some(monocle) = workspace.monocle_container() { + if let Some(window) = monocle.focused_window() { + to_focus = Some(*window); window.raise()?; } - } + for window in workspace.floating_windows() { + window.hide(); + } + } else { + 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() { + if i == focused_container_idx { + to_focus = Some(*window); + } + window.raise()?; + } + } - let mut window_idx_pairs = workspace - .floating_windows_mut() - .make_contiguous() - .iter() - .collect::>(); + let mut window_idx_pairs = workspace + .floating_windows_mut() + .make_contiguous() + .iter() + .collect::>(); - // Sort by window area - window_idx_pairs.sort_by_key(|w| { - let rect = WindowsApi::window_rect(w.hwnd).unwrap_or_default(); - rect.right * rect.bottom - }); + // Sort by window area + window_idx_pairs.sort_by_key(|w| { + let rect = WindowsApi::window_rect(w.hwnd).unwrap_or_default(); + rect.right * rect.bottom + }); - for window in window_idx_pairs { - window.lower()?; + for window in window_idx_pairs { + window.lower()?; + } } } }; diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index a821ec85..91699632 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -500,7 +500,11 @@ impl WindowManager { } } - if !monocle_window_event && monocle_container.is_some() { + let workspace = self.focused_workspace()?; + if !(monocle_window_event + || workspace.layer() != &WorkspaceLayer::Tiling) + && monocle_container.is_some() + { window.hide(); } }