mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-23 18:01:12 +01:00
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:0e8dc85fb1feat(wm): add new float override option30bdaf33d5feat(cli): add command for new option `ToggleFloatOverride`b7bedce1cafeat(wm): add window_container_behaviour and float_override to workspaces221e4ea545feat(cli): add commands for workspace new window behaviour and float_overrideb182cb5818fix(wm): show floating apps in front of stacked windows as well7c9cb11a9bfix(wm): Remove unecessary duplicated code
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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<bool>,
|
||||
/// Determine what happens when a new window is opened (default: Create)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub window_container_behaviour: Option<WindowContainerBehaviour>,
|
||||
/// 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<bool>,
|
||||
}
|
||||
|
||||
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<WindowContainerBehaviour>,
|
||||
/// 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<bool>,
|
||||
/// 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<MoveBehaviour>,
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<Rect>,
|
||||
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<Rect>,
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<WindowContainerBehaviour>,
|
||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
||||
float_override: Option<bool>,
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -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))?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user