Jump to content

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

[How To] Programmatically Tile / Cascade windows


  • Please log in to reply
12 replies to this topic
SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

How to programmatically Tile / Cascade windows ?
http://www.autohotke...p?p=84807#84807

Foreword: If you are one those not been using the Tile / Cascade facility available in the taskbar, then try it now.
Minimize all windows and open two instances of windows explorer. Right-click on an empty area on the Taskbar and select Tile Windows Horizontally.. See the result!... Close those windows! This topic is about doing it from a script.

Posted Image


The search does not reveal much posts on the topic.
Tile Windows Vertically, how? Too difficult?
Tile Windows Horizontally/ Vertically With Only Left Click

There are two seperate functions: TileWindows and CascadeWindows in user32.dll which does the job, and even more.

Simple is the simple solution: You can bind any of these DllCalls to a Hotkey!

Tile windows vertically : DllCall( "TileWindows", uInt,0, Int,0, Int,0, Int,0, Int,0 )
Tile windows horizontally : DllCall( "TileWindows", uInt,0, Int,1, Int,0, Int,0, Int,0 )
Cascade windows : DllCall( "CascadeWindows", uInt,0, Int,4, Int,0, Int,0, Int,0 )

Take a look at the MSDN reference (if you are a techie), or just skip it and continue reading.


Taken from MSDN:

TileWindows Function

The TileWindows function tiles the specified child windows of the specified parent window.

WORD TileWindows(
HWND hwndParent,
UINT wHow,
RECT *lpRect,
UINT cKids,
const HWND *lpKids
);

hwndParent : [in] Handle to the parent window. If this parameter is NULL, the desktop window is assumed.

wHow : [in] Specifies tiling flags. This parameter can be one of the following values—optionally combined with MDITILE_SKIPDISABLED to prevent disabled multiple-document interface (MDI) child windows from being tiled. MDITILE_HORIZONTAL Tiles windows horizontally.
MDITILE_VERTICAL Tiles windows vertically.lpRect : [in] Pointer to a RECT structure that specifies the rectangular area, in client coordinates, within which the windows are arranged. If this parameter is NULL, the client area of the parent window is used.

cKids : [in] Specifies the number of elements in the array specified by the lpKids parameter. This parameter is ignored if lpKids is NULL.

lpKids : [in] Pointer to an array of handles to the child windows to arrange. If this parameter is NULL, all child windows of the specified parent window (or of the desktop window) are arranged.
Return Value

If the function succeeds, the return value is the number of windows arranged.
If the function fails, the return value is zero. To get extended error information, call GetLastError.[/list]Remarks : Calling TileWindows causes all maximized windows to be restored to their previous size.


CascadeWindows Function

The CascadeWindows function cascades the specified child windows of the specified parent window.

WORD CascadeWindows(
HWND hwndParent,
UINT wHow,
const RECT *lpRect,
UINT cKids,
const HWND *lpKids
);
wHow : [in] Specifies a cascade flag. This parameter can be one or more of the following values. MDITILE_SKIPDISABLED: Prevents disabled multiple-document interface (MDI) child windows from being cascaded.
MDITILE_ZORDER: Windows 2000/XP: Arranges the windows in Z order. If this value is not specified, the windows are arranged using the order specified in the lpKids array.
For other parameters, refer: TileWindows (above)[/list]Remarks :

By default, CascadeWindows arranges the windows in the order provided by the lpKids array, but preserves the Z-Order. If you specify the MDITILE_ZORDER flag, CascadeWindows arranges the windows in Z order.

Calling CascadeWindows causes all maximized windows to be restored to their previous size.

Minimum DLL Version : user32.dll, Minimum operating systems : Windows 95, Windows NT 4.0


You can also set the area where the windows should be Tiled / Cascaded. You can also pass the list of windows handles, and limit the tiling/cascading to a set of windows. But those parameters have to passed as pointers to an Array and a Structure. With some patience, I was able to understand InsertInteger() provided in the help file ( DllCall ), but I do not have the knowledge to explain it.

Instead,
I provide here the WinArrange.ahk written as a wrapper for those two functions. You may just include the wrapper in your script and call the WinArrange() function with much ease. Simply put, you call the WinArrange(), and it takes care of creating the RECT structure and the array of window handles.

WinArrange( TC=1, aStr="", VH=0x1, Rect="", hWnd=0x0 )  {
 CreateArray( aRect, Rect )                  ; Create a RECT structure.   
 IfEqual,   Rect,, SetEnv, lpRect, 0         ; determining whether lpRect is NULL  
 IfNotEqual,Rect,, SetEnv, lpRect, % &aRect  ; or a pointer to the RECT Structure.
 cKids := CreateArray( aKids, aStr )         ; Create an Array of window handles. 
 IfEqual,   aStr,, SetEnv, lpKids, 0         ; determining whether lpKids is NULL
 IfNotEqual,aStr,, SetEnv, lpKids, % &aKids  ; or a pointer to the array of handles.
If ( TC= 1 ) {                               ; then the windows have to be Tiled
 Return DllCall("TileWindows",Int,hWnd,UInt,VH,UInt,lpRect,Int,cKids,Int,lpKids)
} Else {                                     ; the windows have to be Cascaded
 IfNotEqual, VH, 4,  SetEnv,VH,0 ; If VH is 4, windows will be cascaded in ZORDER
 Return DllCall("CascadeWindows",Int,hWnd,UInt,VH,UInt,lpRect,Int,cKids,Int,lpKids)
}  }

CreateArray( ByRef Arr, aStr="", Size=4 ) { ; complicated variant of InsertInteger()
 IfEqual, aStr,,  Return 0 ; aStr will be a pipe delimited string of integer values.
 StringReplace, aStr, aStr, |, |, All UseErrorlevel ; Calculating the no. of pipes. 
 aFields := errorLevel + 1 ; errorlevel is no. of pipes, +1 results in no of fields.
 VarSetCapacity( Arr, ( aFields*Size ), 0 ) ; Initialise var length and zero fill it.
 Loop, Parse, aStr, |
   {
     Loop %Size%  
        DllCall( "RtlFillMemory", UInt, &Arr+[color=red](0 pOffset)[/color]+A_Index-1 ; Thanks to Laszlo
        , UInt,1, UChar, A_LoopField >> 8*(A_Index-1) & 0xFF )
     pOffset += %Size%
   } Return aFields
}

Here is an example that demonstrates the use of above wrapper. You can Copy / Paste & Try it but you require to download the wrapper first : Download : WinArrange.ahk

The following script opens two instances of Windows Explorer to allow ease copy/move tasks between folder/drives. You can control those two windows with the WinArrange() function provided in the wrapper. Exiting the script will close the windows.

#Persistent
OnExit, ExitRoutine

TILE         := 1                    ; for Param 1
CASCADE      := 2                    ; for Param 1
VERTICAL     := 0                    ; for Param 3
HORIZONTAL   := 1                    ; for Param 3
ZORDER       := 4                    ; for Param 3
CLIENTAREA   := "200|25|1000|700"    ; for Param 4

; ALLWINDOWS (Param 2), ARRAYORDER (Param 3), FULLSCREEN (Param 4) 
; are undeclared variables simulating NULL content.

Run C:\,,MAX
Sleep, 500
hWnd1 := WinExist("A")

Run D:\,,MAX
Sleep, 500
hWnd2 := WinExist("A")

ARRAY := hWnd1 "|" hWnd2 
WinArrange( TILE, ARRAY, VERTICAL, CLIENTAREA ) 

Return ; Auto-Execute section ends! ------------------------------------

^#F2::WinArrange( CASCADE, ARRAY, ZORDER, CLIENTAREA )
^#F3::WinArrange( TILE, ARRAY, HORIZONTAL, CLIENTAREA )
^#F4::WinArrange( TILE, ARRAY, HORIZONTAL, FULLSCREEN )
^#F5::WinArrange( TILE, ARRAY, VERTICAL, CLIENTAREA )
^#F6::WinArrange( TILE, ARRAY, VERTICAL, FULLSCREEN )

^#NumpadADD::
Loop, Parse, ARRAY, |
  WinActivate, ahk_id %A_LoopField%
Return

^#NumpadSub::
Loop, Parse, ARRAY, |
  WinMinimize, ahk_id %A_LoopField%
Return

ExitRoutine:
Loop, Parse, ARRAY, |
  WinClose, ahk_id %A_LoopField%
ExitApp
Return

#Include WinArrange.ahk ;  ---------------------------------------------

I have used anly two windows in the above example. With WinGet, Var, List creating a list of window handles is very easy. Combining it with ahk_class will give you many probabilities in arranging windows.

I am able see instances where these functions could prove useful.

One may have many instances of Internet Explorer and with the Cascade Windows facility, you can Cascade only the IE windows!
Reading the IE titlebar would allow easy window selection.

You might be alt-tab`ing hard between your script, .ini & other includes.
You can tile only those windows opened by notepad/other editor with a hotkey and view all those files in one go!

I guess, I am tired... Let me see if I can post a part 2 with more examples.

:)


kWo4Lk1.png

fsnow55
  • Members
  • 36 posts
  • Last active: Jun 07 2011 07:27 PM
  • Joined: 08 Jun 2006
SKAN, I'd been using variants of your programs, with a hotkey to tile
all windows or cascade them. I'd like to have a little more control
though, but don't know if it can be done:

1. tile only selected windows
Say there are 5 windows on the desktop. I'd like to tile just 2 of them (select with Control-LMB) ie. the 2 selected windows gets tiled on top of the other windows.


2. Cascade all windows based on a selected windows
size
Say there're 5 windows on the desktop. I resize one of them (thus it becomes the topmost window) and cascade all windows. All windows sizes will be the same size as the selected one, but cascaded behind it.

Thanks.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

1. tile only selected windows
Say there are 5 windows on the desktop. I'd like to tile just 2 of them (select with Control-LMB) ie. the 2 selected windows gets tiled on top of the other windows.


I do not get the idea. The exact facility is already available in XP atleast.
You may select any number of Windows from taskbar with Ctrl+LButton and right click and select Tile windows from Context menu

2. Cascade all windows based on a selected windows
size
Say there're 5 windows on the desktop. I resize one of them (thus it becomes the topmost window) and cascade all windows. All windows sizes will be the same size as the selected one, but cascaded behind it.


For custom cascading, we will have to use WinMove on every other window on Desktop..

Coco
  • Members
  • 697 posts
  • Last active: Oct 31 2015 07:26 PM
  • Joined: 27 Jul 2012
How do I tile or cascade child windows only? I have a script with child windows and I would like to be able to tile or cascade them?

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

I have a script with child windows


I do not understand. Even edit/text controls are child windows.. Can you please elaborate?

Coco
  • Members
  • 697 posts
  • Last active: Oct 31 2015 07:26 PM
  • Joined: 27 Jul 2012

I have a script with child windows


I do not understand. Even edit/text controls are child windows.. Can you please elaborate?


Oh sorry, what I mean is, I have several GUI's with the "+ParentAnotherGUI" option. So they're basically inside the Main GUI. Is it possible to tile or cascade them using your function? I tried it but I can't seem to get it to work.

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005

they're basically inside the Main GUI.


In this topic, I gave picture controls title-bar and was able to tile them with a single DllCall().

Posted Image

I will get a better idea if you can post a short snippet involving a few GUIs.

Coco
  • Members
  • 697 posts
  • Last active: Oct 31 2015 07:26 PM
  • Joined: 27 Jul 2012
Here are the functions that I am currently using to simulate tiling and cascading. It works but it's not as "snappy" as the WinArrange() function.

WinPosTile(hParent, cIndex, cCount, ByRef x, ByRef y, ByRef w, Byref h) ; [color=#FF0000]hParent=handle of the parent GUI, cIndex=index of the[/color]
{                                                                                                         ; [color=#FF0000]child window, cCount=number of child windows[/color]
	cols := Ceil(Sqrt(cCount)), rows := Ceil(Sqrt(cCount))
	if (cols*(rows-1) >= cCount)
		rows-- 
	WinGetPos,,, wGrid, hGrid, ahk_id %hParent%
	pos_Cols := ( C := Mod( cIndex, cols ) ) ? C : cols, pos_Rows := Ceil( cIndex / cols ) ; thanks to SKAN for this tip
	wGrid := wGrid / cols, hGrid := hGrid / rows
	w := wGrid, h := hGrid, x := (wGrid/2) - (w/2) + (wGrid*(pos_Cols-1)), y := (hGrid/2)-(h/2) + (hGrid*(pos_Rows-1))
}

WinPosCascade(hParent, cIndex, cCount, ByRef x, ByRef y, ByRef w, ByRef h, Offset="25") ; [color=#FF0000]Parameters same as above function[/color]
{
	WinGetPos,,, wParent, hParent, ahk_id %hParent%
	w := wParent - (Offset*(cCount-1)), h := (hParent-100) - (Offset*(cCount-1)), x := Offset * (cCount - cIndex), y := Offset * (cCount - cIndex)
	return
}

WinTile()
{
	global SubID, hWork ; [color=#FF0000]SubID is an array containing the Hwnd of the child windows, hWork is the Hwnd of the parent window[/color]
	i := SubID.MaxIndex()
	for k, v in SubID
	{
		WinPosTile(hWork,  k, i, x, y, w, h)
		WinMove, ahk_id %v%,, %x%, %y%, %w%, %h%
	}
}

WinCascade() ; [color=#FF0000]SubID is an array containing the Hwnd of the child windows, hWork is the Hwnd of the parent window[/color]
{
	global SubID, hWork
	i := SubID.MaxIndex()
	for k, v in SubID
	{
		WinPosCascade(hWork,  k, i, x, y, w, h)
		WinMove, ahk_id %v%,, %x%, %y%, %w%, %h%
	}
}


Coco
  • Members
  • 697 posts
  • Last active: Oct 31 2015 07:26 PM
  • Joined: 27 Jul 2012
Got it working now, I used your DllCall solution. I did some research at MSDN a few days back regarding this, and I always assumed that I have to provide the "lpRect" parameter for which I do not know how. The parameter is optional and I need not provide it all the while. Many thanks Skan. :D

Maverick5223
  • Members
  • 2 posts
  • Last active: May 03 2013 07:38 PM
  • Joined: 01 May 2013

Hello,

 

Can you copy and paste the wrapper along with the cascade and tile scripts into one .ahk file? I want to ask before I test it. Also I am using Win7 OS

 

Thanks,

 

Jason



wilcofan
  • Members
  • 10 posts
  • Last active: Mar 22 2016 03:37 PM
  • Joined: 17 Jan 2014
DllCall( "RtlFillMemory", UInt, &Arr+[color=red](0 pOffset)[/color]+A_Index-1 ; Thanks to Laszlo

 

I'm getting an error when I run this.  Unsupported Method.  Any ideas?

 

Win7 32bit.  AHK 1.1.19-ish

 

 



Jackie Sztuk _Blackholyman
  • Spam Officer
  • 3757 posts
  • Last active: Apr 03 2016 08:47 PM
  • Joined: 28 Feb 2012

I'm getting an error when I run this. Unsupported Method. Any ideas?

Win7 32bit. AHK 1.1.19-ish

you need to Remove the [color] tags from the code
Helping%20you%20learn%20autohotkey.jpg?d

[AHK] Version. 1.1+ [CLOUD] DropBox ; Copy [WEBSITE] Blog ; About

wilcofan
  • Members
  • 10 posts
  • Last active: Mar 22 2016 03:37 PM
  • Joined: 17 Jan 2014

thank you very much!  did not notice the tags there