mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-03-28 12:11:59 +01: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)
|
||||
watch-configuration Enable or disable watching of ~/komorebi.ahk (if it exists)
|
||||
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
|
||||
float-rule Add a rule to always float 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] Cycle through stacked windows
|
||||
- [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 across monitor boundary
|
||||
- [x] Move focused window container to monitor and follow
|
||||
- [x] Move focused window container to workspace follow
|
||||
- [x] Send focused window container to monitor
|
||||
|
||||
@@ -57,6 +57,8 @@ pub enum SocketMessage {
|
||||
ToggleMaximize,
|
||||
ToggleWindowContainerBehaviour,
|
||||
WindowHidingBehaviour(HidingBehaviour),
|
||||
ToggleCrossMonitorMoveBehaviour,
|
||||
CrossMonitorMoveBehaviour(MoveBehaviour),
|
||||
UnmanagedWindowOperationBehaviour(OperationBehaviour),
|
||||
// Current Workspace Commands
|
||||
ManageFocusedWindow,
|
||||
@@ -161,6 +163,13 @@ pub enum WindowContainerBehaviour {
|
||||
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)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum HidingBehaviour {
|
||||
|
||||
@@ -20,6 +20,7 @@ use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::Axis;
|
||||
use komorebi_core::FocusFollowsMouseImplementation;
|
||||
use komorebi_core::Layout;
|
||||
use komorebi_core::MoveBehaviour;
|
||||
use komorebi_core::OperationDirection;
|
||||
use komorebi_core::Rect;
|
||||
use komorebi_core::Sizing;
|
||||
@@ -706,6 +707,19 @@ impl WindowManager {
|
||||
let mut hiding_behaviour = HIDING_BEHAVIOUR.lock();
|
||||
*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) => {
|
||||
self.unmanaged_window_operation_behaviour = behaviour;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ use komorebi_core::CycleDirection;
|
||||
use komorebi_core::DefaultLayout;
|
||||
use komorebi_core::FocusFollowsMouseImplementation;
|
||||
use komorebi_core::Layout;
|
||||
use komorebi_core::MoveBehaviour;
|
||||
use komorebi_core::OperationBehaviour;
|
||||
use komorebi_core::OperationDirection;
|
||||
use komorebi_core::Rect;
|
||||
@@ -58,6 +59,7 @@ pub struct WindowManager {
|
||||
pub work_area_offset: Option<Rect>,
|
||||
pub resize_delta: i32,
|
||||
pub window_container_behaviour: WindowContainerBehaviour,
|
||||
pub cross_monitor_move_behaviour: MoveBehaviour,
|
||||
pub unmanaged_window_operation_behaviour: OperationBehaviour,
|
||||
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
|
||||
pub mouse_follows_focus: bool,
|
||||
@@ -74,6 +76,7 @@ pub struct State {
|
||||
pub invisible_borders: Rect,
|
||||
pub resize_delta: i32,
|
||||
pub new_window_behaviour: WindowContainerBehaviour,
|
||||
pub cross_monitor_move_behaviour: MoveBehaviour,
|
||||
pub work_area_offset: Option<Rect>,
|
||||
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
|
||||
pub mouse_follows_focus: bool,
|
||||
@@ -101,6 +104,7 @@ impl From<&WindowManager> for State {
|
||||
work_area_offset: wm.work_area_offset,
|
||||
resize_delta: wm.resize_delta,
|
||||
new_window_behaviour: wm.window_container_behaviour,
|
||||
cross_monitor_move_behaviour: wm.cross_monitor_move_behaviour,
|
||||
focus_follows_mouse: wm.focus_follows_mouse.clone(),
|
||||
mouse_follows_focus: wm.mouse_follows_focus,
|
||||
has_pending_raise_op: wm.has_pending_raise_op,
|
||||
@@ -172,6 +176,7 @@ impl WindowManager {
|
||||
virtual_desktop_id: current_virtual_desktop(),
|
||||
work_area_offset: None,
|
||||
window_container_behaviour: WindowContainerBehaviour::Create,
|
||||
cross_monitor_move_behaviour: MoveBehaviour::Swap,
|
||||
unmanaged_window_operation_behaviour: OperationBehaviour::Op,
|
||||
resize_delta: 50,
|
||||
focus_follows_mouse: None,
|
||||
@@ -1013,41 +1018,47 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let target_workspace = self.focused_workspace_mut()?;
|
||||
// if our MoveBehaviour is Swap, let's try to send back the window container
|
||||
// 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
|
||||
// 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
|
||||
// swap with on the target monitor
|
||||
if target_workspace.containers().len() > 1 {
|
||||
// remove the container from the target monitor workspace
|
||||
let target_container = target_workspace
|
||||
// this is now focused_container_idx + 1 because we have inserted our origin container
|
||||
.remove_container_by_idx(target_workspace.focused_container_idx() + 1)
|
||||
.ok_or_else(|| {
|
||||
anyhow!("could not remove container at given target index")
|
||||
})?;
|
||||
// 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
|
||||
// more than one container, we can fall back to a "move" if there is nothing to
|
||||
// swap with on the target monitor
|
||||
if target_workspace.containers().len() > 1 {
|
||||
// remove the container from the target monitor workspace
|
||||
let target_container = target_workspace
|
||||
// this is now focused_container_idx + 1 because we have inserted our origin container
|
||||
.remove_container_by_idx(
|
||||
target_workspace.focused_container_idx() + 1,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
anyhow!("could not remove container at given target index")
|
||||
})?;
|
||||
|
||||
let origin_workspace =
|
||||
self.focused_workspace_for_monitor_idx_mut(origin_monitor_idx)?;
|
||||
let origin_workspace =
|
||||
self.focused_workspace_for_monitor_idx_mut(origin_monitor_idx)?;
|
||||
|
||||
// insert the container from the target monitor workspace into the origin monitor workspace
|
||||
// at the same position from which our origin container was removed
|
||||
origin_workspace
|
||||
.insert_container_at_idx(origin_container_idx, target_container);
|
||||
// insert the container from the target monitor workspace into the origin monitor workspace
|
||||
// at the same position from which our origin container was removed
|
||||
origin_workspace
|
||||
.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) => {
|
||||
let workspace = self.focused_workspace_mut()?;
|
||||
|
||||
@@ -256,6 +256,14 @@ WindowHidingBehaviour(hiding_behaviour) {
|
||||
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) {
|
||||
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::FocusFollowsMouseImplementation;
|
||||
use komorebi_core::HidingBehaviour;
|
||||
use komorebi_core::MoveBehaviour;
|
||||
use komorebi_core::OperationBehaviour;
|
||||
use komorebi_core::OperationDirection;
|
||||
use komorebi_core::Rect;
|
||||
@@ -118,6 +119,7 @@ gen_enum_subcommand_args! {
|
||||
MouseFollowsFocus: BooleanState,
|
||||
Query: StateQuery,
|
||||
WindowHidingBehaviour: HidingBehaviour,
|
||||
CrossMonitorMoveBehaviour: MoveBehaviour,
|
||||
UnmanagedWindowOperationBehaviour: OperationBehaviour,
|
||||
}
|
||||
|
||||
@@ -627,6 +629,11 @@ enum SubCommand {
|
||||
/// Set the window behaviour when switching workspaces / cycling stacks
|
||||
#[clap(arg_required_else_help = true)]
|
||||
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
|
||||
#[clap(arg_required_else_help = true)]
|
||||
UnmanagedWindowOperationBehaviour(UnmanagedWindowOperationBehaviour),
|
||||
@@ -1167,6 +1174,14 @@ fn main() -> Result<()> {
|
||||
SubCommand::WindowHidingBehaviour(arg) => {
|
||||
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) => {
|
||||
send_message(
|
||||
&*SocketMessage::UnmanagedWindowOperationBehaviour(arg.operation_behaviour)
|
||||
|
||||
Reference in New Issue
Block a user