From d5b65840427c2797730778bfa009fd70baecf251 Mon Sep 17 00:00:00 2001 From: alex-ds13 <145657253+alex-ds13@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:53:36 +0100 Subject: [PATCH] feat(wm): add float override option This commit introduces a new option `float_override`, which makes it so every every window opened, shown or uncloaked will be set to floating, but it won't be ignored. It will be added to the floating_windows of the workspace, meaning that the user can later tile that window with toggle-float command. This allows the users to have all windows open as floating and then manually tile the ones they want. This interactively rebased commit contains changes from the following individual commits: 0e8dc85fb146fba05b9aa509dc91a5432a4b2ba0 feat(wm): add new float override option 30bdaf33d5d2ce80b6f6e9a98aec745654c8159b feat(cli): add command for new option `ToggleFloatOverride` b7bedce1ca8472b9ed762d57f4d36e79eb156f8c feat(wm): add window_container_behaviour and float_override to workspaces 221e4ea545c383e0094bf04a34b698c0c8be7a46 feat(cli): add commands for workspace new window behaviour and float_override b182cb5818efc491e2f5088e56093bdd2ae4864b fix(wm): show floating apps in front of stacked windows as well 7c9cb11a9bdf536c9d59770f74c4589c379c4796 fix(wm): Remove unecessary duplicated code --- komorebi/src/core/mod.rs | 18 +++++++++++++- komorebi/src/process_command.rs | 33 ++++++++++++++++++++++--- komorebi/src/process_event.rs | 22 ++++++++++------- komorebi/src/static_config.rs | 30 +++++++++++++++++++---- komorebi/src/window_manager.rs | 43 +++++++++++++++++++++++++++------ komorebi/src/workspace.rs | 19 ++++++++++++--- komorebic/src/main.rs | 19 +++++++++++++++ 7 files changed, 154 insertions(+), 30 deletions(-) diff --git a/komorebi/src/core/mod.rs b/komorebi/src/core/mod.rs index c08cb4b70..7cbe78363 100644 --- a/komorebi/src/core/mod.rs +++ b/komorebi/src/core/mod.rs @@ -77,6 +77,7 @@ pub enum SocketMessage { ToggleMonocle, ToggleMaximize, ToggleWindowContainerBehaviour, + ToggleFloatOverride, WindowHidingBehaviour(HidingBehaviour), ToggleCrossMonitorMoveBehaviour, CrossMonitorMoveBehaviour(MoveBehaviour), @@ -90,6 +91,8 @@ pub enum SocketMessage { CycleLayout(CycleDirection), ChangeLayoutCustom(PathBuf), FlipLayout(Axis), + ToggleWorkspaceWindowContainerBehaviour, + ToggleWorkspaceFloatOverride, // Monitor and Workspace Commands MonitorIndexPreference(usize, i32, i32, i32, i32), DisplayIndexPreference(usize, String), @@ -343,10 +346,23 @@ pub enum FocusFollowsMouseImplementation { } #[derive( - Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema, + Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, +)] +pub struct WindowManagementBehaviour { + /// The current WindowContainerBehaviour to be used + pub current_behaviour: WindowContainerBehaviour, + /// Override of `current_behaviour` to open new windows as floating windows + /// that can be later toggled to tiled, when false it will default to + /// `current_behaviour` again. + pub float_override: bool, +} + +#[derive( + Clone, Copy, Debug, Default, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema, PartialEq )] pub enum WindowContainerBehaviour { /// Create a new container for each new window + #[default] Create, /// Append new windows to the focused window container Append, diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 09fa303ab..ac0aa2425 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -1346,15 +1346,42 @@ impl WindowManager { self.resize_delta = delta; } SocketMessage::ToggleWindowContainerBehaviour => { - match self.window_container_behaviour { + match self.window_management_behaviour.current_behaviour { WindowContainerBehaviour::Create => { - self.window_container_behaviour = WindowContainerBehaviour::Append; + self.window_management_behaviour.current_behaviour = WindowContainerBehaviour::Append; } WindowContainerBehaviour::Append => { - self.window_container_behaviour = WindowContainerBehaviour::Create; + self.window_management_behaviour.current_behaviour = WindowContainerBehaviour::Create; } } } + SocketMessage::ToggleFloatOverride => { + self.window_management_behaviour.float_override = !self.window_management_behaviour.float_override; + } + SocketMessage::ToggleWorkspaceWindowContainerBehaviour => { + let current_global_behaviour = self.window_management_behaviour.current_behaviour; + if let Some(behaviour) = self.focused_workspace_mut()?.window_container_behaviour_mut() { + match behaviour { + WindowContainerBehaviour::Create => *behaviour = WindowContainerBehaviour::Append, + WindowContainerBehaviour::Append => *behaviour = WindowContainerBehaviour::Create, + } + } else { + self.focused_workspace_mut()?.set_window_container_behaviour( + Some(match current_global_behaviour { + WindowContainerBehaviour::Create => WindowContainerBehaviour::Append, + WindowContainerBehaviour::Append => WindowContainerBehaviour::Create, + }) + ); + }; + } + SocketMessage::ToggleWorkspaceFloatOverride => { + let current_global_override = self.window_management_behaviour.float_override; + if let Some(float_override) = self.focused_workspace_mut()?.float_override_mut() { + *float_override = !*float_override; + } else { + self.focused_workspace_mut()?.set_float_override(Some(!current_global_override)); + }; + } SocketMessage::WindowHidingBehaviour(behaviour) => { let mut hiding_behaviour = HIDING_BEHAVIOUR.lock(); *hiding_behaviour = behaviour; diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index 30a8a952c..46fd9e038 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -333,8 +333,8 @@ impl WindowManager { } if proceed { - let behaviour = - self.window_container_behaviour(focused_monitor_idx, focused_workspace_idx); + let mut behaviour = + self.window_management_behaviour(focused_monitor_idx, focused_workspace_idx); let workspace = self.focused_workspace_mut()?; let workspace_contains_window = workspace.contains_window(window.hwnd); let monocle_container = workspace.monocle_container().clone(); @@ -360,11 +360,13 @@ impl WindowManager { } } - if should_float && !matches!(event, WindowManagerEvent::Manage(_)) { + behaviour.float_override = behaviour.float_override || (should_float && !matches!(event, WindowManagerEvent::Manage(_))); + + if behaviour.float_override { workspace.floating_windows_mut().push(window); - self.update_focused_workspace(false, true)?; + self.update_focused_workspace(false, false)?; } else { - match behaviour { + match behaviour.current_behaviour { WindowContainerBehaviour::Create => { workspace.new_container_for_window(window); self.update_focused_workspace(false, false)?; @@ -375,7 +377,6 @@ impl WindowManager { .ok_or_else(|| anyhow!("there is no focused container"))? .add_window(window); self.update_focused_workspace(true, false)?; - stackbar_manager::send_notification(); } } @@ -431,8 +432,8 @@ impl WindowManager { let focused_monitor_idx = self.focused_monitor_idx(); let focused_workspace_idx = self.focused_workspace_idx().unwrap_or_default(); - let window_container_behaviour = - self.window_container_behaviour(focused_monitor_idx, focused_workspace_idx); + let window_management_behaviour = + self.window_management_behaviour(focused_monitor_idx, focused_workspace_idx); let workspace = self.focused_workspace_mut()?; let focused_container_idx = workspace.focused_container_idx(); @@ -550,8 +551,11 @@ impl WindowManager { } // Here we handle a simple move on the same monitor which is treated as // a container swap + } else if window_management_behaviour.float_override { + workspace.floating_windows_mut().push(window); + self.update_focused_workspace(false, false)?; } else { - match window_container_behaviour { + match window_management_behaviour.current_behaviour { WindowContainerBehaviour::Create => { match workspace.container_idx_from_current_point() { Some(target_idx) => { diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index 2114bc6cd..1e9ee0ad7 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -68,6 +68,7 @@ use crate::core::OperationBehaviour; use crate::core::Rect; use crate::core::SocketMessage; use crate::core::WindowContainerBehaviour; +use crate::core::WindowManagementBehaviour; use color_eyre::Result; use crossbeam_channel::Receiver; use hotwatch::EventKind; @@ -130,6 +131,13 @@ pub struct WorkspaceConfig { /// Apply this monitor's window-based work area offset (default: true) #[serde(skip_serializing_if = "Option::is_none")] pub apply_window_based_work_area_offset: Option, + /// Determine what happens when a new window is opened (default: Create) + #[serde(skip_serializing_if = "Option::is_none")] + pub window_container_behaviour: Option, + /// Enable or disable float override, which makes it so every new window opens in floating mode + /// (default: false) + #[serde(skip_serializing_if = "Option::is_none")] + pub float_override: Option, } impl From<&Workspace> for WorkspaceConfig { @@ -182,6 +190,8 @@ impl From<&Workspace> for WorkspaceConfig { initial_workspace_rules: None, workspace_rules: None, apply_window_based_work_area_offset: Some(value.apply_window_based_work_area_offset()), + window_container_behaviour: *value.window_container_behaviour(), + float_override: *value.float_override(), } } } @@ -235,6 +245,10 @@ pub struct StaticConfig { /// Determine what happens when a new window is opened (default: Create) #[serde(skip_serializing_if = "Option::is_none")] pub window_container_behaviour: Option, + /// Enable or disable float override, which makes it so every new window opens in floating mode + /// (default: false) + #[serde(skip_serializing_if = "Option::is_none")] + pub float_override: Option, /// Determine what happens when a window is moved across a monitor boundary (default: Swap) #[serde(skip_serializing_if = "Option::is_none")] pub cross_monitor_move_behaviour: Option, @@ -520,7 +534,8 @@ impl From<&WindowManager> for StaticConfig { Self { invisible_borders: None, resize_delta: Option::from(value.resize_delta), - window_container_behaviour: Option::from(value.window_container_behaviour), + window_container_behaviour: Option::from(value.window_management_behaviour.current_behaviour), + float_override: Option::from(value.window_management_behaviour.float_override), cross_monitor_move_behaviour: Option::from(value.cross_monitor_move_behaviour), cross_boundary_behaviour: Option::from(value.cross_boundary_behaviour), unmanaged_window_operation_behaviour: Option::from( @@ -1031,9 +1046,10 @@ impl StaticConfig { is_paused: false, virtual_desktop_id: current_virtual_desktop(), work_area_offset: value.global_work_area_offset, - window_container_behaviour: value - .window_container_behaviour - .unwrap_or(WindowContainerBehaviour::Create), + window_management_behaviour: WindowManagementBehaviour { + current_behaviour: value.window_container_behaviour.unwrap_or(WindowContainerBehaviour::Create), + float_override: value.float_override.unwrap_or_default(), + }, cross_monitor_move_behaviour: value .cross_monitor_move_behaviour .unwrap_or(MoveBehaviour::Swap), @@ -1208,7 +1224,11 @@ impl StaticConfig { } if let Some(val) = value.window_container_behaviour { - wm.window_container_behaviour = val; + wm.window_management_behaviour.current_behaviour = val; + } + + if let Some(val) = value.float_override { + wm.window_management_behaviour.float_override = val; } if let Some(val) = value.cross_monitor_move_behaviour { diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 92f222a69..4631ea7cc 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -39,6 +39,7 @@ use crate::core::Rect; use crate::core::Sizing; use crate::core::StackbarLabel; use crate::core::WindowContainerBehaviour; +use crate::core::WindowManagementBehaviour; use crate::border_manager; use crate::border_manager::STYLE; @@ -92,7 +93,7 @@ pub struct WindowManager { pub is_paused: bool, pub work_area_offset: Option, pub resize_delta: i32, - pub window_container_behaviour: WindowContainerBehaviour, + pub window_management_behaviour: WindowManagementBehaviour, pub cross_monitor_move_behaviour: MoveBehaviour, pub cross_boundary_behaviour: CrossBoundaryBehaviour, pub unmanaged_window_operation_behaviour: OperationBehaviour, @@ -112,6 +113,7 @@ pub struct State { pub is_paused: bool, pub resize_delta: i32, pub new_window_behaviour: WindowContainerBehaviour, + pub float_override: bool, pub cross_monitor_move_behaviour: MoveBehaviour, pub unmanaged_window_operation_behaviour: OperationBehaviour, pub work_area_offset: Option, @@ -215,7 +217,8 @@ impl From<&WindowManager> for State { is_paused: wm.is_paused, work_area_offset: wm.work_area_offset, resize_delta: wm.resize_delta, - new_window_behaviour: wm.window_container_behaviour, + new_window_behaviour: wm.window_management_behaviour.current_behaviour, + float_override: wm.window_management_behaviour.float_override, cross_monitor_move_behaviour: wm.cross_monitor_move_behaviour, focus_follows_mouse: wm.focus_follows_mouse, mouse_follows_focus: wm.mouse_follows_focus, @@ -275,7 +278,7 @@ impl WindowManager { is_paused: false, virtual_desktop_id: current_virtual_desktop(), work_area_offset: None, - window_container_behaviour: WindowContainerBehaviour::Create, + window_management_behaviour: WindowManagementBehaviour::default(), cross_monitor_move_behaviour: MoveBehaviour::Swap, cross_boundary_behaviour: CrossBoundaryBehaviour::Workspace, unmanaged_window_operation_behaviour: OperationBehaviour::Op, @@ -308,22 +311,44 @@ impl WindowManager { StaticConfig::reload(pathbuf, self) } - pub fn window_container_behaviour( + pub fn window_management_behaviour( &self, monitor_idx: usize, workspace_idx: usize, - ) -> WindowContainerBehaviour { + ) -> WindowManagementBehaviour { if let Some(monitor) = self.monitors().get(monitor_idx) { if let Some(workspace) = monitor.workspaces().get(workspace_idx) { - return if workspace.containers().is_empty() { + let current_behaviour = if let Some(behaviour) = workspace.window_container_behaviour() { + if workspace.containers().is_empty() && matches!(behaviour, WindowContainerBehaviour::Append) { + // You can't append to an empty workspace + WindowContainerBehaviour::Create + } else { + *behaviour + } + } else if workspace.containers().is_empty() && matches!(self.window_management_behaviour.current_behaviour, WindowContainerBehaviour::Append) { + // You can't append to an empty workspace WindowContainerBehaviour::Create } else { - self.window_container_behaviour + self.window_management_behaviour.current_behaviour + }; + + let float_override = if let Some(float_override) = workspace.float_override() { + *float_override + } else { + self.window_management_behaviour.float_override + }; + + return WindowManagementBehaviour { + current_behaviour, + float_override }; } } - WindowContainerBehaviour::Create + WindowManagementBehaviour { + current_behaviour: WindowContainerBehaviour::Create, + float_override: self.window_management_behaviour.float_override, + } } #[tracing::instrument(skip(self))] @@ -846,6 +871,8 @@ impl WindowManager { && self.focused_workspace()?.maximized_window().is_none() // and we don't have a monocle container && self.focused_workspace()?.monocle_container().is_none() + // and we don't have any floating windows that should show on top + && self.focused_workspace()?.floating_windows().is_empty() { if let Ok(window) = self.focused_window_mut() { if trigger_focus { diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index 39a846e91..a7392159d 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -30,6 +30,7 @@ use crate::static_config::WorkspaceConfig; use crate::window::Window; use crate::window::WindowDetails; use crate::windows_api::WindowsApi; +use crate::WindowContainerBehaviour; use crate::DEFAULT_CONTAINER_PADDING; use crate::DEFAULT_WORKSPACE_PADDING; use crate::INITIAL_CONFIGURATION_LOADED; @@ -83,6 +84,10 @@ pub struct Workspace { tile: bool, #[getset(get_copy = "pub", set = "pub")] apply_window_based_work_area_offset: bool, + #[getset(get = "pub", get_mut = "pub", set = "pub")] + window_container_behaviour: Option, + #[getset(get = "pub", get_mut = "pub", set = "pub")] + float_override: Option, } impl_ring_elements!(Workspace, Container); @@ -106,6 +111,8 @@ impl Default for Workspace { resize_dimensions: vec![], tile: true, apply_window_based_work_area_offset: true, + window_container_behaviour: None, + float_override: None, } } } @@ -162,6 +169,14 @@ impl Workspace { config.apply_window_based_work_area_offset.unwrap_or(true), ); + if config.window_container_behaviour.is_some() { + self.set_window_container_behaviour(config.window_container_behaviour); + } + + if config.float_override.is_some() { + self.set_float_override(config.float_override); + } + Ok(()) } @@ -217,10 +232,6 @@ impl Workspace { container.restore(); } - for container in self.containers_mut() { - container.restore(); - } - if let Some(container) = self.focused_container_mut() { container.focus_window(container.focused_window_idx()); } diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 0079f43fc..f805de6d2 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -1167,6 +1167,16 @@ enum SubCommand { WorkspaceName(WorkspaceName), /// Toggle the behaviour for new windows (stacking or dynamic tiling) ToggleWindowContainerBehaviour, + /// Enable or disable float override, which makes it so every new window opens in floating mode + ToggleFloatOverride, + /// Toggle the behaviour for new windows (stacking or dynamic tiling) for currently focused + /// workspace. If there was no behaviour set for the workspace previously it takes the opposite + /// of the global value. + ToggleWorkspaceWindowContainerBehaviour, + /// Enable or disable float override, which makes it so every new window opens in floating + /// mode, for the currently focused workspace. If there was no override value set for the + /// workspace previously it takes the opposite of the global value. + ToggleWorkspaceFloatOverride, /// Toggle window tiling on the focused workspace TogglePause, /// Toggle window tiling on the focused workspace @@ -2470,6 +2480,15 @@ Stop-Process -Name:komorebi -ErrorAction SilentlyContinue SubCommand::ToggleWindowContainerBehaviour => { send_message(&SocketMessage::ToggleWindowContainerBehaviour)?; } + SubCommand::ToggleFloatOverride => { + send_message(&SocketMessage::ToggleFloatOverride)?; + } + SubCommand::ToggleWorkspaceWindowContainerBehaviour => { + send_message(&SocketMessage::ToggleWorkspaceWindowContainerBehaviour)?; + } + SubCommand::ToggleWorkspaceFloatOverride => { + send_message(&SocketMessage::ToggleWorkspaceFloatOverride)?; + } SubCommand::WindowHidingBehaviour(arg) => { send_message(&SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour))?; }