Ok, I've spent sometime making the keys changeable, and it seems to be working without any problems (not the cleanest implementation, but it works).
I've also added a minimize, but it doesn't work very well. I'm wondering if someone can help me to get it working properly.
Ideally when u minimize it hooks to the window that was minimized. So the next commands should work on that window. I have it so that if you minimize it usually restores, but not always.
Here's the key layout:
fill left half (win+alt+z), fill right half (win+alt+x) -- win+ctrl = alternate
1/4 screen top left = q, top right = w, bottom left = a, bottom right = s
top half = e, bottom half = d, center screen = c.
Maximize = up arrow
Minimize = down arrow
also, fill left half works with left arrow, and fill right works with right arrow.
Heres my code: (Ignore the comments from the original version)
Code:
; WindowPad:
;
; Move and resize windows with Win+Numpad.
; Win+Numpad1 = Fill bottom-left quarter of screen
; Win+Numpad2 = Fill bottom half of screen
; etc.
;
; Move windows across monitors. For example:
; Win+Numpad4 places the window on the left half of the screen.
; Win+Numpad4 again moves it to the monitor to the right.
;
; Quick monitor switch:
; Win+Numpad5 places the window in the center of the screen.
; Win+Numpad5 again moves the window to the next monitor.
; (This works by monitor number, not necessarily left to right.)
;
; QUICKER Monitor Switch:
; Win+NumpadDot switches to the next monitor (1->2->3->1 etc.)
; Win+NumpadDiv moves ALL windows to monitor 2.
; Win+NumpadMult moves ALL windows to monitor 1.
;
; Other shortcuts:
; Win+Numpad0 toggles maximize.
; Insert (or some other key) can be used in place of "Win".
;
; Credits:
; Concept based on HiRes Screen Splitter by JOnGliko.
; Written from scratch by Lexikos to support multiple monitors.
; NumpadDot key functionality suggested by bobbo.
;
; Built with AutoHotkey v1.0.47.02
;
; HISTORY
;
; Version 1.1:
; - "Gather windows" hotkeys (NumpadDiv and NumpadMult)
; - NumpadDot to move window to next monitor
; - Added more EasyKey combos (for symmetry)
; - Original functionality of EasyKey is retained (on key-release)
; - SetWinDelay, -1 to reduce lag when making multiple moves (quickly)
;
; Version 1:
; - intial release
WindowPadInit:
Prefix_Active = #! ; Alt+Win+Char = Move active window
Prefix_Other = #^ ; Ctrl+Win+Numpad = Move previously active window
; Note: Shift (+) should not be used, as +Numpad is hooked by the OS
; to do left/right/up/down/etc. (reverse Numlock) -- at least on Vista.
EasyKey = Insert ; Insert is near Numpad on my keyboard...
; Note: Prefix_Other must not be a sub-string of Prefix_Active.
; (If you want it to be, first edit the line "if (InStr(A_ThisHotkey, Prefix_Other))")
Hotkey, IfWinActive ; in case this is included in another script...
;;Edit all the hotkeys here:
; Left and Right
Leftt = z
Rightt = x
; Grid of four
TopLeft = q
TopRight = w
BottomLeft = a
BottomRight = s
; Top and Bottom
Top = e
Bottom = d
; Center
Center = c
Hotkey1a = %Prefix_Active%%BottomLeft%
Hotkey1o = %Prefix_Other%%BottomLeft%
Hotkey2a = %Prefix_Active%%Bottom%
Hotkey2o = %Prefix_Other%%Bottom%
Hotkey3a = %Prefix_Active%%BottomRight%
Hotkey3o = %Prefix_Other%%BottomRight%
Hotkey4a = %Prefix_Active%%Leftt%
Hotkey4o = %Prefix_Other%%Leftt%
Hotkey4a2 = %Prefix_Active%Left
Hotkey4o2 = %Prefix_Other%Left
Hotkey5a = %Prefix_Active%%Center%
Hotkey5o = %Prefix_Other%%Center%
Hotkey6a = %Prefix_Active%%Rightt%
Hotkey6o = %Prefix_Other%%Rightt%
Hotkey6a2 = %Prefix_Active%Right
Hotkey6o2 = %Prefix_Other%Right
Hotkey7a = %Prefix_Active%%TopLeft%
Hotkey7o = %Prefix_Other%%TopLeft%
Hotkey8a = %Prefix_Active%%Top%
Hotkey8o = %Prefix_Other%%Top%
Hotkey9a = %Prefix_Active%%TopRight%
Hotkey9o = %Prefix_Other%%TopRight%
Hotkey, %Hotkey1a%, DoMoveWindowInDirection
Hotkey, %Hotkey1o%, DoMoveWindowInDirection
Hotkey, %Hotkey2a%, DoMoveWindowInDirection
Hotkey, %Hotkey2o%, DoMoveWindowInDirection
Hotkey, %Hotkey3a%, DoMoveWindowInDirection
Hotkey, %Hotkey3o%, DoMoveWindowInDirection
Hotkey, %Hotkey4a%, DoMoveWindowInDirection
Hotkey, %Hotkey4o%, DoMoveWindowInDirection
Hotkey, %Hotkey4a2%, DoMoveWindowInDirection
Hotkey, %Hotkey4o2%, DoMoveWindowInDirection
Hotkey, %Hotkey5a%, DoMoveWindowInDirection
Hotkey, %Hotkey5o%, DoMoveWindowInDirection
Hotkey, %Hotkey6a%, DoMoveWindowInDirection
Hotkey, %Hotkey6o%, DoMoveWindowInDirection
Hotkey, %Hotkey6a2%, DoMoveWindowInDirection
Hotkey, %Hotkey6o2%, DoMoveWindowInDirection
Hotkey, %Hotkey7a%, DoMoveWindowInDirection
Hotkey, %Hotkey7o%, DoMoveWindowInDirection
Hotkey, %Hotkey8a%, DoMoveWindowInDirection
Hotkey, %Hotkey8o%, DoMoveWindowInDirection
Hotkey, %Hotkey9a%, DoMoveWindowInDirection
Hotkey, %Hotkey9o%, DoMoveWindowInDirection
; Setup Hotkeys
;Loop, 9
;{ ; Register hotkeys.
; Hotkey, %Prefix_Active%Numpad%A_Index%, DoMoveWindowInDirection
; Hotkey, %Prefix_Other%Numpad%A_Index%, DoMoveWindowInDirection
;
; ; OPTIONAL
; if EasyKey
; Hotkey, %EasyKey% & Numpad%A_Index%, DoMoveWindowInDirection
;}
Hotkey, %Prefix_Active%Up, DoMaximizeToggle
Hotkey, %Prefix_Other%Up, DoMaximizeToggle
Hotkey, %Prefix_Active%Down, DoMinimizeToggle
Hotkey, %Prefix_Other%Down, DoMinimizeToggle
Hotkey, %Prefix_Active%NumpadDot, MoveWindowToNextScreen
Hotkey, %Prefix_Other%NumpadDot, MoveWindowToNextScreen
Hotkey, %Prefix_Active%NumpadDiv, GatherWindowsLeft
Hotkey, %Prefix_Active%NumpadMult, GatherWindowsRight
;if (EasyKey) {
; Hotkey, %EasyKey% & Numpad0, DoMaximizeToggle
; Hotkey, %EasyKey% & NumpadDot, MoveWindowToNextScreen
; Hotkey, %EasyKey% & NumpadDiv, GatherWindowsLeft
; Hotkey, %EasyKey% & NumpadMult, GatherWindowsRight
; Hotkey, %EasyKey%, SendEasyKey ; let EasyKey's original function work (on release)
;}
return
SendEasyKey:
Send {%EasyKey%}
return
; This is actually based on monitor number, so if your secondary is on the
; right, you may want to switch these around.
GatherWindowsLeft:
GatherWindows(1)
return
GatherWindowsRight:
GatherWindows(2)
return
; Hotkey handler.
DoMoveWindowInDirection:
; Define constants.
if (!Directions1) {
dir = -1:+1,0:+1,+1:+1,-1:0,0:0,+1:0,-1:-1,0:-1,+1:-1
StringSplit, Directions, dir, `,
}
gosub WP_SetLastFoundWindowByHotkey
; Determine which direction we want to go.
;if (!RegExMatch(A_ThisHotkey, "\d+", dir) or !Directions%dir%)
;{
; MsgBox Error: "%A_ThisHotkey%" was registered and I can't figure out which number it is!
; return
;}
if (A_ThisHotKey = Hotkey1a) or (A_ThisHotKey = Hotkey1o)
{
dir := Directions1
}
else if (A_ThisHotKey = Hotkey2a) or (A_ThisHotKey = Hotkey2o)
{
dir := Directions2
}
else if (A_ThisHotKey = Hotkey3a) or (A_ThisHotKey = Hotkey3o)
{
dir := Directions3
}
else if (A_ThisHotKey = Hotkey4a) or (A_ThisHotKey = Hotkey4o) or (A_ThisHotKey = Hotkey4a2) or (A_ThisHotKey = Hotkey4o2)
{
dir := Directions4
}
else if (A_ThisHotKey = Hotkey5a) or (A_ThisHotKey = Hotkey5o)
{
dir := Directions5
}
else if (A_ThisHotKey = Hotkey6a) or (A_ThisHotKey = Hotkey6o) or (A_ThisHotKey = Hotkey6a2) or (A_ThisHotKey = Hotkey6o2)
{
dir := Directions6
}
else if (A_ThisHotKey = Hotkey7a) or (A_ThisHotKey = Hotkey7o)
{
dir := Directions7
}
else if (A_ThisHotKey = Hotkey8a) or (A_ThisHotKey = Hotkey8o)
{
dir := Directions8
}
else if (A_ThisHotKey = Hotkey9a) or (A_ThisHotKey = Hotkey9o)
{
dir := Directions9
}
;dir := Directions%dir%
StringSplit, dir, dir, :
; Determine width/height factors.
if (dir1 or dir2) { ; to a side
widthFactor := dir1 ? 0.5 : 1.0
heightFactor := dir2 ? 0.5 : 1.0
} else { ; to center
widthFactor := 0.5
heightFactor := 0.5
}
; Move the window!
MoveWindowInDirection(dir1, dir2, widthFactor, heightFactor)
return
WP_SetLastFoundWindowByHotkey:
; Set Last Found Window.
if (InStr(A_ThisHotkey, Prefix_Other))
WinPreviouslyActive()
else
WinExist("A")
return
; "Maximize"
DoMaximizeToggle:
gosub WP_SetLastFoundWindowByHotkey
WinGet, state, MinMax
if minimizedId
{
WinRestore, ahk_id %minimizedId%
minimizedId = 0
}
else
{
if state
WinRestore
else
WinMaximize
}
return
; "Minimize"
DoMinimizeToggle:
gosub WP_SetLastFoundWindowByHotkey
WinGet, state, MinMax
WinGet, active_id, ID, A
if minimizedId
{
; Msgbox minimizedId = %minimizedId%
WinPreviouslyActive()
if state
{
WinRestore, ahk_id %minimizedId%
}
minimizedId = 0
}
else
{
; Msgbox minimizedId = %minimizedId%
if state >= 0
{
minimizedId = %active_id%
WinMinimize, ahk_id %minimizedId%
}
}
return
; Does the grunt work of the script.
MoveWindowInDirection(sideX, sideY, widthFactor, heightFactor, screenMoveOnly=false)
{
WinGetPos, x, y, w, h
; Determine which monitor contains the center of the window.
m := GetMonitorAt(x+w/2, y+h/2)
; Get work area of active monitor.
gosub CalcMonitorStats
; Calculate possible new position for window.
gosub CalcNewPosition
; If the window is already there,
if (newx "," newy "," neww "," newh) = (x "," y "," w "," h)
{ ; ..move to the next monitor along instead.
if (sideX or sideY)
{ ; Move in the direction of sideX or sideY.
SysGet, monB, Monitor, %m% ; get bounds of entire monitor (vs. work area)
x := (sideX>0 ? monBRight : monBLeft) + sideX
y := (sideY>0 ? monBBottom : monBTop) + sideY
newm := GetMonitorAt(x, y, m)
}
else
{ ; Move to center (Numpad5)
newm := m+1
SysGet, mon, MonitorCount
if (newm > mon)
newm := 1
}
if (newm != m)
{ m := newm
; Move to opposite side of monitor (left of a monitor is another monitor's right edge)
sideX *= -1
sideY *= -1
; Get new monitor's work area.
gosub CalcMonitorStats
}
; Calculate new position for window.
gosub CalcNewPosition
}
; Restore before resizing...
WinGet, state, MinMax
if state
WinRestore
; Finally, move the window!
SetWinDelay, -1
WinMove,,, newx, newy, neww, newh
return
CalcNewPosition:
; Calculate new size.
if (IsResizable()) {
neww := Round(monWidth * widthFactor)
newh := Round(monHeight * heightFactor)
} else {
neww := w
newh := h
}
; Calculate new position.
newx := Round(monLeft + (sideX+1) * (monWidth - neww)/2)
newy := Round(monTop + (sideY+1) * (monHeight - newh)/2)
return
CalcMonitorStats:
; Get work area (excludes taskbar-reserved space.)
SysGet, mon, MonitorWorkArea, %m%
monWidth := monRight - monLeft
monHeight := monBottom - monTop
return
}
; Get the index of the monitor containing the specified x and y co-ordinates.
GetMonitorAt(x, y, default=1)
{
y := 100
SysGet, m, MonitorCount
; Iterate through all monitors.
Loop, %m%
{ ; Check if the window is on this monitor.
SysGet, Mon, Monitor, %A_Index%
if (x >= MonLeft && x <= MonRight && y >= MonTop && y <= MonBottom)
return A_Index
}
return default
}
IsResizable()
{
WinGet, Style, Style
return (Style & 0x40000) ; WS_SIZEBOX
}
; Note: This may not work properly with always-on-top windows. (Needs testing)
WinPreviouslyActive()
{
active := WinActive("A")
WinGet, win, List
; Find the active window.
; (Might not be win1 if there are always-on-top windows?)
Loop, %win%
if (win%A_Index% = active)
{
if (A_Index < win)
N := A_Index+1
; hack for PSPad: +1 seems to get the document (child!) window, so do +2
ifWinActive, ahk_class TfPSPad
N += 1
break
}
; Use WinExist to set Last Found Window (for consistency with WinActive())
return WinExist("ahk_id " . win%N%)
}
;
; Switch without moving/resizing (relative to screen)
;
MoveWindowToNextScreen:
gosub WP_SetLastFoundWindowByHotkey
MoveWindowToNextScreen()
return
MoveWindowToNextScreen()
{
WinGetPos, x, y, w, h
; Determine which monitor contains the center of the window.
ms := GetMonitorAt(x+w/2, y+h/2)
; Determine which monitor to move to.
md := ms+1
SysGet, mon, MonitorCount
if (md > mon)
md := 1
; This may happen if someone tries it with only one screen. :P
if (md = ms)
return
; Get source and destination work areas (excludes taskbar-reserved space.)
SysGet, ms, MonitorWorkArea, %ms%
SysGet, md, MonitorWorkArea, %md%
msw := msRight - msLeft, msh := msBottom - msTop
mdw := mdRight - mdLeft, mdh := mdBottom - mdTop
; Calculate new size.
if (IsResizable()) {
w *= (mdw/msw)
h *= (mdh/msh)
}
SetWinDelay, -1
; Move window, using resolution difference to scale co-ordinates.
WinMove,,, mdLeft + (x-msLeft)*(mdw/msw), mdTop + (y-msTop)*(mdh/msh), w, h
}
;
; "Gather" windows on a specific screen.
;
GatherWindows(md=1)
{
SetWinDelay, -1 ; Makes a BIG difference to perceived performance.
; List all visible windows.
WinGet, win, List
; Copy bounds of all monitors to an array.
SysGet, mc, MonitorCount
Loop, %mc%
SysGet, mon%A_Index%, MonitorWorkArea, %A_Index%
; Destination monitor
mdx := mon%md%Left
mdy := mon%md%Top
mdw := mon%md%Right - mdx
mdh := mon%md%Bottom - mdy
Loop, %win%
{
; Set Last Found Window.
if (!WinExist("ahk_id " . win%A_Index%))
continue
WinGetPos, x, y, w, h
; Determine which monitor this window is on.
xc := x+w/2, yc := y+h/2
ms := 1
Loop, %mc%
if (xc >= mon%A_Index%Left && xc <= mon%A_Index%Right
&& yc >= mon%A_Index%Top && yc <= mon%A_Index%Bottom)
{
ms := A_Index
break
}
; If already on destination monitor, skip this window.
if (ms = md)
continue
; Source monitor
msx := mon%ms%Left
msy := mon%ms%Top
msw := mon%ms%Right - msx
msh := mon%ms%Bottom - msy
; If the window is resizable, scale it by the monitors' resolution difference.
if (IsResizable()) {
w *= (mdw/msw)
h *= (mdh/msh)
}
; Move window, using resolution difference to scale co-ordinates.
WinMove,,, mdx + (x-msx)*(mdw/msw), mdy + (y-msy)*(mdh/msh), w, h
}
}