Window Control

Post your working scripts, libraries and tools for AHK v1.1 and older
Verdlin
Posts: 63
Joined: 04 Oct 2013, 08:55
Contact:

Window Control

04 Nov 2013, 12:16

Brother to Control Control.

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:
  1. 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.
  2. 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.
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

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}
}
timeFlies
Posts: 146
Joined: 22 Oct 2013, 20:54
Location: Somewhere in the northern hemisphere.

Re: Window Control

06 Nov 2013, 08:28

Unfortunately, this still seems to trigger the Alt key and open the menubar in Internet Explorer. This script is a good replacement for what I had before.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 108 guests