AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Windows-7 Like Window Positioning for XP and Vista
Goto page 1, 2, 3, 4, 5, 6  Next
 
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
PatrickS



Joined: 21 Mar 2010
Posts: 10

PostPosted: Sun Mar 21, 2010 3:43 am    Post subject: Windows-7 Like Window Positioning for XP and Vista Reply with quote

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.

Code:
;; -----------------------------------------------------------------------
; 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()
Back to top
View user's profile Send private message
Learning one



Joined: 04 Apr 2009
Posts: 1000
Location: Croatia

PostPosted: Sun Mar 21, 2010 8:24 am    Post subject: Reply with quote

Nice. Maybe to add Win__FullSize()
Code:
Win__FullSize()
{   
   Win__GetDesktopPos(X, Y, W, H)
   WinMove, A,, X, Y, W, H
}
Back to top
View user's profile Send private message Visit poster's website
guest3456
Guest





PostPosted: Fri Mar 26, 2010 12:46 pm    Post subject: Reply with quote

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



Joined: 21 Mar 2010
Posts: 10

PostPosted: Sat Mar 27, 2010 2:38 am    Post subject: Reply with quote

guest3456 wrote:
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.
Back to top
View user's profile Send private message
Mr Fish



Joined: 26 Mar 2010
Posts: 3
Location: Denmark (get your legos here)

PostPosted: Sat Mar 27, 2010 1:41 pm    Post subject: Reply with quote

Very nice script for people with big monitors (me).

I added support of 1/4 window support.

Code:
;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()
Back to top
View user's profile Send private message AIM Address
Bluespianist



Joined: 27 Mar 2010
Posts: 10

PostPosted: Sat Mar 27, 2010 4:32 pm    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
vahju



Joined: 17 Feb 2008
Posts: 296

PostPosted: Sun Mar 28, 2010 1:49 am    Post subject: Reply with quote

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.

Code:
; 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.
Back to top
View user's profile Send private message
PatrickS



Joined: 21 Mar 2010
Posts: 10

PostPosted: Sun Mar 28, 2010 10:36 am    Post subject: Version 2.0 Reply with quote

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...

Code:
;; -----------------------------------------------------------------------
;; 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.

Code:

!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")

Back to top
View user's profile Send private message
DeRoc



Joined: 04 Jul 2009
Posts: 9
Location: SF Bay Area

PostPosted: Sun Mar 28, 2010 7:00 pm    Post subject: Reply with quote

This is VERY nice - Thanks! Razz

But don't forget "Numpad0", too.
Code:
!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
   }
}
Back to top
View user's profile Send private message
Mr Fish



Joined: 26 Mar 2010
Posts: 3
Location: Denmark (get your legos here)

PostPosted: Mon Mar 29, 2010 12:30 pm    Post subject: Reply with quote

DeRoc wrote:
This is VERY nice - Thanks! Razz

But don't forget "Numpad0", too.
Code:
!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 Smile Edit: Really like this feature by the way.

Very nice work PatrickS.
_________________
Computer janitor with a future in burger flipping.
Back to top
View user's profile Send private message AIM Address
DeRoc



Joined: 04 Jul 2009
Posts: 9
Location: SF Bay Area

PostPosted: Mon Mar 29, 2010 7:25 pm    Post subject: Reply with quote

Good suggestion, Mr Fish
....shuffling numbers now... Wink
Back to top
View user's profile Send private message
PatrickS



Joined: 21 Mar 2010
Posts: 10

PostPosted: Tue Mar 30, 2010 1:52 am    Post subject: Are you ready for version 3?? Reply with quote

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. Sad

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
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
Code:

;; -----------------------------------------------------------------------
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

Code:

;; -----------------------------------------------------------------------
;; 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)

Code:

;; -----------------------------------------------------------------------
;; 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.

Code:

#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

Back to top
View user's profile Send private message
Mr Fish



Joined: 26 Mar 2010
Posts: 3
Location: Denmark (get your legos here)

PostPosted: Tue Mar 30, 2010 12:28 pm    Post subject: Reply with quote

Masterful documentation mate, great work.
_________________
Computer janitor with a future in burger flipping.
Back to top
View user's profile Send private message AIM Address
DeRoc



Joined: 04 Jul 2009
Posts: 9
Location: SF Bay Area

PostPosted: Wed Mar 31, 2010 9:52 pm    Post subject: Reply with quote

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! Shocked

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 Laughing
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!)
Back to top
View user's profile Send private message
PatrickS



Joined: 21 Mar 2010
Posts: 10

PostPosted: Sun Apr 04, 2010 10:07 pm    Post subject: Update: Automatic Window Gridding -- Win__Gridder() Reply with quote

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
Code:
; 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
Code:
#PrintScreen::   Win__Gridder(true)
+#PrintScreen::   Win__Gridder(false)
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2, 3, 4, 5, 6  Next
Page 1 of 6

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group