diff --git a/komorebi/src/core/mod.rs b/komorebi/src/core/mod.rs index c08cb4b70..441277fbd 100644 --- a/komorebi/src/core/mod.rs +++ b/komorebi/src/core/mod.rs @@ -343,10 +343,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..1afe965ba 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -1346,12 +1346,12 @@ 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; } } } 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..a8b2ee606 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; @@ -235,6 +236,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 +525,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 +1037,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 +1215,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..b5a84564d 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,31 @@ 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 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 + }; + + return WindowManagementBehaviour { + current_behaviour, + float_override: self.window_management_behaviour.float_override, }; } } - WindowContainerBehaviour::Create + WindowManagementBehaviour { + current_behaviour: WindowContainerBehaviour::Create, + float_override: self.window_management_behaviour.float_override, + } } #[tracing::instrument(skip(self))]