Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Windows-7 Like Window Positioning for XP and Vista


  • Please log in to reply
80 replies to this topic
PatrickS
  • Members
  • 14 posts
  • Last active: Jul 20 2019 01:08 AM
  • Joined: 21 Mar 2010
At home, I regularly use the new Windows 7 key combinations Win-Left and Win-Right. These keys reposition / resize the current window to occupy either the entire left half or entire right half of the screen.

When you want to compare two documents, or when you want to copy files between two directories, these new keystrokes come in awfully handy.

Unfortunately, I don't have access to these neat tricks on my XP-based corporate laptop, so I wrote the following scripts as replacement. I've tested them on XP but they ought to work just as well on Vista.

Hope you like them.

;; -----------------------------------------------------------------------
; Get the position and size of the desktop, taking the taskbar area into account.
; This function probably doesn't work on secondary monitors.

Win__GetDesktopPos(ByRef X, ByRef Y, ByRef W, ByRef H)
{
	; Get dimensions of the system tray (taskbar)
	WinGetPos, TrayX, TrayY, TrayW, TrayH, ahk_class Shell_TrayWnd
	
	if (TrayW = A_ScreenWidth)
	{
		; Horizontal Taskbar
		X := 0
		Y := TrayY ? 0 : TrayH
		W := A_ScreenWidth
		H := A_ScreenHeight - TrayH
	}
	else
	{
		; Vertical Taskbar
		X := TrayX ? 0 : TrayW
		Y := 0
		W := A_ScreenWidth - TrayW
		H := A_ScreenHeight
	}
}

;; -----------------------------------------------------------------------
; Mimic Windows-7 Win-Left Key Combination

Win__HalfLeft()
{
	Win__GetDesktopPos(X, Y, W, H)
	WinMove, A,, X, Y, W/2, H
}

;; -----------------------------------------------------------------------
; Mimic Windows-7 Win-Right Key Combination

Win__HalfRight()
{	
	Win__GetDesktopPos(X, Y, W, H)
	WinMove, A,, X + W/2, Y, W/2, H
}

;; -----------------------------------------------------------------------
; Use the Alt-Left and Alt-Right key combinations to simulate
; Win-7's Win-Left and Win-Right key functions.

Alt & Left::    Win__HalfLeft()
Alt & Right::  Win__HalfRight()


Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
Nice. Maybe to add Win__FullSize()
Win__FullSize()
{   
   Win__GetDesktopPos(X, Y, W, H)
   WinMove, A,, X, Y, W, H
}


guest3456
  • Guests
  • Last active:
  • Joined: --
you might look into SysGet and MonitorWorkArea, that might replace having to look for the taskbar, i'm not sure

PatrickS
  • Members
  • 14 posts
  • Last active: Jul 20 2019 01:08 AM
  • Joined: 21 Mar 2010

you might look into SysGet and MonitorWorkArea, that might replace having to look for the taskbar, i'm not sure


Thanks. I did do that originally but found that I got different answers with the two techniques, and that the one I used in my Win__GetDesktopPos() function gave more useful numbers.

Anyhow, I just published a new function, Win__Fling(), whose code uses SysGet and the MonitorWorkArea stuff extensively, with good results so far, so perhaps it was newbie error.

Mr Fish
  • Members
  • 3 posts
  • Last active: Apr 21 2010 09:14 AM
  • Joined: 26 Mar 2010
Very nice script for people with big monitors (me).

I added support of 1/4 window support.

;Full
Win__Full() {   
   Win__GetDesktopPos(X, Y, W, H)
   WinMove, A,, X , Y, W, H
}

;TopFourt 
Win__1TopFourth() {   
   Win__GetDesktopPos(X, Y, W, H)
   WinMove, A,, X , Y, W/2, H/2
}

Win__2TopFourth() {   
   Win__GetDesktopPos(X, Y, W, H)
   WinMove, A,, X+W/2 , Y, W/2, H/2
}

;BottomFourth
Win__1BottomFourth() {
   Win__GetDesktopPos(X, Y, W, H)
   WinMove, A,, X, Y+H/2, W/2, H/2
}

Win__2BottomFourth() {
   Win__GetDesktopPos(X, Y, W, H)
   WinMove, A,, X+W/2, Y+H/2, W/2, H/2
}

;Keys
Alt & Space:: Win__Full()
Alt & Ins:: Win__1TopFourth()
Alt & PgUp:: Win__2TopFourth()
Alt & Del:: Win__1BottomFourth()
Alt & PgDn:: Win__2BottomFourth()


Bluespianist
  • Members
  • 11 posts
  • Last active: Feb 23 2012 09:32 PM
  • Joined: 27 Mar 2010
Great script. Thanks a million. Works like a charm. I run it on Vista, and replaced your Alt keys with Win-keys: #Right and Left.

vahju
  • Members
  • 337 posts
  • Last active: Sep 21 2014 03:52 AM
  • Joined: 17 Feb 2008
I put a little twist on moving the windows to left or right side of screen. Press the same hotkey again and it makes it full screen.

; Mimic Windows-7 Win-Left Key Combination
Win__HalfLeft()
{
   Global WinLtoggle := !WinLtoggle
   If !WinLtoggle
   {
      Win__Full()
      return
   }
   Win__GetDesktopPos(X, Y, W, H)
   WinMove, A,, X, Y, W/2, H
}
return

Though if you move a window left then right it also maximizes the window. Maybe this can be done another way by combining a incremental counter and comparing xywh each time function is run.

PatrickS
  • Members
  • 14 posts
  • Last active: Jul 20 2019 01:08 AM
  • Joined: 21 Mar 2010
Thanks everyone for the feedback.

I've been trying to configure an ideal multi-monitor setup at home (and to some degree at work, too) and I found that my original script screws up - It always moves the active window to the primary monitor as part of the re-sizing operation.

To solve this, I took some of the code I developed for my Win__Fling() script and created a new version of these window re-sizers which maintain the screen on its original monitor.

Here's Version 2.0, which also incorporates some of the other suggestions in this thread (use of the 'work area' system parameters, full screen windows, quarter sized windows). If you also work in a multi-monitor environment, I highly recommend you move to this new version. Coupled with my Win__Fling() function, you now get pretty decent control over the places and shapes of all your windows.

Your comments are welcome...

;; -----------------------------------------------------------------------
;; This function returns the position and dimensions of the monitor which
;; contains (the centre of) a specified window.
;;
;; The target window is given by the parameter WinID, which can be:
;;	(a) The letter "A" signifying the [A]ctive window
;;	(b) The letter "M" signifying the window under the [M]ouse
;;	(c) An arbitrary AHK window ID
;;
;; The centre of the target window is located on one of the monitors and
;; the coordinates of that monitor are returned in the X, Y, W, H output
;; parameters.
;;
;; Note that the input parameter WinID is resolved to an actual window ID
;; when either of the special values "A" or "M" are used. This makes
;; it easy to pass on the ID to another function, like a built-in function,
;; which doesn't grok the "A" or "M" notations.

Win__GetMonitorPos(ByRef X, ByRef Y, ByRef W, ByRef H, ByRef WinID)
{
	; Find the target window based on the "WinID" function parameter.
	; Handle the following special parameter values:
	;	1) The letter "A" means to use the Active window
	;	2) The letter "M" means to use the window under the Mouse
	; Otherwise, the parameter value is assumed to be the AHK window ID of the window to use.

	if (WinID = "A")
	{
		; If the user supplied an "A" as the window ID, we use the Active window
		WinID := WinExist("A")
	}
	else if (WinID = "M")
	{
		; If the user supplied an "M" as the window ID, we use the window currently under the Mouse
		MouseGetPos, MouseX, MouseY, WinID
	}

	; Check to make sure we are working with a valid window
	IfWinNotExist, ahk_id %WinID%
	{
		; Make a short noise so the user knows to stop expecting something fun to happen.
		SoundPlay, *64

		; Debug Support
		;MsgBox, 16, Error, Specified window does not exist.`nWindow ID = %WinID%

		return 0
	}

	; Retrieve the target window's dimensions and from these compute its centre
	WinGetPos, WinX, WinY, WinW, WinH, ahk_id %WinID%
	WinCentreX := WinX + WinW // 2
	WinCentreY := WinY + WinH // 2

	; Here's where we find out just how many monitors we're dealing with
	SysGet, MonitorCount, MonitorCount

	; For each active monitor, we get Top, Bottom, Left, Right of the monitor's
	;  'Work Area' (i.e., excluding taskbar, etc.) and see if the centre of our
	;  target window lies within these bounds.

	WinMonitor = 0
	Loop, %MonitorCount%
	{
		SysGet, Monitor%A_Index%, MonitorWorkArea, %A_Index%
		
		; Is the target window's centre inside this monitor's working area?
		if (    (WinCentreX >= Monitor%A_Index%Left) and (WinCentreX < Monitor%A_Index%Right )
		    and (WinCentreY >= Monitor%A_Index%Top ) and (WinCentreY < Monitor%A_Index%Bottom))
		{
			; Yes -- so record the monitor number and then bail from the loop
			WinMonitor = %A_Index%
			break
		}
	}

	if WinMonitor = 0
	{
		; The centre of the target window wasn't found in any active monitor(?!). Strange.
		; Only useful thing to do in this case seems to be to use the 'Primary' monitor.
		SysGet, WinMonitor, MonitorPrimary
	}
		
	X := Monitor%WinMonitor%Left
	Y := Monitor%WinMonitor%Top
	W := Monitor%WinMonitor%Right  - Monitor%WinMonitor%Left
	H := Monitor%WinMonitor%Bottom - Monitor%WinMonitor%Top

	return 1
}

;; -----------------------------------------------------------------------

Win__QuarterTopLeft(WinID)
{
	if Win__GetMonitorPos(X, Y, W, H, WinID)
	{
		WinMove, ahk_id %WinID%,, X, Y, W//2, H//2
	}
}

;; -----------------------------------------------------------------------

Win__QuarterTopRight(WinID)
{
	if Win__GetMonitorPos(X, Y, W, H, WinID)
	{
		WinMove, ahk_id %WinID%,, X + W//2, Y, W - W//2, H//2
	}
}

;; -----------------------------------------------------------------------

Win__QuarterBottomLeft(WinID)
{
	if Win__GetMonitorPos(X, Y, W, H, WinID)
	{
		WinMove, ahk_id %WinID%,, X, Y + H//2, W//2, H - H//2
	}
}

;; -----------------------------------------------------------------------

Win__QuarterBottomRight(WinID)
{
	if Win__GetMonitorPos(X, Y, W, H, WinID)
	{
		WinMove, ahk_id %WinID%,, X + W//2, Y + H//2, W - W//2, H - H//2
	}
}

;; -----------------------------------------------------------------------
; Mimic Windows-7 Win-Left Key Combination

Win__HalfLeft(WinID)
{
	if Win__GetMonitorPos(X, Y, W, H, WinID)
	{
		WinMove, ahk_id %WinID%,, X, Y, W//2, H
	}
}

;; -----------------------------------------------------------------------
; Mimic Windows-7 Win-Right Key Combination

Win__HalfRight(WinID)
{
	if Win__GetMonitorPos(X, Y, W, H, WinID)
	{
		WinMove, ahk_id %WinID%,, X + W//2, Y, W - W//2, H
	}
}

;; -----------------------------------------------------------------------

Win__HalfTop(WinID)
{
	if Win__GetMonitorPos(X, Y, W, H, WinID)
	{
		WinMove, ahk_id %WinID%,, X, Y, W, H//2
	}
}

;; -----------------------------------------------------------------------

Win__HalfBottom(WinID)
{
	if Win__GetMonitorPos(X, Y, W, H, WinID)
	{
		WinMove, ahk_id %WinID%,, X, Y + H//2, W, H - H//2
	}
}

;; -----------------------------------------------------------------------

Win__Fullscreen(WinID)
{
	if Win__GetMonitorPos(X, Y, W, H, WinID)
	{
		WinMove, ahk_id %WinID%,, X, Y, W, H
	}
}

To go along with this script, may I suggest the following hotkeys which use the geometry of the keypad as a mnemonic for how the windows will be re-sized. Notice how the hotkey functions now all require the "A" parameter to indicate that it is the "Active" window that is to be re-sized.

!Numpad1::      Win__QuarterBottomLeft("A")
!Numpad2::      Win__HalfBottom("A")
!Numpad3::      Win__QuarterBottomRight("A")
!Numpad4::      Win__HalfLeft("A")
!Numpad5::      Win__Fullscreen("A")
!Numpad6::      Win__HalfRight("A")
!Numpad7::      Win__QuarterTopLeft("A")
!Numpad8::      Win__HalfTop("A")
!Numpad9::      Win__QuarterTopRight("A")



DeRoc
  • Members
  • 9 posts
  • Last active: Jan 31 2016 03:59 PM
  • Joined: 04 Jul 2009
This is VERY nice - Thanks! :p

But don't forget "Numpad0", too.
!Numpad0::      Win__CenterScreen("A")
Win__Centerscreen(WinID)
{
   if Win__GetMonitorPos(X, Y, W, H, WinID)
   {
      WinMove, ahk_id %WinID%,, X + W//5, Y + H//8, W - W//2.5, H - H//4
   }
}


Mr Fish
  • Members
  • 3 posts
  • Last active: Apr 21 2010 09:14 AM
  • Joined: 26 Mar 2010

This is VERY nice - Thanks! :p

But don't forget "Numpad0", too.

!Numpad0::      Win__CenterScreen("A")
Win__Centerscreen(WinID)
{
   if Win__GetMonitorPos(X, Y, W, H, WinID)
   {
      WinMove, ahk_id %WinID%,, X + W//5, Y + H//8, W - W//2.5, H - H//4
   }
}


Now shouldn't that be on 5, then full could be on 0 :) Edit: Really like this feature by the way.

Very nice work PatrickS.
Computer janitor with a future in burger flipping.

DeRoc
  • Members
  • 9 posts
  • Last active: Jan 31 2016 03:59 PM
  • Joined: 04 Jul 2009
Good suggestion, Mr Fish
....shuffling numbers now... :wink:

PatrickS
  • Members
  • 14 posts
  • Last active: Jul 20 2019 01:08 AM
  • Joined: 21 Mar 2010
Ok, so I can't help myself from tinkering... here's a new version.

Change 1

Version 3.0 introduces a new algorithm for determining the monitor to which a window belongs. Previously, the code used to look at the centre point of the window and see which monitor contained it. The obvious problem with that is that it's quite possible for a window to be hanging so far outside the bounds of its monitor that its centre doesn't show up in any of them.

The new algorithm looks for the monitor which contains 'most' of the window, in the sense of screen area. The only way this new algorithm can 'fail' is for the window to not appear at all in any monitor. I'm not sure if that's even possible -- I'm really not much of a Windows guru.

Change 2

I've introduced what I think is a better way handle moving / resizing. I've defined a new function called Win__AlignToGrid(). Basically, it takes a window and a grid definition given by the user and it moves / re-sizes the window to fit on one or more cells in the grid. See the documentation at the top of the function itself for more details.

Change 3

There was a bunch of code in common with my other current AHK programming project, Win__Fling(). That function is designed to fling (move, shift, throw) a window from one monitor to another in a multi-monitor system. Being a good programmer, I decided it was time to modularize the code, so this new version has a bunch of small functions that are used by both Win__AlignToGrid() and Win__Fling().

I've included the code for Win__Fling() below, but my other thread which introduced it has had no action so far, so I'm thinking you probably don't care. :(

Change 4

Inspired by DeRoc's input, I've introduced a new window moving function, Win__Centre(), which centres a window. However, unlike DeRoc's version, this function doesn't attempt to re-size the window, just move it so that it occupies the center portion of its monitor.

The code is included below. I've broken it up into different logical parts, but you'll of course need all of it for the thing to work.

The Main Window Resizing Code
;; -----------------------------------------------------------------------
;; Verifies that the given window exists. Along the way it also resolves
;; special values of the "WinID" function parameter:
;;		1) The letter "A" means to use the Active window
;;		2) The letter "M" means to use the window under the Mouse
;; The parameter value is checked to see that it corresponds to a valid
;; window, the function returning true or false accordingly.

Win__ResolveWinID(ByRef WinID)
{
	if (WinID = "A")
	{
		; "A" means: Use the Active window
		WinID := WinExist("A")
	}
	else if (WinID = "M")
	{
		; "M" means: Use the window currently under the Mouse
		MouseGetPos,,, WinID	; MouseX, MouseY are not needed
	}

	; Check to make sure we are working with a valid window ID
	IfWinNotExist, ahk_id %WinID%
	{
		; Make a short noise so the user knows to stop expecting something fun to happen.
		SoundPlay, *64

		; Debug Support
		;MsgBox, 16, Error, Specified window does not exist.`nWindow ID = %WinID%

		return false
	}

	return true
}

;; -----------------------------------------------------------------------
;; Set the min/maximized state of the given window.
;;
;; This function serves as a kind of inverse to the built-in function:
;;		WinGet, Var, MinMax, WinID

Win__SetMinMax(TargetMinMaxState, WinID)
{
	WinGet, CurrentMinMaxState, MinMax, ahk_id %WinID%

	if CurrentMinMaxState <> TargetMinMaxState
	{
		if (TargetMinMaxState = 1)
		{
			WinMaximize, ahk_id %WinID%
		}
		else if (TargetMinMaxState = -1)
		{
			WinMinimize, ahk_id %WinID%
		}
		else
		{
			WinRestore, ahk_id %WinID%
		}
	}
}

;; -----------------------------------------------------------------------
;; This function returns the position and dimensions of the monitor which
;; contains (the most screen area of) a specified window.
;;
;; Note that the input parameter WinID is resolved to an actual window ID.
;; See the documentation for Win__ResolveWinID() for details.

Win__GetMonitorPosShowingWin(ByRef MonX, ByRef MonY, ByRef MonW, ByRef MonH, ByRef MonN, ByRef WinID)
{
	if !Win__ResolveWinID(WinID)
	{
		; Specified window doesn't exist
		return false
	}

	; Compute the dimensions of the subject window
	WinGetPos, WinLeft, WinTop, WinWidth, WinHeight, ahk_id %WinID%
	WinRight  := WinLeft + WinWidth
	WinBottom := WinTop  + WinHeight

	; How many monitors are we dealing with?
	SysGet, MonitorCount, MonitorCount

	; For each active monitor, we get Top, Bottom, Left, Right of the monitor's
	;  'Work Area' (i.e., excluding taskbar, etc.). From these values we compute Width and Height.
	;  As we loop, we track which monitor has the largest overlap (in the sense of screen area)
	;  with the subject window. We call that monitor the window's 'Source Monitor'.

	SourceMonitorNum = 0
	MaxOverlapArea   = 0

	Loop, %MonitorCount%
	{
		MonitorNum    := A_Index		; Give the loop variable a sensible name

		; Retrieve position / dimensions of the monitor's work area
		SysGet, Monitor, MonitorWorkArea, %MonitorNum%
		MonitorWidth  := MonitorRight  - MonitorLeft
		MonitorHeight := MonitorBottom - MonitorTop

		; Check for any overlap with the subject window
		; The following ternary expressions simulate "max(a,b)" and "min(a,b)" type function calls:
		;	max(a,b) <==> (a>b ? a : b)
		;	min(a,b) <==> (a<b ? a : b)
		; The intersection between two windows is characterized as that part below both
		; windows' "Top" values and above both "Bottoms"; similarly to the right of both "Lefts"
		; and to the left of both "Rights". Hence the need for all these min/max operations.

		MaxTop    := (WinTop    > MonitorTop   ) ? WinTop    : MonitorTop
		MinBottom := (WinBottom < MonitorBottom) ? WinBottom : MonitorBottom

		MaxLeft   := (WinLeft   > MonitorLeft  ) ? WinLeft   : MonitorLeft
		MinRight  := (WinRight  < MonitorRight ) ? WinRight  : MonitorRight

		HorizontalOverlap := MinRight  - MaxLeft
		VerticalOverlap   := MinBottom - MaxTop

		if (HorizontalOverlap > 0 and VerticalOverlap > 0)
		{
			OverlapArea := HorizontalOverlap * VerticalOverlap
			if (OverlapArea > MaxOverlapArea)
			{
				SourceMonitorLeft		:= MonitorLeft
				SourceMonitorRight		:= MonitorRight		; not used
				SourceMonitorTop		:= MonitorTop
				SourceMonitorBottom		:= MonitorBottom	; not used
				SourceMonitorWidth		:= MonitorWidth
				SourceMonitorHeight		:= MonitorHeight
				SourceMonitorNum		:= MonitorNum

				MaxOverlapArea      	:= OverlapArea
			}
		}
	}

	if MaxOverlapArea = 0
	{
		; If the subject window wasn't visible in *ANY* monitor, default to the 'Primary'
		SysGet, SourceMonitorNum, MonitorPrimary

		SysGet, SourceMonitor, MonitorWorkArea, %SourceMonitorNum%
		SourceMonitorWidth  := SourceMonitorRight  - SourceMonitorLeft
		SourceMonitorHeight := SourceMonitorBottom - SourceMonitorTop
	}

	MonX := SourceMonitorLeft
	MonY := SourceMonitorTop
	MonW := SourceMonitorWidth
	MonH := SourceMonitorHeight
	MonN := SourceMonitorNum

	return true
}

;; -----------------------------------------------------------------------
;; Prepare a window for any sort of scripted 'move' operation.
;;
;; The first thing to do is to restore the window if it was min/maximized.
;; The reason for this is that the standard min/max window controls don't
;; seem to like it if you script a move / resize while a window is
;; minimized or maximized.
;;
;; After that, we look to see which monitor holds the "most" of the window
;; (in the sense of screen real estate) and we return a bunch of information
;; about that monitor so the caller can figure out the best way to do the move.
;;
;; The original min/max state is also returned in case the window needs to
;; be restored to that state at some future time.
;;
;; The window ID is also resolved, as per the function Win__ResolveWinID().

Win__PrepForMove(ByRef MonX, ByRef MonY, ByRef MonW, ByRef MonH, ByRef MonN, ByRef WinMinMax, ByRef WinID)
{
	if !Win__ResolveWinID(WinID)
	{
		; Subject window doesn't exist
		return false
	}

	; If the subject window started out min/maximized, then we restore it
	; in preparation of it being moved or resized.

	WinGet, WinMinMax, MinMax, ahk_id %WinID%
	if WinMinMax
	{
		WinRestore, ahk_id %WinID%
	}

	result := Win__GetMonitorPosShowingWin(MonX, MonY, MonW, MonH, MonN, WinID)
	return result
}

;; -----------------------------------------------------------------------
;; Move and resize a window to align it to a specified screen grid.
;;
;; The first two parameters, GridCols and GridRows, determine the granularity
;; of the grid. The other four grid parameters determine which grid cell
;; the window is to fit into and how big it should be in each direction:
;;
;;		GridUnitsX:	The X coordinate of the top left grid cell, in the range 1..GridCols
;;		GridUnitsY:	The Y coordinate of the top left grid cell, in the range 1..GridRows
;;		GridUnitsW:	The width  of the window, in units of cells
;;		GridUnitsH:	The height of the window, in units of cells
;;
;; For example, to arrange six windows on the screen, three across and two
;; down, each occupying a single grid cell, you might issue the following
;; six commands to six different windows (WinID1 .. WinID6):
;;
;;	 	Win__AlignToGrid(3, 2,   1, 1,   1, 1,   WinID1)
;;	 	Win__AlignToGrid(3, 2,   1, 2,   1, 1,   WinID2)
;;	 	Win__AlignToGrid(3, 2,   1, 3,   1, 1,   WinID3)
;;	 	Win__AlignToGrid(3, 2,   2, 1,   1, 1,   WinID4)
;;	 	Win__AlignToGrid(3, 2,   2, 2,   1, 1,   WinID5)
;;	 	Win__AlignToGrid(3, 2,   2, 3,   1, 1,   WinID6)
;;
;; I've added extra spaces between pairs of related parameters to act as visual
;; clues for the reader. The spaces are, of course, not required.
;;
;; These commands would result in the following gridded window arrangement:
;;
;;		+---------+---------+---------+
;;		|         |         |         |
;;		|    1    |    2    |    3    |
;;		|         |         |         |
;;		+---------+---------+---------+
;;		|         |         |         |
;;		|    4    |    5    |    6    |
;;		|         |         |         |
;;		+---------+---------+---------+
;;
;; As another example, consider the following two commands:
;;
;;	 	Win__AlignToGrid(3, 2,   1, 1,   2, 2,   WinID7)
;;	 	Win__AlignToGrid(3, 2,   3, 1,   1, 2,   WinID8)
;;
;; Here the windows are larger than a single grid cell, as they were in the first example.
;; The first command asks for a 2x2 window and the second one asks for a 1x2 (1 col, 2 rows)
;; window. This ought to result in the following window arrangement:
;;
;;		+-------------------+---------+
;;		|                   |         |
;;		|                   |         |
;;		|                   |         |
;;		|        7          |    8    |
;;		|                   |         |
;;		|                   |         |
;;		|                   |         |
;;		+-------------------+---------+
;;

Win__AlignToGrid(GridCols, GridRows, GridUnitsX, GridUnitsY, GridUnitsW, GridUnitsH, WinID)
{
	if !Win__PrepForMove(MonX, MonY, MonW, MonH, MonN, WinMinMax, WinID)
	{
		return false
	}

	; Bound the input parameters so that they make sense
	;  and so the window will actually fit on the screen
	Gen__Bound(GridCols,   1, MonW)
	Gen__Bound(GridRows,   1, MonH)
	Gen__Bound(GridUnitsX, 1, GridCols)
	Gen__Bound(GridUnitsY, 1, GridRows)
	Gen__Bound(GridUnitsW, 1, GridCols - GridUnitsX + 1)
	Gen__Bound(GridUnitsH, 1, GridRows - GridUnitsY + 1)

	X := Round(MonW * (GridUnitsX - 1) / GridCols) + MonX
	Y := Round(MonH * (GridUnitsY - 1) / GridRows) + MonY
	W := Round(MonW *  GridUnitsW      / GridCols)
	H := Round(MonH *  GridUnitsH      / GridRows)

	WinMove, ahk_id %WinID%,, X, Y, W, H
	return true
}

The New Half and Quarter Sized Window Handlers
;; -----------------------------------------------------------------------
Win__QuarterTopLeft(WinID)
{
;                    C  R    X  Y    W  H
	Win__AlignToGrid(2, 2,   1, 1,   1, 1,   WinID)
}

;; -----------------------------------------------------------------------

Win__QuarterTopRight(WinID)
{
;                    C  R    X  Y    W  H
	Win__AlignToGrid(2, 2,   2, 1,   1, 1,   WinID)
}

;; -----------------------------------------------------------------------

Win__QuarterBottomLeft(WinID)
{
;                    C  R    X  Y    W  H
	Win__AlignToGrid(2, 2,   1, 2,   1, 1,   WinID)
}

;; -----------------------------------------------------------------------

Win__QuarterBottomRight(WinID)
{
;                    C  R    X  Y    W  H
	Win__AlignToGrid(2, 2,   2, 2,   1, 1,   WinID)
}

;; -----------------------------------------------------------------------
; Mimic Windows-7 Win-Left Key Combination

Win__HalfLeft(WinID)
{
;                    C  R    X  Y    W  H
	Win__AlignToGrid(2, 2,   1, 1,   1, 2, WinID)
}

;; -----------------------------------------------------------------------
; Mimic Windows-7 Win-Right Key Combination

Win__HalfRight(WinID)
{
;                    C  R    X  Y    W  H
	Win__AlignToGrid(2, 2,   2, 1,   1, 2,   WinID)
}

;; -----------------------------------------------------------------------

Win__HalfTop(WinID)
{
;                    C  R    X  Y    W  H
	Win__AlignToGrid(2, 2,   1, 1,   2, 1,   WinID)
}

;; -----------------------------------------------------------------------

Win__HalfBottom(WinID)
{
;                    C  R    X  Y    W  H
	Win__AlignToGrid(2, 2,   1, 2,   2, 1,   WinID)
}

;; -----------------------------------------------------------------------

Win__Fullscreen(WinID)
{
;                    C  R    X  Y    W  H
	Win__AlignToGrid(1, 1,   1, 1,   1, 1,   WinID)
}

;; -----------------------------------------------------------------------

Win__Centre(WinID)
{
	if !Win__PrepForMove(MonX, MonY, MonW, MonH, MonN, MinMax, WinID)
	{
		return false
	}

	WinGetPos, WinX, WinY, WinW, WinH, ahk_id %WinID%

	BorderX := WinW < MonW ? MonW - WinW : 0
	BorderY := WinH < MonH ? MonH - WinH : 0

	X := MonX + BorderX // 2
	Y := MonY + BorderY // 2
	W := MonW - BorderX
	H := MonH - BorderY

	WinMove, ahk_id %WinID%,, X, Y, W, H
	return true
}

Some General Purpose Stuff

;; -----------------------------------------------------------------------
;; Bound the given variable between the specified lower and upper limits.
;; i.e., the post-condition is:  Lower <= Value <= Upper

Gen__Bound(ByRef Value, Lower, Upper)
{
	if (Value < Lower)
	{
		Value := Lower
	}
	else if (Value > Upper)
	{
		Value := Upper
	}
}

Code to Implement the Multi-Monitor Window Flinging
(This code requires some of the earlier functions to operate)

;; -----------------------------------------------------------------------
;; Compute the next monitor number in (circular) sequence.

Win__NextMonitorNum(SourceMonitorNum, Direction = 1)
{
	SysGet, MonitorCount, MonitorCount
	TargetMonitorNum := SourceMonitorNum + (Direction >= 0 ? 1 : -1)

	; Valid monitor numbers are 1..MonitorCount.
	; Adjust target for out-of-bounds values while effecting a 'circular' sequence.

	if (TargetMonitorNum > MonitorCount)
	{
		TargetMonitorNum = 1
	}
	else if (TargetMonitorNum <= 0)
	{
		TargetMonitorNum = %MonitorCount%
	}

	return TargetMonitorNum
}

;; -----------------------------------------------------------------------
;; Fling (i.e., move, shift, throw) a window from one monitor to the next in a multi-monitor system.
;;
;; Function Parameters:
;;
;;		FlingDirection		The direction of the fling, expected to be either +1 or -1.
;;							The function is not limited to just two monitors; it supports
;;							as many monitors as are currently connected to the system and
;;							can fling a window serially through each of them in turn, in a
;;							circular fashion.
;;
;;		WinID				The window ID of the window to move.
;;							There are two special WinID values supported:
;;
;;							1) The value "A" means to use the [A]ctive window (default).
;;							2) The value "M" means to use the window currently under the [M]ouse.
;;
;; The flung window will be resized to have the same *relative* size in the new monitor.
;; For example, if the window originally occupied the entire right half of its original monitor,
;; it will again on the new monitor (assuming the window is one that can be resized).
;;
;; The return value of the function is non-zero if the window was successfully flung.
;;
;; Example hotkeys:
;;	#NumpadEnter::	Win__Fling(1, "A")	; Windows-NumpadEnter flings the active window
;;	#LButton::		Win__Fling(1, "M")	; Windows-LeftClick flings the window under the mouse

Win__Fling(FlingDirection = 1, WinID = "A")
{
	if FlingDirection = 0
	{
		; Ok, so we're going nowhere after all
		return true
	}

	; Assume the worst
	result = false

	; If the subject window started out min/maximized, then the plan is to:
	;	(a) restore it,
	;	(b) fling it, then
	;	(c) re-min/maximize it on the target monitor.
	; The reason for this is so that the usual maximize / restore windows controls
	; work as you'd expect. You want Windows to use the dimensions of the non-maximized
	; window when you click the little restore icon on a previously flung (min/maximized) window.

	if Win__PrepForMove(SourceMonitorLeft, SourceMonitorTop, SourceMonitorWidth, SourceMonitorHeight, SourceMonitorNum, WinMinMax, WinID)
	{
		; Compute the number of the target monitor in the specified direction of the fling
		TargetMonitorNum := Win__NextMonitorNum(SourceMonitorNum, FlingDirection)

		if SourceMonitorNum <> TargetMonitorNum
		{
			; Get the dimensions of the subject window
			WinGetPos, WinLeft, WinTop, WinWidth, WinHeight, ahk_id %WinID%

			; Get the dimensions of the target monitor
			SysGet, TargetMonitor, MonitorWorkArea, %TargetMonitorNum%
			TargetMonitorWidth  := TargetMonitorRight  - TargetMonitorLeft
			TargetMonitorHeight := TargetMonitorBottom - TargetMonitorTop

			; Translate and scale the position / dimensions of the subject window by the ratio of the two monitor sizes.
			; Programming Note 1: Do multiplies before divides in order to maintain accuracy.
			; Programming Note 2: If you truncate instead of round, the window tends to creep to the upper left and get smaller.
			;   as you repeatedly switch monitors. That's not a bug, it's just a natural artefact of integer calculations with
			;   expansion factors that are non-integers. Rounding, however, tends to stablilize after just a couple of flings.

			WinFlingLeft   :=  Round((WinLeft   - SourceMonitorLeft) * TargetMonitorWidth  / SourceMonitorWidth ) + TargetMonitorLeft
			WinFlingTop    :=  Round((WinTop    - SourceMonitorTop ) * TargetMonitorHeight / SourceMonitorHeight) + TargetMonitorTop
			WinFlingWidth  :=  Round( WinWidth                       * TargetMonitorWidth  / SourceMonitorWidth )
			WinFlingHeight :=  Round( WinHeight                      * TargetMonitorHeight / SourceMonitorHeight)

			; It's time for the subject window to make its highly anticipated move
			WinMove, ahk_id %WinID%,, WinFlingLeft, WinFlingTop, WinFlingWidth, WinFlingHeight
		}

		result = true
	}

	; If the subject window was originally min/maximized, then min/maximize it again on its new monitor.
	; Note that this step should be done whether or not the window actually got moved by the above code.
	Win__SetMinMax(WinMinMax, WinID)

	; As if anybody is listening...
	return result
}

And here are some revised hotkeys. I've moved to using only the Windows key prefix macros, as it just makes sense to me for windows manipulating scripts.

#Numpad0::		Win__Fullscreen("A")
#Numpad1::      Win__QuarterBottomLeft("A")
#Numpad2::      Win__HalfBottom("A")
#Numpad3::      Win__QuarterBottomRight("A")
#Numpad4::      Win__HalfLeft("A")
#Numpad5::      Win__Centre("A")
#Numpad6::      Win__HalfRight("A")
#Numpad7::      Win__QuarterTopLeft("A")
#Numpad8::      Win__HalfTop("A")
#Numpad9::      Win__QuarterTopRight("A")

#NumpadEnter::	Win__Fling(1, "A")	; Windows-NumpadEnter flings the active window
#LButton::		Win__Fling(1, "M")	; Windows-LeftClick flings the window under the mouse



Mr Fish
  • Members
  • 3 posts
  • Last active: Apr 21 2010 09:14 AM
  • Joined: 26 Mar 2010
Masterful documentation mate, great work.
Computer janitor with a future in burger flipping.

DeRoc
  • Members
  • 9 posts
  • Last active: Jan 31 2016 03:59 PM
  • Joined: 04 Jul 2009
I've run across a CAVEAT which might be of note:

When I use WinAmp, it sits at the top of my screen as a narrow control bar. I happened to press the "UpperHalf" hotkey while it was still selected and it enlarged the WinAmp "canvas" below the actual bar to a big, gray, unsizeable box! :shock:

Well, WinAmp doesn't have a default size-reset anywhere that I could find (even looked through the registry), so I ended up editing this script temporarily to force the resize back to the original dimensions.

LOL :lol:
Thought some others users might run into this.

Love using your program, though. Thanks PatrickS.
(Oh and thanks for the "honorable mention" -- glad to be of some inspiration!)

PatrickS
  • Members
  • 14 posts
  • Last active: Jul 20 2019 01:08 AM
  • Joined: 21 Mar 2010
Hi Folks

I'm back with yet another update. I'm introducing a new function, Win__Gridder() which is able to automatically grid (or tile) your windows into configurable arrangements.

If you sometimes work with a lot of windows, like a bunch of Exporer windows where you're dragging files all around, or a couple of Word windows doing compares or cross-edits, then this new function will gather all your Explorer windows, or all your Word windows, or whatever, into a nice gridded arrangement, in one keystroke.

You can find my latest script on AutoHotkey.net at this link. This update includes all of the code I've previously talked about in this thread, plus this new function.

The new function depends on a configurable set of gridding rules that go into a simple text file. Below you'll find the one I'm using. You should put this into a file named "Gridder.txt" in your %A_WorkingDir% (typically "My Documents").

The new capability is pretty heavily commented, so please have a look at the code for more info. I've tried to make it easy to use so please provide feedback, yay or nay.

Gridding Rules File
; Gridding Rules for AHK function Win__Gridder()
;N		C	R		X	Y		W	H
1		1	1		1	1		1	1
2		2	2		1	1		1	2
2		2	2		2	1		1	2
3		2	2		1	1		1	2
3		2	2		2	1		1	1
3		2	2		2	2		1	1
4		2	2		1	1		1	1
4		2	2		1	2		1	1
4		2	2		2	1		1	1
4		2	2		2	2		1	1
5		2	2		1	1		1	1
5		2	2		2	1		1	1
5		3	2		1	2		1	1
5		3	2		2	2		1	1
5		3	2		3	2		1	1
6		3	2		1	1		1	1
6		3	2		2	1		1	1
6		3	2		3	1		1	1
6		3	2		1	2		1	1
6		3	2		2	2		1	1
6		3	2		3	2		1	1

Example Hotkeys
#PrintScreen::	Win__Gridder(true)
+#PrintScreen::	Win__Gridder(false)