Skip to content

Instantly share code, notes, and snippets.

@th0th
Forked from park-brian/AdvancedWindowSnap.ahk
Created August 14, 2019 11:14
Show Gist options
  • Save th0th/5428836c4e8c75f674a43d4b3822576f to your computer and use it in GitHub Desktop.
Save th0th/5428836c4e8c75f674a43d4b3822576f to your computer and use it in GitHub Desktop.
Advanced Window Snap is a script for AutoHotKey that expands upon Windows built-in window-snapping hotkeys.

Advanced Window Snap

Advanced Window Snap is a script for AutoHotKey that expands upon Windows built-in window-snapping hotkeys (which are Win + LEFT to snap an active window to the left half of a monitor and Win + RIGHT to snap a window to the right half of a monitor) by adding 9 additional snap methods.

Installation Steps

  1. Install AutoHotKey
  2. Copy or Download the AdvancedWindowSnap.ahk file to your computer and double click it to run it.
  3. (Optional) To have the program run when you start up your computer, place the .ahk file into your computer's startup folder.
    • The Windows 7 Startup Folder can be accessed by mousing to Start > All Programs, then right-clicking on Startup and selecting "Open".
    • The Windows 8 Startup Folder can be accessed by tapping Win + R on your keyboard, then in the Open: field, type shell:startup then press Enter.

Advanced Window Snap Keybindings

Directional Arrow Hotkeys:

Hotkey Behavior
Win + Alt + UP Window will snap to the top half of the screen.
Win + Alt + DOWN Window will snap to the bottom half of the screen.
Ctrl + Win + Alt + UP Window will snap to the top third of the screen.
Ctrl + Win + Alt + DOWN Window will snap to the bottom third of the screen.

Numberpad Hotkeys (Landscape):

These will work only if you have NumLock turned ON. These are ideal for Landscape Monitors.

Hotkey Behavior
Win + Alt + Numpad 4 Window will snap to the left third of the screen.
Win + Alt + Numpad 5 Window will snap to the center third of the screen.
Win + Alt + Numpad 6 Window will snap to the right third of the screen.

Numberpad Hotkeys (Portrait):

These will work only if you have NumLock turned ON. These are ideal for Portrait Monitors.

Hotkey Behavior
Ctrl + Win + Numpad 8 Window will snap to the top third of the screen.
Ctrl + Win + Numpad 5 Window will snap to the middle third of the screen.
Ctrl + Win + Numpad 2 Window will snap to the bottom third of the screen

Numberpad Hotkeys (3x3 Grid):

These will work only if you have NumLock turned ON.

Hotkey Behavior
Ctrl + Win + Alt + Numpad 7 Window will snap to the upper left third of the screen.
Ctrl + Win + Alt + Numpad 8 Window will snap to the upper center third of the screen.
Ctrl + Win + Alt + Numpad 9 Window will snap to the upper right third of the screen.
Ctrl + Win + Alt + Numpad 4 Window will snap to the center left third of the screen.
Ctrl + Win + Alt + Numpad 5 Window will snap to the center third of the screen.
Ctrl + Win + Alt + Numpad 6 Window will snap to the center right third of the screen.
Ctrl + Win + Alt + Numpad 1 Window will snap to the lower left third of the screen.
Ctrl + Win + Alt + Numpad 2 Window will snap to the lower center third of the screen.
Ctrl + Win + Alt + Numpad 3 Window will snap to the lower right third of the screen.

Changelog

  • v1.1, 15 May 2019

    • Added Grid
  • v1.00, 08 Jan 2015

    • Initial Version R

Recommendation For Editing AHK Files

If you plan on working with AutoHotKey files, consider using Sublime Text 3. Read my steps for setting up Sublime Text 3 to edit AutoHotKey files here: Working with AutoHotKey in Sublime Text.

/**
* Advanced Window Snap
* Snaps the Active Window to one of nine different window positions.
*
* @author Andrew Moore <[email protected]>
* @version 1.1
*/
/**
* Resizes and moves the active window to a given position on a grid
* @param {integer} numRows The number of rows in the grid
* @param {integer} numCols The number of columns in the grid
* @param {integer} row The specific row within the grid to place the window
* @param {integer} col The specific column within the grid to place the window
*
* @example (Snap a window to the left third of the screen)
* SnapActiveWindowGrid(1, 3, 1, 1);
*
* @example (Snap a window to the bottom half of the screen)
* SnapActiveWindowGrid(2, 1, 2, 1);
*/
SnapActiveWindowGrid(numRows, numCols, row, col) {
WinGet activeWin, ID, A
activeMon := GetMonitorIndexFromWindow(activeWin)
SysGet, MonitorWorkArea, MonitorWorkArea, %activeMon%
; Determine the width and height
height := (MonitorWorkAreaBottom - MonitorWorkAreaTop)/numRows
width := (MonitorWorkAreaRight - MonitorWorkAreaLeft)/numCols
; Determine the x and y offsets
posX := MonitorWorkAreaLeft + (col - 1) * width
posY := MonitorWorkAreaTop + (row - 1) * height
; Use WinGetPosEx to determine position/size offsets (to remove gaps around windows)
WinGetPosEx(activeWin, X, Y, realWidth, realHeight, offsetX, offsetY)
; Move and resize the active window
WinMove, A,, (posX + offsetX), (posY + offsetY), (width + offsetX * -2), (height + (offsetY - 2) * -2)
}
/**
* 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%
}
;------------------------------
;
; Function: WinGetPosEx
;
; Description:
;
; Gets the position, size, and offset of a window. See the *Remarks* section
; for more information.
;
; Parameters:
;
; hWindow - Handle to the window.
;
; X, Y, Width, Height - Output variables. [Optional] If defined, these
; variables contain the coordinates of the window relative to the
; upper-left corner of the screen (X and Y), and the Width and Height of
; the window.
;
; Offset_X, Offset_Y - Output variables. [Optional] Offset, in pixels, of the
; actual position of the window versus the position of the window as
; reported by GetWindowRect. If moving the window to specific
; coordinates, add these offset values to the appropriate coordinate
; (X and/or Y) to reflect the true size of the window.
;
; Returns:
;
; If successful, the address of a RECTPlus structure is returned. The first
; 16 bytes contains a RECT structure that contains the dimensions of the
; bounding rectangle of the specified window. The dimensions are given in
; screen coordinates that are relative to the upper-left corner of the screen.
; The next 8 bytes contain the X and Y offsets (4-byte integer for X and
; 4-byte integer for Y).
;
; Also if successful (and if defined), the output variables (X, Y, Width,
; Height, Offset_X, and Offset_Y) are updated. See the *Parameters* section
; for more more information.
;
; If not successful, FALSE is returned.
;
; Requirement:
;
; Windows 2000+
;
; Remarks, Observations, and Changes:
;
; * Starting with Windows Vista, Microsoft includes the Desktop Window Manager
; (DWM) along with Aero-based themes that use DWM. Aero themes provide new
; features like a translucent glass design with subtle window animations.
; Unfortunately, the DWM doesn't always conform to the OS rules for size and
; positioning of windows. If using an Aero theme, many of the windows are
; actually larger than reported by Windows when using standard commands (Ex:
; WinGetPos, GetWindowRect, etc.) and because of that, are not positioned
; correctly when using standard commands (Ex: gui Show, WinMove, etc.). This
; function was created to 1) identify the true position and size of all
; windows regardless of the window attributes, desktop theme, or version of
; Windows and to 2) identify the appropriate offset that is needed to position
; the window if the window is a different size than reported.
;
; * The true size, position, and offset of a window cannot be determined until
; the window has been rendered. See the example script for an example of how
; to use this function to position a new window.
;
; * 20150906: The "dwmapi\DwmGetWindowAttribute" function can return odd errors
; if DWM is not enabled. One error I've discovered is a return code of
; 0x80070006 with a last error code of 6, i.e. ERROR_INVALID_HANDLE or "The
; handle is invalid." To keep the function operational during this types of
; conditions, the function has been modified to assume that all unexpected
; return codes mean that DWM is not available and continue to process without
; it. When DWM is a possibility (i.e. Vista+), a developer-friendly messsage
; will be dumped to the debugger when these errors occur.
;
; Credit:
;
; Idea and some code from *KaFu* (AutoIt forum)
;
; Author:
;
; jballi
;
; Forum Link:
;
; https://autohotkey.com/boards/viewtopic.php?t=3392
;-------------------------------------------------------------------------------
WinGetPosEx(hWindow,ByRef X="",ByRef Y="",ByRef Width="",ByRef Height="",ByRef Offset_X="",ByRef Offset_Y="") {
Static Dummy5693
,RECTPlus
,S_OK:=0x0
,DWMWA_EXTENDED_FRAME_BOUNDS:=9
;-- Workaround for AutoHotkey Basic
PtrType:=(A_PtrSize=8) ? "Ptr":"UInt"
;-- Get the window's dimensions
; Note: Only the first 16 bytes of the RECTPlus structure are used by the
; DwmGetWindowAttribute and GetWindowRect functions.
VarSetCapacity(RECTPlus,24,0)
DWMRC:=DllCall("dwmapi\DwmGetWindowAttribute"
,PtrType,hWindow ;-- hwnd
,"UInt",DWMWA_EXTENDED_FRAME_BOUNDS ;-- dwAttribute
,PtrType,&RECTPlus ;-- pvAttribute
,"UInt",16) ;-- cbAttribute
if (DWMRC<>S_OK)
{
if ErrorLevel in -3,-4 ;-- Dll or function not found (older than Vista)
{
;-- Do nothing else (for now)
}
else
outputdebug,
(ltrim join`s
Function: %A_ThisFunc% -
Unknown error calling "dwmapi\DwmGetWindowAttribute".
RC=%DWMRC%,
ErrorLevel=%ErrorLevel%,
A_LastError=%A_LastError%.
"GetWindowRect" used instead.
)
;-- Collect the position and size from "GetWindowRect"
DllCall("GetWindowRect",PtrType,hWindow,PtrType,&RECTPlus)
}
;-- Populate the output variables
X:=Left :=NumGet(RECTPlus,0,"Int")
Y:=Top :=NumGet(RECTPlus,4,"Int")
Right :=NumGet(RECTPlus,8,"Int")
Bottom :=NumGet(RECTPlus,12,"Int")
Width :=Right-Left
Height :=Bottom-Top
OffSet_X:=0
OffSet_Y:=0
;-- If DWM is not used (older than Vista or DWM not enabled), we're done
if (DWMRC<>S_OK)
Return &RECTPlus
;-- Collect dimensions via GetWindowRect
VarSetCapacity(RECT,16,0)
DllCall("GetWindowRect",PtrType,hWindow,PtrType,&RECT)
GWR_Width :=NumGet(RECT,8,"Int")-NumGet(RECT,0,"Int")
;-- Right minus Left
GWR_Height:=NumGet(RECT,12,"Int")-NumGet(RECT,4,"Int")
;-- Bottom minus Top
;-- Calculate offsets and update output variables
NumPut(Offset_X:=(Width-GWR_Width)//2,RECTPlus,16,"Int")
NumPut(Offset_Y:=(Height-GWR_Height)//2,RECTPlus,20,"Int")
Return &RECTPlus
}
; Directional Arrow Hotkeys
; Snap to top half of screen (Win + Alt + Arrows)
; 2 rows, 1 column, first row, first column
#!Up::SnapActiveWindowGrid(2, 1, 1, 1)
; Snap to bottom half of screen
; 2 rows, 1 columns, second row, first column
#!Down::SnapActiveWindowGrid(2, 1, 2, 1)
; Snap to top third of screen (Ctrl + Win + Alt + Arrows)
; 3 rows, 1 column, first row, first column
^#!Up::SnapActiveWindowGrid(3, 1, 1, 1)
; Snap to bottom third of screen
; 3 rows, 1 column, third row, first column
^#!Down::SnapActiveWindowGrid(3, 1, 3, 1)
; Numberpad Hotkeys
; Snap to left third of screen (Win + Alt + Numpad)
; 1 row, 3 columns, first row, first column
#!Numpad4::SnapActiveWindowGrid(1, 3, 1, 1)
; Snap to center third of screen
; 1 row, 3 columns, first row, second column
#!Numpad5::SnapActiveWindowGrid(1, 3, 1, 2)
; Snap to right third of screen
; 1 row, 3 columns, first row, second column
#!Numpad6::SnapActiveWindowGrid(1, 3, 1, 3)
; Snap to upper third of screen (Win + Alt + Numpad)
; 3 rows, 1 column, first row, first column
^#Numpad8::SnapActiveWindowGrid(3, 1, 1, 1)
; Snap to middle third of screen
; 3 rows, 1 column, second row, first column
^#Numpad5::SnapActiveWindowGrid(3, 1, 2, 1)
; Snap to bottom third of screen
; 3 rows, 1 column, third row, first column
^#Numpad2::SnapActiveWindowGrid(3, 1, 3, 1)
; Snap to top left third of screen (Ctrl + Win + Alt + Numpad)
^#!Numpad7::SnapActiveWindowGrid(3, 3, 1, 1)
; Snap to top middle third of screen
^#!Numpad8::SnapActiveWindowGrid(3, 3, 1, 2)
; Snap to top right third of screen
^#!Numpad9::SnapActiveWindowGrid(3, 3, 1, 3)
; Snap to center left third of screen
^#!Numpad4::SnapActiveWindowGrid(3, 3, 2, 1)
; Snap to center third of screen
^#!Numpad5::SnapActiveWindowGrid(3, 3, 2, 2)
; Snap to center right third of screen
^#!Numpad6::SnapActiveWindowGrid(3, 3, 2, 3)
; Snap to bottom left third of screen
^#!Numpad1::SnapActiveWindowGrid(3, 3, 3, 1)
; Snap to bottom middle third of screen
^#!Numpad2::SnapActiveWindowGrid(3, 3, 3, 2)
; Snap to bottom right third of screen
^#!Numpad3::SnapActiveWindowGrid(3, 3, 3, 3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment