mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-07-11 15:42:43 +02:00
feat(wm): add cross-monitor move behaviour config
This commit provides two new commands, to set and toggle the behaviour (swap, insert) when moving window containers across monitor boundaries. MoveBehaviour::Swap has been selected as the default as this seems to be the default on bspwm. resolve #145
This commit is contained in:
@@ -495,6 +495,8 @@ unmanage Unmanage a window that was forcibly m
|
|||||||
reload-configuration Reload ~/komorebi.ahk (if it exists)
|
reload-configuration Reload ~/komorebi.ahk (if it exists)
|
||||||
watch-configuration Enable or disable watching of ~/komorebi.ahk (if it exists)
|
watch-configuration Enable or disable watching of ~/komorebi.ahk (if it exists)
|
||||||
window-hiding-behaviour Set the window behaviour when switching workspaces / cycling stacks
|
window-hiding-behaviour Set the window behaviour when switching workspaces / cycling stacks
|
||||||
|
cross-monitor-move-behaviour Set the behaviour when moving windows across monitor boundaries
|
||||||
|
toggle-cross-monitor-move-behaviour Toggle the behaviour when moving windows across monitor boundaries
|
||||||
unmanaged-window-operation-behaviour Set the operation behaviour when the focused window is not managed
|
unmanaged-window-operation-behaviour Set the operation behaviour when the focused window is not managed
|
||||||
float-rule Add a rule to always float the specified application
|
float-rule Add a rule to always float the specified application
|
||||||
manage-rule Add a rule to always manage the specified application
|
manage-rule Add a rule to always manage the specified application
|
||||||
@@ -531,7 +533,9 @@ used [is available here](komorebi.sample.with.lib.ahk).
|
|||||||
- [x] Window stacks
|
- [x] Window stacks
|
||||||
- [x] Cycle through stacked windows
|
- [x] Cycle through stacked windows
|
||||||
- [x] Change focused window by direction
|
- [x] Change focused window by direction
|
||||||
|
- [x] Change focused window by direction across monitor boundary
|
||||||
- [x] Move focused window container in direction
|
- [x] Move focused window container in direction
|
||||||
|
- [x] Move focused window container in direction across monitor boundary
|
||||||
- [x] Move focused window container to monitor and follow
|
- [x] Move focused window container to monitor and follow
|
||||||
- [x] Move focused window container to workspace follow
|
- [x] Move focused window container to workspace follow
|
||||||
- [x] Send focused window container to monitor
|
- [x] Send focused window container to monitor
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ pub enum SocketMessage {
|
|||||||
ToggleMaximize,
|
ToggleMaximize,
|
||||||
ToggleWindowContainerBehaviour,
|
ToggleWindowContainerBehaviour,
|
||||||
WindowHidingBehaviour(HidingBehaviour),
|
WindowHidingBehaviour(HidingBehaviour),
|
||||||
|
ToggleCrossMonitorMoveBehaviour,
|
||||||
|
CrossMonitorMoveBehaviour(MoveBehaviour),
|
||||||
UnmanagedWindowOperationBehaviour(OperationBehaviour),
|
UnmanagedWindowOperationBehaviour(OperationBehaviour),
|
||||||
// Current Workspace Commands
|
// Current Workspace Commands
|
||||||
ManageFocusedWindow,
|
ManageFocusedWindow,
|
||||||
@@ -161,6 +163,13 @@ pub enum WindowContainerBehaviour {
|
|||||||
Append,
|
Append,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||||
|
#[strum(serialize_all = "snake_case")]
|
||||||
|
pub enum MoveBehaviour {
|
||||||
|
Swap,
|
||||||
|
Insert,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||||
#[strum(serialize_all = "snake_case")]
|
#[strum(serialize_all = "snake_case")]
|
||||||
pub enum HidingBehaviour {
|
pub enum HidingBehaviour {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ use komorebi_core::ApplicationIdentifier;
|
|||||||
use komorebi_core::Axis;
|
use komorebi_core::Axis;
|
||||||
use komorebi_core::FocusFollowsMouseImplementation;
|
use komorebi_core::FocusFollowsMouseImplementation;
|
||||||
use komorebi_core::Layout;
|
use komorebi_core::Layout;
|
||||||
|
use komorebi_core::MoveBehaviour;
|
||||||
use komorebi_core::OperationDirection;
|
use komorebi_core::OperationDirection;
|
||||||
use komorebi_core::Rect;
|
use komorebi_core::Rect;
|
||||||
use komorebi_core::Sizing;
|
use komorebi_core::Sizing;
|
||||||
@@ -706,6 +707,19 @@ impl WindowManager {
|
|||||||
let mut hiding_behaviour = HIDING_BEHAVIOUR.lock();
|
let mut hiding_behaviour = HIDING_BEHAVIOUR.lock();
|
||||||
*hiding_behaviour = behaviour;
|
*hiding_behaviour = behaviour;
|
||||||
}
|
}
|
||||||
|
SocketMessage::ToggleCrossMonitorMoveBehaviour => {
|
||||||
|
match self.cross_monitor_move_behaviour {
|
||||||
|
MoveBehaviour::Swap => {
|
||||||
|
self.cross_monitor_move_behaviour = MoveBehaviour::Insert;
|
||||||
|
}
|
||||||
|
MoveBehaviour::Insert => {
|
||||||
|
self.cross_monitor_move_behaviour = MoveBehaviour::Swap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SocketMessage::CrossMonitorMoveBehaviour(behaviour) => {
|
||||||
|
self.cross_monitor_move_behaviour = behaviour;
|
||||||
|
}
|
||||||
SocketMessage::UnmanagedWindowOperationBehaviour(behaviour) => {
|
SocketMessage::UnmanagedWindowOperationBehaviour(behaviour) => {
|
||||||
self.unmanaged_window_operation_behaviour = behaviour;
|
self.unmanaged_window_operation_behaviour = behaviour;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use komorebi_core::CycleDirection;
|
|||||||
use komorebi_core::DefaultLayout;
|
use komorebi_core::DefaultLayout;
|
||||||
use komorebi_core::FocusFollowsMouseImplementation;
|
use komorebi_core::FocusFollowsMouseImplementation;
|
||||||
use komorebi_core::Layout;
|
use komorebi_core::Layout;
|
||||||
|
use komorebi_core::MoveBehaviour;
|
||||||
use komorebi_core::OperationBehaviour;
|
use komorebi_core::OperationBehaviour;
|
||||||
use komorebi_core::OperationDirection;
|
use komorebi_core::OperationDirection;
|
||||||
use komorebi_core::Rect;
|
use komorebi_core::Rect;
|
||||||
@@ -58,6 +59,7 @@ pub struct WindowManager {
|
|||||||
pub work_area_offset: Option<Rect>,
|
pub work_area_offset: Option<Rect>,
|
||||||
pub resize_delta: i32,
|
pub resize_delta: i32,
|
||||||
pub window_container_behaviour: WindowContainerBehaviour,
|
pub window_container_behaviour: WindowContainerBehaviour,
|
||||||
|
pub cross_monitor_move_behaviour: MoveBehaviour,
|
||||||
pub unmanaged_window_operation_behaviour: OperationBehaviour,
|
pub unmanaged_window_operation_behaviour: OperationBehaviour,
|
||||||
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
|
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
|
||||||
pub mouse_follows_focus: bool,
|
pub mouse_follows_focus: bool,
|
||||||
@@ -74,6 +76,7 @@ pub struct State {
|
|||||||
pub invisible_borders: Rect,
|
pub invisible_borders: Rect,
|
||||||
pub resize_delta: i32,
|
pub resize_delta: i32,
|
||||||
pub new_window_behaviour: WindowContainerBehaviour,
|
pub new_window_behaviour: WindowContainerBehaviour,
|
||||||
|
pub cross_monitor_move_behaviour: MoveBehaviour,
|
||||||
pub work_area_offset: Option<Rect>,
|
pub work_area_offset: Option<Rect>,
|
||||||
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
|
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
|
||||||
pub mouse_follows_focus: bool,
|
pub mouse_follows_focus: bool,
|
||||||
@@ -101,6 +104,7 @@ impl From<&WindowManager> for State {
|
|||||||
work_area_offset: wm.work_area_offset,
|
work_area_offset: wm.work_area_offset,
|
||||||
resize_delta: wm.resize_delta,
|
resize_delta: wm.resize_delta,
|
||||||
new_window_behaviour: wm.window_container_behaviour,
|
new_window_behaviour: wm.window_container_behaviour,
|
||||||
|
cross_monitor_move_behaviour: wm.cross_monitor_move_behaviour,
|
||||||
focus_follows_mouse: wm.focus_follows_mouse.clone(),
|
focus_follows_mouse: wm.focus_follows_mouse.clone(),
|
||||||
mouse_follows_focus: wm.mouse_follows_focus,
|
mouse_follows_focus: wm.mouse_follows_focus,
|
||||||
has_pending_raise_op: wm.has_pending_raise_op,
|
has_pending_raise_op: wm.has_pending_raise_op,
|
||||||
@@ -172,6 +176,7 @@ impl WindowManager {
|
|||||||
virtual_desktop_id: current_virtual_desktop(),
|
virtual_desktop_id: current_virtual_desktop(),
|
||||||
work_area_offset: None,
|
work_area_offset: None,
|
||||||
window_container_behaviour: WindowContainerBehaviour::Create,
|
window_container_behaviour: WindowContainerBehaviour::Create,
|
||||||
|
cross_monitor_move_behaviour: MoveBehaviour::Swap,
|
||||||
unmanaged_window_operation_behaviour: OperationBehaviour::Op,
|
unmanaged_window_operation_behaviour: OperationBehaviour::Op,
|
||||||
resize_delta: 50,
|
resize_delta: 50,
|
||||||
focus_follows_mouse: None,
|
focus_follows_mouse: None,
|
||||||
@@ -1013,41 +1018,47 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
// if our MoveBehaviour is Swap, let's try to send back the window container
|
||||||
let target_workspace = self.focused_workspace_mut()?;
|
// whose position which just took over
|
||||||
|
if matches!(self.cross_monitor_move_behaviour, MoveBehaviour::Swap) {
|
||||||
|
{
|
||||||
|
let target_workspace = self.focused_workspace_mut()?;
|
||||||
|
|
||||||
// if the target workspace doesn't have more than one container, this means it
|
// if the target workspace doesn't have more than one container, this means it
|
||||||
// was previously empty, by only doing the second part of the swap when there is
|
// was previously empty, by only doing the second part of the swap when there is
|
||||||
// more than one container, we can fall back to a "move" if there is nothing to
|
// more than one container, we can fall back to a "move" if there is nothing to
|
||||||
// swap with on the target monitor
|
// swap with on the target monitor
|
||||||
if target_workspace.containers().len() > 1 {
|
if target_workspace.containers().len() > 1 {
|
||||||
// remove the container from the target monitor workspace
|
// remove the container from the target monitor workspace
|
||||||
let target_container = target_workspace
|
let target_container = target_workspace
|
||||||
// this is now focused_container_idx + 1 because we have inserted our origin container
|
// this is now focused_container_idx + 1 because we have inserted our origin container
|
||||||
.remove_container_by_idx(target_workspace.focused_container_idx() + 1)
|
.remove_container_by_idx(
|
||||||
.ok_or_else(|| {
|
target_workspace.focused_container_idx() + 1,
|
||||||
anyhow!("could not remove container at given target index")
|
)
|
||||||
})?;
|
.ok_or_else(|| {
|
||||||
|
anyhow!("could not remove container at given target index")
|
||||||
|
})?;
|
||||||
|
|
||||||
let origin_workspace =
|
let origin_workspace =
|
||||||
self.focused_workspace_for_monitor_idx_mut(origin_monitor_idx)?;
|
self.focused_workspace_for_monitor_idx_mut(origin_monitor_idx)?;
|
||||||
|
|
||||||
// insert the container from the target monitor workspace into the origin monitor workspace
|
// insert the container from the target monitor workspace into the origin monitor workspace
|
||||||
// at the same position from which our origin container was removed
|
// at the same position from which our origin container was removed
|
||||||
origin_workspace
|
origin_workspace
|
||||||
.insert_container_at_idx(origin_container_idx, target_container);
|
.insert_container_at_idx(origin_container_idx, target_container);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure to update the origin monitor workspace layout because it is no
|
|
||||||
// longer focused so it won't get updated at the end of this fn
|
|
||||||
let offset = self.work_area_offset;
|
|
||||||
let invisible_borders = self.invisible_borders;
|
|
||||||
|
|
||||||
self.monitors_mut()
|
|
||||||
.get_mut(origin_monitor_idx)
|
|
||||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
|
||||||
.update_focused_workspace(offset, &invisible_borders)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure to update the origin monitor workspace layout because it is no
|
||||||
|
// longer focused so it won't get updated at the end of this fn
|
||||||
|
let offset = self.work_area_offset;
|
||||||
|
let invisible_borders = self.invisible_borders;
|
||||||
|
|
||||||
|
self.monitors_mut()
|
||||||
|
.get_mut(origin_monitor_idx)
|
||||||
|
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||||
|
.update_focused_workspace(offset, &invisible_borders)?;
|
||||||
}
|
}
|
||||||
Some(new_idx) => {
|
Some(new_idx) => {
|
||||||
let workspace = self.focused_workspace_mut()?;
|
let workspace = self.focused_workspace_mut()?;
|
||||||
|
|||||||
@@ -256,6 +256,14 @@ WindowHidingBehaviour(hiding_behaviour) {
|
|||||||
Run, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide
|
Run, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CrossMonitorMoveBehaviour(move_behaviour) {
|
||||||
|
Run, komorebic.exe cross-monitor-move-behaviour %move_behaviour%, , Hide
|
||||||
|
}
|
||||||
|
|
||||||
|
ToggleCrossMonitorMoveBehaviour() {
|
||||||
|
Run, komorebic.exe toggle-cross-monitor-move-behaviour, , Hide
|
||||||
|
}
|
||||||
|
|
||||||
UnmanagedWindowOperationBehaviour(operation_behaviour) {
|
UnmanagedWindowOperationBehaviour(operation_behaviour) {
|
||||||
Run, komorebic.exe unmanaged-window-operation-behaviour %operation_behaviour%, , Hide
|
Run, komorebic.exe unmanaged-window-operation-behaviour %operation_behaviour%, , Hide
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ use komorebi_core::CycleDirection;
|
|||||||
use komorebi_core::DefaultLayout;
|
use komorebi_core::DefaultLayout;
|
||||||
use komorebi_core::FocusFollowsMouseImplementation;
|
use komorebi_core::FocusFollowsMouseImplementation;
|
||||||
use komorebi_core::HidingBehaviour;
|
use komorebi_core::HidingBehaviour;
|
||||||
|
use komorebi_core::MoveBehaviour;
|
||||||
use komorebi_core::OperationBehaviour;
|
use komorebi_core::OperationBehaviour;
|
||||||
use komorebi_core::OperationDirection;
|
use komorebi_core::OperationDirection;
|
||||||
use komorebi_core::Rect;
|
use komorebi_core::Rect;
|
||||||
@@ -118,6 +119,7 @@ gen_enum_subcommand_args! {
|
|||||||
MouseFollowsFocus: BooleanState,
|
MouseFollowsFocus: BooleanState,
|
||||||
Query: StateQuery,
|
Query: StateQuery,
|
||||||
WindowHidingBehaviour: HidingBehaviour,
|
WindowHidingBehaviour: HidingBehaviour,
|
||||||
|
CrossMonitorMoveBehaviour: MoveBehaviour,
|
||||||
UnmanagedWindowOperationBehaviour: OperationBehaviour,
|
UnmanagedWindowOperationBehaviour: OperationBehaviour,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,6 +629,11 @@ enum SubCommand {
|
|||||||
/// Set the window behaviour when switching workspaces / cycling stacks
|
/// Set the window behaviour when switching workspaces / cycling stacks
|
||||||
#[clap(arg_required_else_help = true)]
|
#[clap(arg_required_else_help = true)]
|
||||||
WindowHidingBehaviour(WindowHidingBehaviour),
|
WindowHidingBehaviour(WindowHidingBehaviour),
|
||||||
|
/// Set the behaviour when moving windows across monitor boundaries
|
||||||
|
#[clap(arg_required_else_help = true)]
|
||||||
|
CrossMonitorMoveBehaviour(CrossMonitorMoveBehaviour),
|
||||||
|
/// Toggle the behaviour when moving windows across monitor boundaries
|
||||||
|
ToggleCrossMonitorMoveBehaviour,
|
||||||
/// Set the operation behaviour when the focused window is not managed
|
/// Set the operation behaviour when the focused window is not managed
|
||||||
#[clap(arg_required_else_help = true)]
|
#[clap(arg_required_else_help = true)]
|
||||||
UnmanagedWindowOperationBehaviour(UnmanagedWindowOperationBehaviour),
|
UnmanagedWindowOperationBehaviour(UnmanagedWindowOperationBehaviour),
|
||||||
@@ -1167,6 +1174,14 @@ fn main() -> Result<()> {
|
|||||||
SubCommand::WindowHidingBehaviour(arg) => {
|
SubCommand::WindowHidingBehaviour(arg) => {
|
||||||
send_message(&*SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour).as_bytes()?)?;
|
send_message(&*SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour).as_bytes()?)?;
|
||||||
}
|
}
|
||||||
|
SubCommand::CrossMonitorMoveBehaviour(arg) => {
|
||||||
|
send_message(
|
||||||
|
&*SocketMessage::CrossMonitorMoveBehaviour(arg.move_behaviour).as_bytes()?,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
SubCommand::ToggleCrossMonitorMoveBehaviour => {
|
||||||
|
send_message(&*SocketMessage::ToggleCrossMonitorMoveBehaviour.as_bytes()?)?;
|
||||||
|
}
|
||||||
SubCommand::UnmanagedWindowOperationBehaviour(arg) => {
|
SubCommand::UnmanagedWindowOperationBehaviour(arg) => {
|
||||||
send_message(
|
send_message(
|
||||||
&*SocketMessage::UnmanagedWindowOperationBehaviour(arg.operation_behaviour)
|
&*SocketMessage::UnmanagedWindowOperationBehaviour(arg.operation_behaviour)
|
||||||
|
|||||||
Reference in New Issue
Block a user