This is a simple scripts that packages some nify window control functions into Alt+L/R/MButton combinations. After releasing Control Control, it felt just natural to progress towards this script.
The two biggest advantages to this script are as follows:
- You may move/resize a window clicking anywhere within a window without sending unwanted keystrokes/clicks to the window. You probably are thinking, "So what? That is easy." Maybe you know, maybe you don't know, but using hotkeys such as ~Alt & ~LButton don't work well for scripts like this because they do send keystrokes/clicks to the window underneath the mouse.
- Smart, mac-like multi-monitor support. Tested and working well on four monitors. This script detects that corners of the window, and it will disallow resizing/moving past monitor bezels until a certain pixel threshold is crossed (g_iIntraMonitorThreashold := 350 pixels. Works well on 1920x1080 monitors, but you probably will want to reduce this number for smaller resolutions). An added bonus is that, unintentionally, this corner-restriction is not invoked for dramatic mouse movements, so if you move your mouse fast, the window will past the monitors corner -- like mac.
1. Alt + LButton = Move window
2. Alt + Shift + LButton = Move window along X axis only
3. Alt + Ctrl + LButton = Move window along Y axis only
4. Alt + RButton = Resize
5. Alt + Shift + RButton = Resize window width only
6. Alt + Ctrl + RButton = Resize window height only
7. Alt + MButton OR Alt + Win + C = Change text within window
8. Win + LButton = Enable/Disable Window
Code: Select all
#Persistent
#SingleInstance, Force
; Hotkeys
; 1. Alt + LButton = Move window
; 2. Alt + Shift + LButton = Move window along X axis only
; 3. Alt + Ctrl + LButton = Move window along Y axis only
; 4. Alt + RButton = Resize
; 5. Alt + Shift + RButton = Resize window width only
; 6. Alt + Ctrl + RButton = Resize window height only
; 7. Alt + MButton OR Alt + Win + C = Change text within window
; 8. Win + LButton = Enable/Disable Window
Menu, TRAY, NoStandard
Menu, TRAY, MainWindow ; For compiled scripts
Menu, TRAY, Add, &Reload, Reload
Menu, TRAY, Add, E&xit, Exit
CoordMode, Mouse
SetWinDelay, -1
g_iIntraMonitorThreashold := 350 ; pixels
return ; End auto-execute
Reload:
Reload
Exit:
ExitApp
#!c::
{
gosub ChangeWindowTitle
return
}
; Simple hotkeys like ~Alt & ~LButton cannot be used becuase this does not disable clicks inside of windows
Shift & ~Alt::
Ctrl & ~Alt::
~Alt::
{
Hotkey, *LButton, Alt_And_LButton, On
Hotkey, *RButton, Alt_And_RButton, On
Hotkey, *MButton, ChangeWindowTitle, On
KeyWait, Alt
Hotkey, *LButton, Off
Hotkey, *RButton, Off
Hotkey, *MButton, Off
if (A_ThisHotkey = "*LButton")
gosub Alt_And_LButton
else if (A_ThisHotkey = "*RButton")
gosub Alt_And_RButton
else if (A_ThisHotkey = "*MButton")
gosub ChangeWindowTitle
return
}
Alt_And_LButton:
{
iPrevMouseX := iPrevMouseY := A_Blank
MouseGetPos,,, hWnd
if (IsApprovedHwnd(hWnd))
{
g_bUseXThreshold := g_bUseYThreshold := true
g_iXDeltaAtLeftCorner := g_iXDeltaAtRightCorner := g_iYDeltaAtTopCorner := g_iYDeltaAtBottomCorner := 0
while (GetKeyState("Alt", "P") && GetKeyState("LButton", "P"))
{
bIgnoreX := GetKeyState("Ctrl", "P")
bIgnoreY := GetKeyState("Shift", "P")
MouseGetPos, iMouseX, iMouseY
iMouseX := iMouseX
iMouseY := iMouseY
WinGetPos, iX, iY, iWndW, iWndH, ahk_id %hWnd%
iXDelta := bIgnoreX ? 0 : iMouseX - (iPrevMouseX == A_Blank ? iMouseX : iPrevMouseX)
iYDelta := bIgnoreY ? 0 : iMouseY - (iPrevMouseY == A_Blank ? iMouseY : iPrevMouseY)
if (iXDelta == 0 && iYDelta == 0)
{
iPrevMouseX := iMouseX
iPrevMouseY := iMouseY
continue
}
iX := iX + iXDelta
iY := iY + iYDelta
bMoveX := bMoveY := true
rectMonMouseIsOn := GetMonitorRectAt(iMouseX, iMouseY)
; if we have passed a monitor corner over onto another monitor, or else we have shifted directions towards to opposite corner of the monitor, reset
if (abs(iMouseX) - abs(iMouseXPosAtCorner) < g_iIntraMonitorThreashold)
g_bUseXThreshold := true
if (abs(iMouseY) - abs(iMouseYPosAtCorner) < g_iIntraMonitorThreashold)
g_bUseYThreshold := true
if (g_bUseXThreshold)
{
if (!bIgnoreX)
{
if (iXDelta < 0 ; moving window to the left
&& iX - iXDelta >= rectMonMouseIsOn.left ; the left corner of the wnd is not already past the monitor's left corner
&& iX < rectMonMouseIsOn.left) ; we are trying to move the window past the left corner
{
g_iXDeltaAtLeftCorner += abs(iXDelta)
if (g_iXDeltaAtLeftCorner < g_iIntraMonitorThreashold)
{
bMoveX := false
g_bUseXThreshold := true
}
else
{
bMoveX := true
g_bUseXThreshold := false
iMouseXPosAtCorner := iMouseX
g_iXDeltaAtLeftCorner := 0
}
}
else if (iXDelta > 0 ; moving window to the right
&& iX - iXDelta + iWndW <= rectMonMouseIsOn.right ; the right corner of the wnd is not already past the monitor's right corner
&& (iX + iWndW) > rectMonMouseIsOn.right) ; we are trying to move the window past the right corner
{
g_iXDeltaAtRightCorner += abs(iXDelta)
if (g_iXDeltaAtRightCorner < g_iIntraMonitorThreashold)
{
bMoveX := false
g_bUseXThreshold := true
}
else
{
bMoveX := true
g_bUseXThreshold := false
iMouseXPosAtCorner := iMouseX
g_iXDeltaAtRightCorner := 0
}
}
}
}
if (g_bUseYThreshold)
{
if (!bIgnoreY)
{
if (iYDelta < 0 ; moving window to the bottom
&& iY - iYDelta >= rectMonMouseIsOn.top ; the top corner of the wnd is not already past the monitor's top corner
&& iY < rectMonMouseIsOn.top) ; we are trying to move the window past the top corner
{
g_iYDeltaAtBottomCorner += abs(iYDelta)
if (g_iYDeltaAtBottomCorner < g_iIntraMonitorThreashold)
{
bMoveY := false
g_bUseYThreshold := true
}
else
{
bMoveY := true
g_bUseYThreshold := false
iMouseYPosAtCorner := iMouseY
g_iYDeltaAtBottomCorner := 0
}
}
else if (iYDelta > 0 ; moving window to the right
&& iY - iYDelta + iWndH <= rectMonMouseIsOn.bottom ; the right corner of the wnd is not already past the monitor's bottom corner
&& (iY + iWndH) > rectMonMouseIsOn.bottom) ; we are trying to move the window past the bottom corner
{
g_iYDeltaAtTopCorner += abs(iYDelta)
if (g_iYDeltaAtTopCorner < g_iIntraMonitorThreashold)
{
bMoveY := false
g_bUseYThreshold := true
}
else
{
bMoveY := true
g_bUseYThreshold := false
iMouseYPosAtCorner := iMouseY
g_iYDeltaAtTopCorner := 0
}
}
}
}
WinMove, ahk_id %hWnd%,, % bMoveX ? iX : "", bMoveY ? iY : ""
iPrevMouseX := iMouseX
iPrevMouseY := iMouseY
}
}
return
}
Alt_And_RButton:
{
iPrevMouseX := iPrevMouseY := A_Blank
g_bUseWThreshold := g_bUseHThreshold := true
MouseGetPos,,, hWnd
if (IsApprovedHwnd(hWnd))
{
while (GetKeyState("Alt", "P") && GetKeyState("RButton", "P"))
{
bIgnoreW := GetKeyState("Ctrl", "P")
bIgnoreH := GetKeyState("Shift", "P")
MouseGetPos, iMouseX, iMouseY
iMouseX := iMouseX
iMouseY := iMouseY
WinGetPos, iX, iY, iWndW, iWndH, ahk_id %hWnd%
iXDelta := bIgnoreW ? 0 : iMouseX - (iPrevMouseX == A_Blank ? iMouseX : iPrevMouseX)
iYDelta := bIgnoreH ? 0 : iMouseY - (iPrevMouseY == A_Blank ? iMouseY : iPrevMouseY)
if (iXDelta == 0 && iYDelta == 0)
{
iPrevMouseX := iMouseX
iPrevMouseY := iMouseY
continue
}
iWndW := iWndW + iXDelta
iWndH := iWndH + iYDelta
bMoveW := bMoveH := true
rectMonMouseIsOn := GetMonitorRectAt(iMouseX, iMouseY)
; if we have passed a monitor corner over onto another monitor, or else we have shifted directions towards to opposite corner of the monitor, reset
if (abs(iMouseX) - abs(iMouseXPosAtCorner) < g_iIntraMonitorThreashold)
g_bUseWThreshold := true
if (abs(iMouseY) - abs(iMouseYPosAtCorner) < g_iIntraMonitorThreashold)
g_bUseHThreshold := true
if (g_bUseWThreshold)
{
if (!bIgnoreW)
{
if (iXDelta < 0 ; moving window to the left
&& iX - iXDelta >= rectMonMouseIsOn.left ; the left corner of the wnd is not already past the monitor's left corner
&& iX < rectMonMouseIsOn.left) ; we are trying to move the window past the left corner
{
g_iXDeltaAtLeftCorner += abs(iXDelta)
if (g_iXDeltaAtLeftCorner < g_iIntraMonitorThreashold)
{
bMoveW := false
g_bUseWThreshold := true
}
else
{
bMoveW := true
g_bUseWThreshold := false
iMouseXPosAtCorner := iMouseX
g_iXDeltaAtLeftCorner := 0
}
}
else if (iXDelta > 0 ; moving window to the right
&& iX - iXDelta + iWndW <= rectMonMouseIsOn.right ; the right corner of the wnd is not already past the monitor's right corner
&& (iX + iWndW) > rectMonMouseIsOn.right) ; we are trying to move the window past the right corner
{
g_iXDeltaAtRightCorner += abs(iXDelta)
if (g_iXDeltaAtRightCorner < g_iIntraMonitorThreashold)
{
bMoveW := false
g_bUseWThreshold := true
}
else
{
bMoveW := true
g_bUseWThreshold := false
iMouseXPosAtCorner := iMouseX
g_iXDeltaAtRightCorner := 0
}
}
}
}
if (g_bUseHThreshold)
{
if (!bIgnoreH)
{
if (iYDelta < 0 ; moving window to the bottom
&& iY - iYDelta >= rectMonMouseIsOn.top ; the top corner of the wnd is not already past the monitor's top corner
&& iY < rectMonMouseIsOn.top) ; we are trying to move the window past the top corner
{
g_iYDeltaAtBottomCorner += abs(iYDelta)
if (g_iYDeltaAtBottomCorner < g_iIntraMonitorThreashold)
{
bMoveH := false
g_bUseHThreshold := true
}
else
{
bMoveH := true
g_bUseHThreshold := false
iMouseYPosAtCorner := iMouseY
g_iYDeltaAtBottomCorner := 0
}
}
else if (iYDelta > 0 ; moving window to the right
&& iY - iYDelta + iWndH <= rectMonMouseIsOn.bottom ; the right corner of the wnd is not already past the monitor's bottom corner
&& (iY + iWndH) > rectMonMouseIsOn.bottom) ; we are trying to move the window past the bottom corner
{
g_iYDeltaAtTopCorner += abs(iYDelta)
if (g_iYDeltaAtTopCorner < g_iIntraMonitorThreashold)
{
bMoveH := false
g_bUseHThreshold := true
}
else
{
bMoveH := true
g_bUseHThreshold := false
iMouseYPosAtCorner := iMouseY
g_iYDeltaAtTopCorner := 0
}
}
}
}
WinMove, ahk_id %hWnd%,,,, % bMoveW ? iWndW : "", bMoveH ? iWndH : ""
iPrevMouseX := iMouseX
iPrevMouseY := iMouseY
}
}
return
}
ChangeWindowTitle:
{
; Turn off hotkeys so that the LButton is not responise
Hotkey, *LButton, Off
Hotkey, *RButton, Off
Hotkey, *MButton, Off
; This hotkey seems to be triggered twice every time it is activated, so g_iTimeAtThisExecution is used to prevent double-execution
g_iTimeAtThisExecution := SubStr(A_Now, StrLen(A_Now) - 3, 4)
if (A_ThisHotkey = "*MButton" && g_iTimeAtLastExecution != A_Blank && g_iTimeAtThisExecution - g_iTimeAtLastExecution < 1)
return
MouseGetPos,,, hWnd
if (IsApprovedHwnd(hWnd))
{
WinGetTitle, sExistingTitle, ahk_id %hWnd%
InputBox, sNewTitle, Set Window Title,,,,,,,,, %sExistingTitle%
g_iTimeAtLastExecution := SubStr(A_Now, StrLen(A_Now) - 3, 4)
if (ErrorLevel)
return
WinSetTitle, ahk_id %hWnd%,, %sNewTitle%
}
return
}
#LButton::
{
MouseGetPos,,, hWnd
if (IsApprovedHwnd(hWnd))
{
sEnable := (DllCall("IsWindowEnabled", uint, hWnd) ? "Disable" : "Enable")
WinSet, %sEnable%,, ahk_id %hWnd%
TT_Out("Window " sEnable "d!")
}
return
}
!+C::
{
MouseGetPos,,,, hCtrl, 2
ControlGetText, sCtrlTxt,, ahk_id %hCtrl%
ControlGetPos, iX, iY, iW, iH,, ahk_id %hCtrl%
if !((iX == A_Blank || iY == A_Blank || iW == A_Blank || iH == A_Blank))
clipboard := "Control:`t" sCtrlTxt "`nLeft:`t" iX "`nTop:`t" iY "`nRight:`t" iW "`nBottom:`t" iH
return
}
TT_Out(sOutput)
{
Tooltip, %sOutput%
SetTimer, TT_Out, 2500
return
}
TT_Out:
{
Tooltip
SetTimer, TT_Out, Off
return
}
IsApprovedHwnd(hWnd)
{
WinGetClass, sClass, ahk_id %hWnd%
return !(sClass== "WorkerW"
|| sClass == "Shell_TrayWnd"
|| sClass== "Progman"
|| sClass== "SideBar_HTMLHostWindow")
}
/*
===============================================================================
Function: wp_GetMonitorAt (Modified by Verdlin to return monitor rect)
Get the index of the monitor containing the specified x and y coordinates.
Parameters:
x,y - Coordinates
default - Default monitor
Returns:
array of monitor coordinates
Author(s):
Original - Lexikos - http://www.autohotkey.com/forum/topic21703.html
===============================================================================
*/
GetMonitorRectAt(x, y, default=1)
{
SysGet, m, MonitorCount
; Iterate through all monitors.
Loop, %m%
{ ; Check if the window is on this monitor.
SysGet, Mon%A_Index%, MonitorWorkArea, %A_Index%
if (x >= Mon%A_Index%Left && x <= Mon%A_Index%Right && y >= Mon%A_Index%Top && y <= Mon%A_Index%Bottom)
return {left: Mon%A_Index%Left, right: Mon%A_Index%Right, top: Mon%A_Index%Top, bottom: Mon%A_Index%Bottom}
}
return {left: Mon%default%Left, right: Mon%default%Right, top: Mon%default%Top, bottom: Mon%default%Bottom}
}