|
/** |
|
* Advanced Window Snap (Extended |
|
* Snaps the Active Window to one of nine different window positions. |
|
* |
|
* @Editing author Jarrett Urech |
|
* @Original author Andrew Moore <[email protected]> |
|
* @version 2.1 |
|
* |
|
**/ |
|
|
|
/** |
|
* SnapActiveWindow resizes and moves (snaps) the active window to a given position. |
|
* @param {string} winPlaceVertical The vertical placement of the active window. |
|
* Expecting "bottom" or "middle", otherwise assumes |
|
* "top" placement. |
|
* @param {string} winPlaceHorizontal The horizontal placement of the active window. |
|
* Expecting "left" or "right", otherwise assumes |
|
* window should span the "full" width of the monitor. |
|
* @param {string} winSizeHeight The height of the active window in relation to |
|
* the active monitor's height. Expecting "half" size, |
|
* otherwise will resize window to a "third". |
|
*/ |
|
|
|
#NoEnv |
|
#NoTrayIcon |
|
#SingleInstance force |
|
#MaxThreads 1 |
|
|
|
|
|
SnapActiveWindow(winPlaceVertical, winPlaceHorizontal, winSizeHeight, activeMon := 0) { |
|
WinGet activeWin, ID, A |
|
SysGet, MonitorCount, MonitorCount |
|
|
|
if (!activeMon) { |
|
activeMon := GetMonitorIndexFromWindow(activeWin) |
|
} else if (activeMon > MonitorCount) { |
|
activeMon := 1 |
|
} |
|
|
|
SysGet, MonitorWorkArea, MonitorWorkArea, %activeMon% |
|
|
|
if (winSizeHeight == "half") { |
|
height := (MonitorWorkAreaBottom - MonitorWorkAreaTop)/2 |
|
} else if (winSizeHeight == "third") { |
|
height := (MonitorWorkAreaBottom - MonitorWorkAreaTop)/3 |
|
} else { |
|
height := (MonitorWorkAreaBottom - MonitorWorkAreaTop) |
|
} |
|
|
|
if (winPlaceHorizontal == "left") { |
|
posX := MonitorWorkAreaLeft |
|
width := (MonitorWorkAreaRight - MonitorWorkAreaLeft)/2 |
|
} else if (winPlaceHorizontal == "right") { |
|
posX := MonitorWorkAreaLeft + (MonitorWorkAreaRight - MonitorWorkAreaLeft)/2 |
|
width := (MonitorWorkAreaRight - MonitorWorkAreaLeft)/2 |
|
} else { |
|
posX := MonitorWorkAreaLeft |
|
width := MonitorWorkAreaRight - MonitorWorkAreaLeft |
|
} |
|
|
|
if (winPlaceVertical == "bottom") { |
|
posY := MonitorWorkAreaBottom - height |
|
} else if (winPlaceVertical == "middle") { |
|
posY := MonitorWorkAreaTop + height |
|
} else { |
|
posY := MonitorWorkAreaTop |
|
} |
|
|
|
; Rounding |
|
posX := floor(posX) |
|
posY := floor(posY) |
|
width := floor(width) |
|
height := floor(height) |
|
|
|
; Borders (Windows 10) |
|
SysGet, BorderX, 32 |
|
SysGet, BorderY, 33 |
|
if (BorderX) { |
|
posX := posX - BorderX |
|
width := width + (BorderX * 2) |
|
} |
|
if (BorderY) { |
|
height := height + BorderY |
|
} |
|
|
|
; If window is already there move to same spot on next monitor |
|
WinGetPos, curPosX, curPosY, curWidth, curHeight, A |
|
if ((posX = curPosX) && (posY = curPosY) && (width = curWidth) && (height = curHeight)) { |
|
activeMon := activeMon + 1 |
|
SnapActiveWindow(winPlaceVertical, winPlaceHorizontal, winSizeHeight, activeMon) |
|
} else { |
|
WinMove,A,,%posX%,%posY%,%width%,%height% |
|
} |
|
} |
|
|
|
/** |
|
* GetMonitorIndexFromWindow retrieves the HWND (unique ID) of a given window. |
|
* @param {Uint} windowHandle |
|
* @author shinywong |
|
* @link http://www.autohotkey.com/board/topic/69464-how-to-determine-a-window-is-in-which-monitor/?p=440355 |
|
*/ |
|
GetMonitorIndexFromWindow(windowHandle) { |
|
; Starts with 1. |
|
monitorIndex := 1 |
|
|
|
VarSetCapacity(monitorInfo, 40) |
|
NumPut(40, monitorInfo) |
|
|
|
if (monitorHandle := DllCall("MonitorFromWindow", "uint", windowHandle, "uint", 0x2)) |
|
&& DllCall("GetMonitorInfo", "uint", monitorHandle, "uint", &monitorInfo) { |
|
monitorLeft := NumGet(monitorInfo, 4, "Int") |
|
monitorTop := NumGet(monitorInfo, 8, "Int") |
|
monitorRight := NumGet(monitorInfo, 12, "Int") |
|
monitorBottom := NumGet(monitorInfo, 16, "Int") |
|
workLeft := NumGet(monitorInfo, 20, "Int") |
|
workTop := NumGet(monitorInfo, 24, "Int") |
|
workRight := NumGet(monitorInfo, 28, "Int") |
|
workBottom := NumGet(monitorInfo, 32, "Int") |
|
isPrimary := NumGet(monitorInfo, 36, "Int") & 1 |
|
|
|
SysGet, monitorCount, MonitorCount |
|
|
|
Loop, %monitorCount% { |
|
SysGet, tempMon, Monitor, %A_Index% |
|
|
|
; Compare location to determine the monitor index. |
|
if ((monitorLeft = tempMonLeft) and (monitorTop = tempMonTop) |
|
and (monitorRight = tempMonRight) and (monitorBottom = tempMonBottom)) { |
|
monitorIndex := A_Index |
|
break |
|
} |
|
} |
|
} |
|
|
|
return %monitorIndex% |
|
} |
|
|
|
dynamicPickup(initial:=0) { |
|
; Pickup the initial keypress |
|
pressed%initial% := true |
|
|
|
; Disable Additional Presses |
|
loop 9 |
|
Hotkey, ^#Numpad%A_Index%, dynamicPickup, Off |
|
|
|
; Pickup Areas |
|
while (GetKeyState("Control") || GetKeyState("LWin") || GetKeyState("RWin")) { |
|
if (GetKeyState("Numpad1")) |
|
pressed1 := true |
|
if (GetKeyState("Numpad2")) |
|
pressed2 := true |
|
if (GetKeyState("Numpad3")) |
|
pressed3 := true |
|
if (GetKeyState("Numpad4")) |
|
pressed4 := true |
|
if (GetKeyState("Numpad5")) |
|
pressed5 := true |
|
if (GetKeyState("Numpad6")) |
|
pressed6 := true |
|
if (GetKeyState("Numpad7")) |
|
pressed7 := true |
|
if (GetKeyState("Numpad8")) |
|
pressed8 := true |
|
if (GetKeyState("Numpad9")) |
|
pressed9 := true |
|
} |
|
|
|
; Calc Window Height |
|
height:=0 |
|
if ((pressed1 || pressed2 || pressed3) && (pressed7 || pressed8 || pressed9)) { |
|
height := 3 |
|
} else { |
|
if (pressed1 || pressed2 || pressed3) |
|
height := height + 1 |
|
if (pressed4 || pressed5 || pressed6) |
|
height := height + 1 |
|
if (pressed7 || pressed8 || pressed9) |
|
height := height + 1 |
|
} |
|
|
|
|
|
; Calc Window Width |
|
width:=0 |
|
if ((pressed1 || pressed4 || pressed7) && (pressed3 || pressed6 || pressed9)) { |
|
width := 3 |
|
} else { |
|
if (pressed1 || pressed4 || pressed7) |
|
width := width + 1 |
|
if (pressed2 || pressed5 || pressed8) |
|
width := width + 1 |
|
if (pressed3 || pressed6 || pressed9) |
|
width := width + 1 |
|
} |
|
|
|
; Calc Anchor Point (matches numpad for zones) (order matters here) |
|
anchor := 0 |
|
if (pressed3 && (height = 1)) |
|
anchor := 9 |
|
if (pressed2 && (height = 1)) |
|
anchor := 8 |
|
if (pressed1 && (height = 1)) |
|
anchor := 7 |
|
if (pressed6 && (height < 3)) |
|
anchor := 6 |
|
if ((pressed5 || (pressed2 && pressed6)) && (height < 3)) |
|
anchor := 5 |
|
if ((pressed4 || (pressed1 && (pressed5 || pressed6))) && (height < 3)) |
|
anchor := 4 |
|
if (pressed9) |
|
anchor := 3 |
|
if (pressed8 || (pressed9 && (pressed5 || pressed2))) |
|
anchor := 2 |
|
if (pressed7 || ((pressed4 || pressed1) && (pressed8 || pressed9))) |
|
anchor := 1 |
|
|
|
; Set This window up! |
|
SnapActiveWindowAdvanced(anchor, width, height) |
|
|
|
; Enable Hotkeys |
|
loop 9 |
|
Hotkey, ^#Numpad%A_Index%, dynamicPickup, On |
|
} |
|
|
|
SnapActiveWindowAdvanced(anchor, widthUnit, heightUnit, snapGrid := 3,activeMon := 0) { |
|
; SnapGrid units are the width/height of the active monitor evenly split into the snapgrid number |
|
; Width and Height is multiples of snapGrid units |
|
; The anchor points are arranged in order from left to right, top to bottom |
|
|
|
WinGet activeWin, ID, A |
|
SysGet, MonitorCount, MonitorCount |
|
|
|
if (!activeMon) { |
|
activeMon := GetMonitorIndexFromWindow(activeWin) |
|
} else if (activeMon > MonitorCount) { |
|
activeMon := 1 |
|
} |
|
|
|
SysGet, MonitorWorkArea, MonitorWorkArea, %activeMon% |
|
|
|
; Snap Units |
|
unitX := (MonitorWorkAreaRight - MonitorWorkAreaLeft) / snapGrid |
|
unitY := (MonitorWorkAreaBottom - MonitorWorkAreaTop) / snapGrid |
|
|
|
; Resolve Anchor |
|
posX := MonitorWorkAreaLeft + (unitX * Mod(anchor - 1, snapGrid)) |
|
posY := MonitorWorkAreaTop + (unitY * floor((anchor - 1) / snapGrid) - 1) |
|
|
|
; Calculate size as a percentage of the screen |
|
width := (MonitorWorkAreaLeft + (unitX * (Mod(anchor - 1, snapGrid) + widthUnit))) - posX |
|
if ((posX + width) > (MonitorWorkAreaRight - 50)) |
|
width := MonitorWorkAreaRight - (posX - 1) |
|
|
|
height := (MonitorWorkAreaTop + (unitY * (floor((anchor - 1) / snapGrid) + heightUnit) - 1)) - posY |
|
if ((posY + height) > (MonitorWorkAreaBottom - 50)) |
|
height := MonitorWorkAreaBottom - (posY - 1) |
|
|
|
; Clover Special Case [START] (http://en.ejie.me) |
|
; If the internal windows explorer window is selected then switch to clover wrapper |
|
IfWinActive, ahk_class CabinetWClass ahk_exe explorer.exe |
|
{ |
|
WinGetPos, expX, expY,,, A |
|
WinGet, id, list,,, Program Manager |
|
Loop, %id% { |
|
this_id := id%A_Index% |
|
WinGetPos, clovX, clovY,,, ahk_id %this_id% |
|
if (((expX - 8) = clovX) && ((expY - 18) = clovY)) { |
|
WinActivate, ahk_id %this_id% |
|
break |
|
} |
|
} |
|
} |
|
; Adjust limits to accomodate the wrapper dimensions |
|
IfWinActive, ahk_class Clover_WidgetWin_0 ahk_exe clover.exe |
|
{ |
|
posX := posX + 2 |
|
width := width - 4 |
|
height := height - 3 |
|
} |
|
; Clover Special Case [END] |
|
|
|
; Borders (Windows 10) |
|
SysGet, BorderX, 32 |
|
SysGet, BorderY, 33 |
|
if (BorderX) { |
|
posX := (posX + 1) - BorderX |
|
width := (width - 2) + (BorderX * 2) |
|
} |
|
if (BorderY) { |
|
height := height + BorderY |
|
} |
|
|
|
; Rounding |
|
posX := floor(posX) |
|
posY := floor(posY) |
|
width := floor(width) |
|
height := floor(height) |
|
|
|
; If window is already there move to same spot on next monitor |
|
WinGetPos, curPosX, curPosY, curWidth, curHeight, A |
|
if ((posX = curPosX) && (posY = curPosY) && (width = curWidth) && (height = curHeight)) { |
|
activeMon := activeMon + 1 |
|
SnapActiveWindowAdvanced(anchor, widthUnit, heightUnit, snapGrid, activeMon) |
|
} else { |
|
WinRestore, A |
|
WinMove,A,,%posX%,%posY%,%width%,%height% |
|
} |
|
} |
|
|
|
; Dynamic Snapping |
|
loop 9 |
|
Hotkey, ^#Numpad%A_Index%, dynamicPickup, On |
|
return |
|
|
|
; Dynamic Hook Function |
|
dynamicPickup: |
|
dynamicPickup(SubStr(A_ThisHotkey,0)) |
|
return |
|
|
|
; Directional Arrow Hotkeys |
|
#!Up::SnapActiveWindow("top","full","half") |
|
#!Down::SnapActiveWindow("bottom","full","half") |
|
|
|
; Numberpad Hotkeys (Landscape) |
|
#!Numpad7::SnapActiveWindow("top","left","half") |
|
#!Numpad8::SnapActiveWindow("top","full","half") |
|
#!Numpad9::SnapActiveWindow("top","right","half") |
|
#!Numpad1::SnapActiveWindow("bottom","left","half") |
|
#!Numpad2::SnapActiveWindow("bottom","full","half") |
|
#!Numpad3::SnapActiveWindow("bottom","right","half") |
|
#!Numpad4::SnapActiveWindow("top","left","full") |
|
#!Numpad6::SnapActiveWindow("top","right","full") |
|
|
|
; Numberpad Hotkeys (Portrait) |
|
^#!Numpad8::SnapActiveWindow("top","full","third") |
|
^#!Numpad5::SnapActiveWindow("middle","full","third") |
|
^#!Numpad2::SnapActiveWindow("bottom","full","third") |
|
^#!Numpad7::SnapActiveWindow("top","left","third") |
|
^#!Numpad4::SnapActiveWindow("middle","left","third") |
|
^#!Numpad1::SnapActiveWindow("bottom","left","third") |
|
^#!Numpad9::SnapActiveWindow("top","right","third") |
|
^#!Numpad6::SnapActiveWindow("middle","right","third") |
|
^#!Numpad3::SnapActiveWindow("bottom","right","third") |
|
|
|
|
This is great!
I found though that keys like Win+Alt+Numpad 4 do not work when a window is maximized.
I'm using Windows 10.
Also, a suggestion for multi mon:
I have a 2nd monitor to the left and my main monitor on the right.
When I press Win_Alt Numpad 4 the window will jump to the left edge of the main monitor.
Pressing Win + Alt + Numpad 4 again jumps the window to the left edge of the left monitor.
I'd rather it jump to the right edge of the left monitor.
In general I'd like a way to jump between monitors, but with mirrored X coordinates: Jumping a window that is on the right edge of the right monitor, it should go to the left edge of the left monitor, and a window on the left edge of the right monitor should jump to the right edge of the left monitor.