From 4804b2f8274afbc5ea2d00b65de020510e2a003a Mon Sep 17 00:00:00 2001 From: Fernando Schauenburg Date: Fri, 10 Mar 2023 11:15:48 +0100 Subject: [PATCH] Add support to move windows between monitors --- Moonlander Window Manager.ahk | 192 ++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 90 deletions(-) diff --git a/Moonlander Window Manager.ahk b/Moonlander Window Manager.ahk index 95746ac..749618e 100644 --- a/Moonlander Window Manager.ahk +++ b/Moonlander Window Manager.ahk @@ -1,12 +1,23 @@ -log(text) +; Returns the rectangle (position & size) of a given monitor. +MonitorGetRect(N) { - FileAppend(text . "`n", "log.txt") + MonitorGetWorkArea(N, &left, &top, &right, &bottom) + return { + pos: { + x: left, + y: top, + }, + size: { + width: Abs(right - left), + height: Abs(bottom - top), + } + } } ; Return the number of the monitor that contains a window. -getMonitor(winId:="A") +WinGetMonitor(WinTitle:="A") { - WinGetPos(&x,, &width,, winId) + WinGetPos(&x,, &width,, WinTitle) midx := x + width // 2 Loop MonitorGetCount() { @@ -18,122 +29,123 @@ getMonitor(winId:="A") return MonitorGetPrimary() ; fallback to primary if none found } -; Returns the bound of the monitor that contains a window. -getMonitorBounds(winId:="A") +; Center a window without changing its size. +WinCenter(WinTitle:="A") { - monitor := getMonitor(winId) - MonitorGetWorkArea(monitor, &left, &top, &right, &bottom) + WinGetPos(,, &width, &height, WinTitle) + r := MonitorGetRect(WinGetMonitor(WinTitle)) + x := Round(r.pos.x + r.size.width/2 - width/2) + y := Round(r.pos.y + r.size.height/2 - height/2) + WinMove(x, y, width, height, WinTitle) +} + +; Return the rect (in percentages) of a window in its current monitor. +WinGetRelativeRect(WinTitle:="A") +{ + WinGetPos(&x, &y, &width, &height, WinTitle) + monitor := WinGetMonitor(WinTitle) + rect := MonitorGetRect(monitor) return { - x : left, - y : top, - width : Abs(right - left), - height : Abs(bottom - top), + pos: { + x: Abs((x - rect.pos.x) / rect.size.width), + y: Abs((y - rect.pos.y) / rect.size.height), + }, + size: { + width: width / rect.size.width, + height: height / rect.size.height, + } } } -clamp(n, limit:=1) +; Move and resize window to a rect (in percentage) in a given monitor. +WinSetRelativeRect(winRect, monitor?, WinTitle:="A") { - n := Abs(n) - if (n > limit) - return Mod(n, limit) - return n + if not IsSet(monitor) + monitor := WinGetMonitor(WinTitle) + + rect := MonitorGetRect(monitor) + + x := Round(rect.pos.x + winRect.pos.x * rect.size.width) + y := Round(rect.pos.y + winRect.pos.y * rect.size.height) + width := Round( winRect.size.width * rect.size.width) + height := Round( winRect.size.height * rect.size.height) + + WinMove(x, y, width, height, WinTitle) } -; Move and resize window to match spec (x, y, width, height). -positionWindow(spec, winId:="A") +; Move a window wihtout changing its size. +WinTranslate(dx, dy, WinTitle:="A") { - xScale := clamp(spec.x) - yScale := clamp(spec.y) - wScale := clamp(spec.w) - hScale := clamp(spec.h) - - monitor := getMonitorBounds(winId) - - x := Round(monitor.x + xScale * monitor.width) - y := Round(monitor.y + yScale * monitor.height) - width := Round( wScale * monitor.width) - height := Round( hScale * monitor.height) - - WinMove(x, y, width, height, winId) + WinGetPos(&x, &y, &width, &height, WinTitle) + WinMove(x + dx, y + dy, width, height, WinTitle) } -; Center a window without changing its size. -centerWindow(winId:="A") +; Move a window to a different monitor, ajusting the relative scaling. +WinSetMonitor(target, WinTitle:="A") { - WinGetPos(,, &width, &height, winId) - - monitor := getMonitorBounds(winId) - - x := Round(monitor.x + monitor.width/2 - width/2) - y := Round(monitor.y + monitor.height/2 - height/2) - - WinMove(x, y, width, height, winId) + WinSetRelativeRect(WinGetRelativeRect(WinTitle), target, WinTitle) } -; Move a window wihtou changing its size. -moveWindow(dx, dy, winId:="A") -{ - WinGetPos(&x, &y, &width, &height, winId) - WinMove(x + dx, y + dy, width, height, winId) -} - -SPECS := { +POS := { ; 2x3 matrix - upperLeft : {x: 0, y: 0, w: 1/3, h: 1/2 }, - upperMiddle : {x: 1/3, y: 0, w: 1/3, h: 1/2 }, - upperRight : {x: 2/3, y: 0, w: 1/3, h: 1/2 }, - lowerLeft : {x: 0, y: 1/2, w: 1/3, h: 1/2 }, - lowerMiddle : {x: 1/3, y: 1/2, w: 1/3, h: 1/2 }, - lowerRight : {x: 2/3, y: 1/2, w: 1/3, h: 1/2 }, + upperLeft : {pos: {x: 0, y: 0}, size: {width: 1/3, height: 1/2 }}, + upperMiddle : {pos: {x: 1/3, y: 0}, size: {width: 1/3, height: 1/2 }}, + upperRight : {pos: {x: 2/3, y: 0}, size: {width: 1/3, height: 1/2 }}, + lowerLeft : {pos: {x: 0, y: 1/2}, size: {width: 1/3, height: 1/2 }}, + lowerMiddle : {pos: {x: 1/3, y: 1/2}, size: {width: 1/3, height: 1/2 }}, + lowerRight : {pos: {x: 2/3, y: 1/2}, size: {width: 1/3, height: 1/2 }}, - ; full height thirds - thirdLeft : {x: 0, y: 0, w: 1/3, h: 1 }, - doubleLeft : {x: 0, y: 0, w: 2/3, h: 1 }, - thirdRight : {x: 2/3, y: 0, w: 1/3, h: 1 }, - doubleRight : {x: 1/3, y: 0, w: 2/3, h: 1 }, + ; Full Height Thirds + thirdLeft : {pos: {x: 0, y: 0}, size: {width: 1/3, height: 1 }}, + doubleLeft : {pos: {x: 0, y: 0}, size: {width: 2/3, height: 1 }}, + thirdRight : {pos: {x: 2/3, y: 0}, size: {width: 1/3, height: 1 }}, + doubleRight : {pos: {x: 1/3, y: 0}, size: {width: 2/3, height: 1 }}, - ; full height halves - halfLeft : {x: 0, y: 0, w: 1/2, h: 1 }, - halfRight : {x: 1/2, y: 0, w: 1/2, h: 1 }, + ; Full Height Halves + halfLeft : {pos: {x: 0, y: 0}, size: {width: 1/2, height: 1 }}, + halfRight : {pos: {x: 1/2, y: 0}, size: {width: 1/2, height: 1 }}, - ; center - mainFocus : {x: 0.18, y: 0, w: 0.64, h: 1 }, + ; Center + mainFocus : {pos: {x: 0.18, y: 0}, size: {width: 0.64, height: 1 }}, } ^!r::Reload +; Make sure NumLock is active so that Numpad mappinfs below will work. +SetNumLockState True + ; 2x3 matrix -!#Numpad7:: positionWindow(SPECS.upperLeft) -!#NumpadDiv:: positionWindow(SPECS.upperMiddle) -!#Numpad8:: positionWindow(SPECS.upperMiddle) -!#Numpad9:: positionWindow(SPECS.upperRight) -!#Numpad1:: positionWindow(SPECS.lowerLeft) -!#NumpadSub:: positionWindow(SPECS.lowerMiddle) -!#Numpad2:: positionWindow(SPECS.lowerMiddle) -!#Numpad3:: positionWindow(SPECS.lowerRight) +!#Numpad7:: WinSetRelativeRect(POS.upperLeft) +!#NumpadDiv:: WinSetRelativeRect(POS.upperMiddle) +!#Numpad8:: WinSetRelativeRect(POS.upperMiddle) +!#Numpad9:: WinSetRelativeRect(POS.upperRight) +!#Numpad1:: WinSetRelativeRect(POS.lowerLeft) +!#NumpadSub:: WinSetRelativeRect(POS.lowerMiddle) +!#Numpad2:: WinSetRelativeRect(POS.lowerMiddle) +!#Numpad3:: WinSetRelativeRect(POS.lowerRight) ; Full height thirds -!#Numpad4:: positionWindow(SPECS.thirdLeft) -!#NumpadMult:: positionWindow(SPECS.doubleLeft) -!#Numpad5:: positionWindow(SPECS.doubleRight) -!#Numpad6:: positionWindow(SPECS.thirdRight) +!#Numpad4:: WinSetRelativeRect(POS.thirdLeft) +!#NumpadMult:: WinSetRelativeRect(POS.doubleLeft) +!#Numpad5:: WinSetRelativeRect(POS.doubleRight) +!#Numpad6:: WinSetRelativeRect(POS.thirdRight) ; Full height halves -!^Numpad1::positionWindow(SPECS.halfLeft) -!^Numpad3::positionWindow(SPECS.halfRight) +!^Numpad1:: WinSetRelativeRect(POS.halfLeft) +!^Numpad3:: WinSetRelativeRect(POS.halfRight) ; Center and... -!#NumpadAdd:: positionWindow(SPECS.mainFocus) ; ... resize to default. -!^Numpad5:: centerWindow() ; ... keep size. +!#NumpadAdd:: WinSetRelativeRect(POS.mainFocus) ; ... resize to default. +!^Numpad5:: WinCenter() ; ... keep size. -; Move to other monitor -; !^Left:: TODO move to other monitor -; !^Right:: TODO move to other monitor +; Move to other monitor (FIXME this is a hack, but works on my current setup) +!^Left:: WinSetMonitor(2) +!^Right:: WinSetMonitor(1) ; Move without resize -step := 50 ; dx dy -!^Numpad4:: moveWindow(-step, 0) ; left (H) -!^Numpad2:: moveWindow( 0, step) ; down (J) -!^Numpad8:: moveWindow( 0, -step) ; up (K) -!^Numpad6:: moveWindow( step, 0) ; right (L) +step := 50 ; dx dy +!^Numpad4:: WinTranslate(-step, 0) ; left (H) +!^Numpad2:: WinTranslate( 0, step) ; down (J) +!^Numpad8:: WinTranslate( 0, -step) ; up (K) +!^Numpad6:: WinTranslate( step, 0) ; right (L)