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 

Dock'nDrag - Docks Two Windows Together for Move and Resize
Goto page 1, 2  Next
 
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
DAT



Joined: 01 Dec 2008
Posts: 49
Location: UK

PostPosted: Fri Feb 06, 2009 10:40 am    Post subject: Dock'nDrag - Docks Two Windows Together for Move and Resize Reply with quote

Note added 07-02-2009: Instructions and screen shots available at http://docs.google.com/Doc?id=dccck29f_0ghgsjtmh.

I got the idea for this script because my primary monitor isn’t big enough to have the AHK ScITE editing and Help windows wide enough at the same time when side-by-side. Now I just dock the two windows together and drag the dividing line back and forth as needed. Anyway that’s what got me started on trying to make a script and here it is in case anyone else finds it useful.

I call it Dock’nDrag because you can dock two windows side-by-side (or one above the other) after which you can drag and resize them as a pair. I use WindowPad by Lexikos all the time anyway so it was natural to use CapsLock as modifier and Left-Drag for moving and resizing. Dock’nDrag doesn’t replace WindowPad but I think it complements it quite well.

Plenty of scripts do dragging and resizing but I’ve not seen any that do docking. I cribbed ideas and code from scripts like ‘Easy Window Dragging - KDE style’ by Jonny, and NiftyWindows (both in Script Showcase). A couple of things I felt were missing I’ve added in. When you’re stretching a window in any direction, once it’s reached maximum size the whole window starts moving. In EWD this happens only when you’re pulling upward or to the left but not downward or to the right. Also I added min and max size limits because some applications crash if you accidentally make their window zero-width (Directory Opus for example).

To make the docking as intuitive as the drag and resize part I used CapsLock+RightClick for the ‘Dock’ and ‘Undock’ commands. A CapsLock+RightClick on any of the four sides of a window makes it grab the nearest other window that’s on the same side, and on the same monitor. Do the same in the centre of either window, and it releases the dock. If you imagine a 3-by-3 grid superimposed on each window, it’s the cells in the grid that you have to click in. The docking cells are the middle ones along each edge.

When you dock two nearby windows, the edge that you click in stays fixed but both windows expand out sideways to the full width of the monitor. The height of the ‘slave’ adjusts to match the master. (For vertical docking it’s the same but height become width, etc.). After docking, when you move or resize either window, the other one moves as well but they stay linked along one edge and their total width remains the monitor width. This seems to work out quite well. A fun way to swap the positions of two side-by-side screens is to move one of them around the other in three stages by docking it to each side in turn.

There are three options for selecting the window to grab. I prefer ‘Edge’ which selects the one whose appropriate edge is nearest but I’ve left in two experimental options in case anyone wants to try them. ‘Area’ grabs the window with the greatest area and ‘Centre’ the one whose centre is nearest to the centre of the master.

The script works fine on my XP and Vista laptops and it supports multiple monitors, although the most I’ve tried is three arranged side-by-side. While experimenting I found it useful to have a hotkey that would force an ExitApp. Otherwise if the script caused an error message it would leave CapsLock permanently on making it awkward to terminate from Task Manager. It hasn’t crashed for quite a while now but the panic button is still there as CapsLock+RControl. That’s ok for me because I never use the right-hand control key.

To share CapsLock as the hotkey modifier for both Dock’nDrag and WindowPad, I found I had either to make sure to load WP first and DnD second, or to alter the ini file for WP. When I load WP after DnD, it seems to take over CapsLock completely and it won’t work for DnD. However this restriction goes away if you change the WindowPad ini file slightly. All I had to do was this:

1. Delete or comment out the line “CapsLock = Hotkeys, Active Window (WADS)”.
2. Move the entries that were under [Hotkeys: Active Window (WADS)] and put them under [Hotkeys]. (Leave the originals in place if you prefer as they won’t affect anything).
3. Then edit all the new entries under [Hotkeys] by adding “Capslock & ” [note the two spaces] immediately before each of the hotkey characters. So for example, “z = WindowPadMove, -1, +1, 0.5, 0.5” becomes “Capslock & z = WindowPadMove, -1, +1, 0.5, 0.5”.

There’s also a feature in Dock’nDrag that other Directory Opus users might find useful. Copernic Desktop Search and Picasa both ignore the fact that I’ve set Directory Opus as my default folder browser. So as a workaround I added subroutine DoExplorer. Now if Explorer opens instead of DOpus, all I have to do is Caps-Lock+Right Click in the left-hand third of Explorer’s Title Bar – the part that’s in the top left-hand square of the grid - and Explorer shuts down and the folder reopens in DOpus. (Thanks to Andreone’s function ‘IsOverTitleBar’). There’s still cope to trigger else from the right-hand side of a Title Bar.

I use Dock’nDrag everyday and getting it to work taught me a lot, but it will be especially nice if someone else finds it useful as well. Apologies for all the syntax bloomers like unnecessary brackets. Also the coding style is terribly inconsistent. However at least it kind of acknowledges my debt to all those generous forum contributors who may be able to recognise their bits of code that I’ve gratefully ‘recycled’.
Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 'Dock'nDrag'
; By David Tong 03-02-2009.
;
; Primary Functions:
;  1. CapsLock+left-drag from the central rectangle to move a window without resizing it.
;  2. CapsLock+left-drag a window by corner or side to resize it. Select either '‘Easy Window
;     Dragging' or 'NiftyWindows' modes of dragging and resizing. 
;  3. CapsLock+rightclick in centre of the strip along an edge to 'dock' a nearby window to it
;     Afterwards both windows move or resize as one. Do the same in centre of either window
;     to uncouple. [Great for using a Help window at same time as AHK ScITE editor].
;  4. Works with multiple monitors. Boundary limits ensure a coupled window is not stranded on
;     a non-existent monitor.
;  5. Max width (height) of a coupled window is that of the monitor. Minimum for either partner
;     is set by a parameter.
;  6. When stretching a window in any direction, after it reaches maximum size the whole
;     window starts moving. This is more ergonomic than EWD or NiftyW which do this only when
;     pulling up or to the left, but not down or to the right.
;  7. Supports multiple monitors and ensures partnered window is not accidentally left
;     stranded on a non-existent monitor.
;  8. Tested in XP and Vista.

;  Extra Functions:
;  1. CapsLock+Leftclick in left-hand side of title bar of Windows Explorer closes it and opens
;     same folder in Directory Opus. (Useful because Picasa and Copernic Desktop Search
;     don't respect default browser settings).
;  2.  Keeps CapsLock off to avoid typing in caps by mistake. [No longer done as standard]
;  3.  As a precaution while testing I included CapsLock+RControl as a hotkey to force an exit
;      if the script becomes unresponsive but with CapsLock activated.

; Acknowledgements:
; Basic window dragging functions inspired by ‘Easy Window Dragging - KDE style’
; by Jonny, and NiftyWindows (both in Script Showcase) and similar variants.
; I cribbed lots of code from the many great scripts on the AHK forum, in particular
; WindowPad by Lexikos. Most thanks of all of course to Chris M. for developing
; AutoHotkey in the first place.

SetBatchLines , 20ms ; If too low, non-dragged windows take too long to redraw.
SetWinDelay , 10     ; If too low the dragged window leaves too much of a trail.

#NoEnv
SendMode Input 
SetWorkingDir %A_ScriptDir%
#NoTrayIcon
#SingleInstance , ;ignore

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Set up user options:
;
; Define minimum width limit for windows.
   MinDimx := 200   ;Min window width 
   MinDimy := 200   ;Min window height

; Define the criterion for selecting a partner window when coupling two together.
; In all cases only visible windows on the same monitor are selected, and only those
; whose centres are on the same side of target centre as the side of target that
; you click on.
;
;     'Area' chooses the window with greatest area.
;   'Centre' chooses the window whose centre is nearest to the centre of target window.
;   'Edge' [recommended] Selects the one whose edge-to-be-shared is nearest to the
;   edge you clicked.

   Criterion := edge   
   
; If DragMode=1, each edge has three clickable sections allowing dragging by side or corner
; like on a normal window.  (Same mode as used in NiftyWindows).
; If DragMode<>1, four clickable quadrants giving corner-dragging modes only as in ‘Easy
; Window Dragging - KDE style’.
   
    DragMode = 1  ; set to 1 or 0 (or blank) as desired
   
; Define the hotkeys:

    Modifier := "CapsLock `& " ; Other examples: "!", "+", "Insert `& ", etc.
    DragButton := "LButton"
    DockButton := "RButton"
    EmergencyButton := "RControl"

; End of user setup section
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;
; Might want to alter the next two lines and CheckCapslock: if using Insert, ScrollLock, etc
IfInString , Modifier, CapsLock
        Settimer, CheckCapslock, 200
       
FullDragButton = %Modifier%%DragButton%
FullDockButton = %Modifier%%DockButton%
FullEmergencyButton = %Modifier%%EmergencyButton%

Hotkey , %FullDragButton% , Resizer
Hotkey , %FullDockButton% , WinDock
Hotkey , %FullEmergencyButton% , Finish
Return

;;;;;;;;;;;;;;;;;;;;
Finish:  ; Used for emergency exit if script stalls with CapsLock activated.Gosub , CheckCapslock
Gosub , CheckCapslock
SoundBeep 2000, 500
ExitApp
;;;;;;;;;;;;;;;;;
; Make sure Capslock is not left in an activated state.
CheckCapslock:
GetKeyState , caps , CapsLock , T ; 'D' means key is 'on', 'U' means 'off'.
IfEqual , caps , D
   {
      sleep 20      ; used to use 100
      SetCapsLockState , OFF   
   }
Return
;;;;;;;;;;;;;;;;;;;;;;;;



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Combines two windows as a pair so they can be moved and resized as one.
;
WinDock:
; Action depends on which of the nine cells (in a 3x3 grid) you clicked in.
; Top left-hand cell is row 1, column 1. Bottom right is 3,3.
;    -----------------
;   | 1,1 | 1,2 | 1,3 |
;    -----------------
;   | 2,1 | 2,1 | 2,3 |
;    -----------------
;   | 3,1 | 3,2 | 3,3 |
;    -----------------

; Click in cell:
;   1,2 to couple another window to top edge.
;   3,2 to couple another window to bottom edge.
;   2,1 to couple another window to lefthand edge.
;   2,3 to couple another window to righthand edge.
;   2,2 to uncouple a coupled pair.
;   1,1 and within title bar of Windows Explorer to close Explorer and open
;       same folder in Directory Opus.

CoordMode , Mouse , Screen   
MouseGetPos ,xx , yy , ID1, Ctrl, 1   ; defines window that you clicked in

; Next command required else first right-click on desktop gives
; context menu with some items missing (those for Directory Opus).
WinActivate , ahk_id %ID1% 

;  Ignore clicks on desktop
WinGetClass , clss, ahk_id %ID1%
If clss = Progman   
    Return
Gosub , ClearContextPopup  ; Clear unwanted right-click pop-up menus

; Find which cell in the target window was clicked in.
Get_Cell(Column,Row) 


; Store a shortlist of acceptable windows in Item1, Item2, etc
WinGetPos , My_x, My_y, My_w, My_h , ahk_id %ID1%  ;details for current window
My_mon := GetMonitorAt(My_x+My_w/2, My_y+My_h/2) ; Using which monitor?
Gosub , CalcMonStats    ; loads My_monRight, etc
GoSub, Sort           

; Do work as defined by the cell.

; LatVert = +1 defines lateral coupling, +1 defines vertical (also used in Resizer).
; LftRght = +1 for coupling to right (or up) and -1 for left (or down)

If (Column=1 and Row=1)
{
    If IsOverTitleBar(xx, yy, ID1)
        Gosub, DoExplorer
}
Else If (Row=2 and Column=2)
{
    If (ID1 = Master) or (ID1 = Slave)
        Gosub, UnCouple
    Return
}
Else If (Column=1 and Row=2)
{
    LatVert := 1                        ; lateral
    LftRght := -1                       ; to left
    Winid := FindWin(LatVert, LftRght)  ; returns selected id from the list.
    IfEqual , Winid ,, Return           ; skip if no suitable window
    Gosub , Coupled                     ; announce coupled state
   
   ; Master - leave gap for partner and extend other edge to monitor limit
    newmx := My_x
    If (My_x < (My_monLeft+MinDimx))      ;NB outer brackets are essential here!
        newmx := My_monLeft+MinDimx   
    newmw := My_monRight-newmx 

    ; Partner - move to gap and match height to Master.
    newpx := My_monLeft
    newpw := Newmx-My_monLeft 
    WinMove , ahk_id %Master% ,, %newmx% ,, %newmw% , 
    WinMove , ahk_id %Winid% ,, %newpx% ,My_y, %newpw% , My_h 
    Return
}
Else If (Column=3 and Row=2)
{
    LatVert := 1                        ; lateral 
    LftRght := 1                        ; to right
    Winid := FindWin(LatVert, LftRght)  ; returns selected id from the list.
    IfEqual , Winid ,, Return           ; skip if no suitable window
    Gosub , Coupled                     ; announce coupled state
   
   ; Master - leave gap for partner and extend other edge to monitor limit
    newmx := My_monLeft
    newmw := My_w+(My_x-My_monLeft) 
    If (My_monRight-(My_x+My_w)) < MinDimx
        newmw := MaxDimx-MinDimx
       
    ; Move Partner into gap and match height to Master.
    newpx := My_monLeft+newmw     ; newmw value on right refers to main window
    newpw := MaxDimx-newmw    ; same here
    WinMove , ahk_id %Master% ,, %newmx% ,, %newmw% ,
    WinMove , ahk_id %Slave% ,, %newpx% , My_y, %newpw% ,My_h 
    return
}
Else If (Row=1 and Column=2)
{
    LatVert := -1                       ; vertical
    LftRght := +1                       ; upward
    Winid := FindWin(LatVert, LftRght)  ; returns selected id from the list.
    IfEqual , Winid ,, Return           ; skip if no suitable window
    Gosub , Coupled                     ; announce coupled state
   
   ; Master - leave gap for partner and extend other edge to monitor limit
    newmy := My_y
    If (My_y<MinDimy)
        newmy := MinDimy   
    newmh := MaxDimy-newmy
   
    ; Move Partner into gap and match height to Master.
    newpy := My_monTop
    newph := MaxDimy-newmh
    WinMove , ahk_id %Master% ,,  ,%newmy%,  , %newmh%
    WinMove , ahk_id %Slave% ,,%My_x%,%newpy%, %My_w% ,%newph% 
    Return
}
Else If (Row=3 and Column=2)
{
    LatVert := -1                       ; vertical
    LftRght := -1                       ; downward
    Winid := FindWin(LatVert, LftRght)  ; returns selected id from the list.
    IfEqual , Winid ,, Return           ; skip if no suitable window
    Gosub , Coupled                     ; announce coupled state

    ; Master - leave gap for partner and extend other edge to monitor limit
    newmy := My_monTop
    newmh := My_h+ My_y       
    If (MaxDimy-newmh)<MinDimy
        newmh := MaxDimy- MinDimy 
       
    ; Move Partner into gap and match height to Master.
    newpy := -(My_monTop - newmh)     
    newph := MaxDimy-newmh
    WinMove , ahk_id %Master% ,,  ,%newmy%,  , %newmh%
    WinMove , ahk_id %Slave% ,,%My_x%,%newpy%, %My_w% ,%newph% 
    Return
}
Return
;;;;;;;;;;

;;;;;;;;;;
; Clears spurious Right-Click menus
;
;  Not so easy to find a method that works with all windows. For example,
;  sending 'ControlSend,,{ESC}, ahk_id %ID1%' to Word2003 makes it crash.
;  This seems ok with everything so far:
;
ClearContextPopup:
WinActivate , ahk_id %ID1% 
WinWaitActive , ahk_id %ID1%
sleep ,100
send , {alt}
sleep ,100
send , {esc}
Return
;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;
; Closes an open Explorer window and re-opens same folder in DOpus as a new tab. 
; (Useful with Picasa and Copernic which insist on using Explorer instead of DOpus).

DoExplorer:
WinGet, Prcs , ProcessName, ahk_id %ID1%
WinGetTitle , Ttle, ahk_id %ID1%
If Prcs contains explorer
{
    Ttle1 = "%Ttle%" ; DOpus command line wants it in quotes
    run , "C:\Program Files\GPSoftware\Directory Opus\dopusrt.exe" /CMD Go %Ttle1% Newtab
    WinClose , ahk_id %ID1%
}
Return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;
; Get work area (excludes taskbar-reserved space).
; Copied from WindowPad. Modified to suit DAT work area
CalcMonStats:
; Get work area less a guard-band to minimise accidental triggering of
; popup toolbars on three sides of main monitor
SysGet, My_mon, MonitorWorkArea, %My_mon%
IfEqual , My_mon , 1
{
    My_monRight := My_monRight  ;- 25
    My_monTop := My_monTop      ;+ 25
    My_monLeft := My_monLeft    ;+ 25
}
MaxDimx  := My_monRight - My_monLeft
MaxDimy := My_monBottom - My_monTop
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;
; Returns the id of a window that meets the selection criteria.
; LatVert = +1 means 'lateral' and -1, 'vertical' coupling
;
; LftRght defines direction, seen from centre of Master window, in which
; to look for a target window.
; If LatVert=+1: LftRght = +1 means look right and -1 means look left
; If LatVert=-1, LftRght = +1 means look up and -1 means look down.

; For example: If LatVert=+1 and LftRght = +1, a window will be selected
; whose right edge [see note] is nearest to the left edge of the Master
; (in either direction), and whose centre is to the right of
; the centre of the Master.
;
; Note: Instead of selecting by 'edge' you can select on 'area' or
; 'centre' by setting the variable 'Criterion').

FindWin(LatVert, LftRght) 
{
    global ID1, Item, Item1, Item2, Item3, Item4, Item5, Criterion ; Assumes max of 5 candidates.
   
    WinGetPos , My_x, My_y, My_w, My_h , ahk_id %ID1%

    ;  Discard unsuitable windows and store parameters for shortlist candidates
    {
        My_centrex := (My_x+My_w/2)
        My_centrey := (My_y+My_h/2)   ; missed off until 24-01-2009 12:18
        index := ""         ; count selected windows
        Loop , %Item%       
            {
                this_Item := Item%A_Index% 
                WinGetPos , this_x, this_y, this_w, this_h , ahk_id %this_Item%
                This_centrex := (this_x+this_w/2)
                This_centrey := (this_y+this_h/2)
               
                If ((LatVert = +1)
                    and ((-LftRght+1)/2*this_centrex + (LftRght+1)/2*My_centrex)
                        >((LftRght+1)/2*this_centrex + (-LftRght+1)/2*My_centrex))
                or ((LatVert = -1)
                    and ((LftRght+1)/2*this_centrey + (-LftRght+1)/2*My_centrey)
                        >((-LftRght+1)/2*this_centrey + (LftRght+1)/2*My_centrey))       
                    {
                        Item%A_Index% := ""     ; wrong side of my window so discard
                        continue               
                    }
                index++                     
                Item%A_Index% := ""     
               
                If (LatVert = +1) {
                    EdgeSep := Abs(This_x - My_x + (-LftRght+1)/2*This_w - (LftRght+1)/2*My_w)   
                    CentreSep := Abs(My_centrex-this_centrex)    ; Centre-to-centre separation 
                }
                If (LatVert = -1) {
                    EdgeSep := Abs(This_y - My_y + (LftRght+1)/2*This_h - (-LftRght+1)/2*My_h)
                    CentreSep := Abs(My_centrey-this_centrey)    ; Centre-to-centre separation
                }
                Area := (this_w * this_h)                   ; area
                Item%index% = %this_Item%%A_Space%%CentreSep%%A_Space%%Area%%A_Space%%EdgeSep% ; store results
            }
    }
    ;  Select the best candidate
    Item:= index        ; number of candidates 
    Dmin := 10000       ; Start high. Will drop to lowest value after loop
    Dmax := 0           ; Start at zero. Will rise to higher value after loop
    Loop , %Item%       
        {
            this_Item := Item%A_Index%
            StringSplit , string , this_Item, %A_Space%
            If (Criterion = edge)  {     ; select on edge-to-edge separation
                If (string4 < Dmin)  {
                    Dmin := string4
                    Item1 := string1
                    }
                    Continue
                }
            If (Criterion = area)  {      ; select for largest area
                If (string3 > Dmax)  {
                    Dmax := string3
                    Item1 := string1
                    }
                    Continue
                }   
            If (Criterion = centre) {    ; select for nearest centre
                If (string2 < Dmin)  {
                    Dmin := string2
                    Item1 := string1
                    }
                    Continue
                }   
        }
    Return, Item1                       ; returns id of chosen window
}
;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;
Sort:
; Lists other windows on same monitor that have style 0x16CF0000 or 0x14CF0000. 
;
WinGet, Item, list,,, Program Manager   
index := ""                             ; count number in selection
Loop, %Item%                            ;  loop thro' all current windows
{
    this_Item := Item%A_Index%  ; Item1 contains id of first window, etc
    WinGet, this_style , style , ahk_id %this_Item% ; get 'Style' of the window
    If ((this_Style <> 0x16CF0000) and (this_Style <> 0x14CF0000)) or (this_Item = ID1)
    {
        Item%A_Index% := ""     
        continue                        ; wrong style or is Master window
    }
    WinGetPos , this_x, this_y, this_w, this_h , ahk_id %this_Item%
    this_mon := GetMonitorAt(this_x+this_w/2, this_y+this_h/2) ; which monitor?
    If  (this_mon <> My_mon)
        {
        Item%A_Index% := ""     
        continue                        ; window is on wrong monitor
        }
    index++                             ; increment pointer
    Item%index% = %this_Item%       
}
    Item:= index    ; store revised total of windows
Return

;;;;;;;;;;;;;;;;;;;;;
; Determine which cell the mouse is in. Use a 3x3 grid.  Columns are 1,2,3 (top to
; bottom) and rows are 1,2,3 (left to right).

Get_Cell(ByRef Column, ByRef Row)
{
    CoordMode, Mouse, Screen
    MouseGetPos,X1,Y1,id
    WinGet,Win,MinMax,ahk_id %id%
    If Win
        return
    ; Get the initial window position and size.
    WinGetPos, WinX1, WinY1, WinW, WinH, ahk_id %id%

    Div := 3  ; e.g. Div=4 makes outer cells 1/4 as wide as the window.
    PW := Abs(WinW/Div)
    PH := Abs(WinH/Div)
    If ((WinX1 < X1) and (X1 < WinX1 + PW))
        Column := 1
    If ((WinX1 + PW < X1) and (X1 < WinX1 + WinW - PW))
        Column := 2
    If ((WinX1 + WinW - PW < X1) and (X1 < WinX1 + WinW))
        Column := 3
       
    If ((WinY1 < Y1) and (Y1 < WinY1 + PH))
        Row := 1
    If ((WinY1 + PH < Y1) and (Y1 < WinY1 + WinH - PH))
        Row := 2
    If ((WinY1 + WinH - PH < Y1) and (Y1 < WinY1 + WinH))
        Row := 3         
    Return
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Called when two windows first couple together
Coupled:
    Master :=  ID1  ; Resizer uses this to see if it's dealing with a Master window.
    Slave := winid
    Coupled := 1
    ; Promote partner to number two window. Avoids having a third window sandwiched
    ; between Master and partner.  Also makes both windows blink when coupled.
    WinActivate , ahk_id %Slave%   
    WinActivate , ahk_id %Master%
    #Persistent
    ToolTip , Linking On
    SetTimer , TooltipOff , 1000
    SoundBeep, 800, 25 
    SoundBeep, 1200,  25 
Return

; Called when two windows uncouple
UnCouple:
    If (Coupled = 0)
        Return
    Master := ""
    Slave := ""     
    Coupled := 0
    #Persistent
    ToolTip , Linking Off
    SetTimer , TooltipOff , 1000
    SoundBeep, 1200,  25 
    SoundBeep, 800,  25 
Return

; Clear tooltip
TooltipOff:
    SetTimer , TooltipOff , Off
    ToolTip
Return
;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Resizer:
    ; Get the initial mouse position, target window info, monitor limits
    CoordMode, Mouse, Screen           
    MouseGetPos,KDE_X1,KDE_Y1,KDE_id
    WinGetPos,KDE_WinX1,KDE_WinY1, DimMx, DimMy,ahk_id %KDE_id%
    My_mon := GetMonitorAt(KDE_WinX1+DimMx/2, KDE_WinY1+DimMy/2) ; From WindowPad by Lexikos
    Gosub , CalcMonStats    ; gets My_monRight, etc
    WinActivate , ahk_id %KDE_id%

    ; Set flag to show window is part of a coupled pair
    OneOfPair := (Coupled = 1) and ((KDE_id = Master) or (KDE_id = Slave)) ? 1:0
    ; Uncouple if either window has been minimised, maximised, or closed.
    WinGet,SGone,MinMax,ahk_id %Slave%   
    WinGet,MGone,MinMax,ahk_id %Master%   
    If (OneOfPair = 1) and ((SGone <> 0) or (MGone <> 0)) {
        OneOfPair := 0
        Gosub , UnCouple
    }   
    ; Abort if selected window is full-screen or minimised
    WinGet,KDE_Win,MinMax,ahk_id %KDE_id% 
        IfNotEqual , KDE_Win, 0 , Return   

    ; If one of a couple, make sure both are in consecutive z-order.
    ; Avoids a third window being sandwiched between a coupled pair. Also
    ; indicates paired state by making both task bars blink when selected by
    ; this routine.
    If (Coupled = 1) and (KDE_id = Master) {
            WinActivate , ahk_id %Slave%
            WinActivate , ahk_id %Master%
            }
    If (Coupled = 1) and (KDE_id = Slave) {
            WinActivate , ahk_id %Master%
            WinActivate , ahk_id %Slave%
            }
    ; If clicking in what was previously the Slave, swap Master and Slave
    If (Coupled = 1) and (Slave = KDE_id)
        Swap(Master, Slave)

    WinGetPos,KDE_P_x,KDE_P_y, DimPx, DimPy,ahk_id %Slave%  ; used during limit checks

    ; Find which of the nine cells in the target window received the click.
    Get_Cell(Column,Row) 
    If (Column=2) and (Row=2)   ; Centre cell, so do move without resizing.
    {
        Loop   
        {
         GetKeyState,KDE_Button,%DragButton% ,P    
            If KDE_Button = U               ;  Abort if button was released.
                break
            MouseGetPos,KDE_X2,KDE_Y2       ; current mouse position.
            KDE_X2 -= KDE_X1                ; offset from  initial position.
            KDE_Y2 -= KDE_Y1           
            newx := (KDE_WinX1 + KDE_X2)    ; Apply offset to window
            newy := (KDE_WinY1 + KDE_Y2)
                       
            ; If Partner exists, stop dragging before it leaves valid monitor
            If (OneOfPair = 1)
            {
                Mon_P := GetMonitorAt(KDE_P_x + KDE_X2+ DimPx/2, KDE_P_y+ KDE_Y2+ DimPy/2, 0)
                IfEqual , Mon_P, 0, Continue     
                WinMove,ahk_id %Slave%,, KDE_P_x + KDE_X2, KDE_P_y + KDE_Y2
            }   
            WinMove,ahk_id %KDE_id%,, newx, newy
        }
        return
    }
    Else    ; Do resizing
    {     
        ; Use the quadrants, North-West, NE, SE, and SW to set the basic
      ; directions in which the Master window can be dragged. These are
        ; refined later according to which of the eight peripheral cells was clicked.
       
        WestEast := KDE_X1 < KDE_WinX1+ DimMx/2 ? 1:-1      ; ie, Left-Right
        NorthSouth := KDE_Y1 < KDE_WinY1+ DimMy/2 ? 1:-1     ;ie, Up-Down

        ; Set max width (height) parameter to leave space for Slave if present, and
        ; Set 'Master Near' flag to show if Master or Slave nearest to top-left of monitor.
        MaxDim2x := MaxDimx ; used in limit calculation for Master
        MaxDim2y := MaxDimy
        If (OneOfPair = 1) and (LatVert = 1) {
            MaxDim2x := MaxDimx-MinDimx
            MasterNear := (KDE_Winx1+ DimMx/2) > (KDE_P_x + DimPx/2) ? 1:-1
            }
        If (OneOfPair = 1) and (LatVert = -1) {
            MaxDim2y := MaxDimy-MinDimy
            MasterNear := (KDE_Winy1+ DimMy/2) > (KDE_P_y + DimPy/2) ? 1:-1
            }           
        Loop
        {
         GetKeyState,KDE_Button,%DragButton% ,P
            If KDE_Button = U                       ; Abort if button was released.
                break
            MouseGetPos,KDE_X2,KDE_Y2               ; Get current mouse position.
       
            KDE_X2 -= KDE_X1                        ; offset from initial mouse position.
            KDE_Y2 -= KDE_Y1
            KDE_X1 := (KDE_X2 + KDE_X1)     ; Reset initial position for next iteration.
            KDE_Y1 := (KDE_Y2 + KDE_Y1)
           
            ; Implement drag mode choice (quadrants or cells). 
            If (DragMode = 1) and (Column=2)   ; Forces lateral-only dragging for Col2
                KDE_X2 := 0       
            If (DragMode = 1) and (Row=2)      ; Forces vertical-only dragging for Row2
                KDE_Y2 := 0
               
            ; Calculate shifts based on quadrant
            DeltaMX := (WestEast+1)/2*KDE_X2
            DeltaMY :=  (NorthSouth+1)/2*KDE_Y2
            DeltaDimMx := -WestEast*KDE_X2           
            DeltaDimMy := -NorthSouth*KDE_Y2
           
            ; Apply limits on maximum size
            StretchLimit("y", "NorthSouth")
            StretchLimit("x", "WestEast")
           
            KDE_WinX1 += DeltaMX
            KDE_WinY1 += DeltaMY
            DimMx  += DeltaDimMx
            DimMy  += DeltaDimMy
               
           ; Apply limits on minimum size
            ShrinkLimit("x", "WestEast")
            ShrinkLimit("y", "NorthSouth")
           
            ; Calculate dimensions of Slave if it exists
            IfNotEqual ,OneOfPair ,0       
            {
                ; LatVert previously set to +1 for lateral coupling and -1 for vertical coupling, 
                If (latvert = 1) {      ; lateral coupling
                    abs := "x"
                    ord := "y"
                    }
                If (latvert = -1) {     ; vertical coupling
                    abs := "y"
                    ord := "x"
                }
                KDE_P_%abs% := KDE_Win%abs%1+ DimM%abs% - MaxDim%abs%*(1+MasterNear)/2
                KDE_P_%ord% := KDE_Win%ord%1
                DimP%abs% := MaxDim%abs% - DimM%abs% 
                DimP%ord% := DimM%ord%

                ; Abort if centre of Slave would be on non-existent monitor (Mon_P = 0)
                Mon_P := GetMonitorAt(KDE_P_x+ DimPx/2, KDE_P_y+ DimPy/2, 0)
                IfEqual , Mon_P, 0, Continue     
                WinMove ,ahk_id %Slave%,, KDE_P_x , KDE_P_y, DimPx, DimPy 
            }
            WinMove,ahk_id %KDE_id%,, KDE_WinX1 , KDE_WinY1,  DimMx,  DimMy
        }
        return
    }
;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;
; Apply maximum width or height limits
StretchLimit(axis, Dir)
{
    If (DimM%axis% > MaxDim2%axis%) and (DeltaDimM%axis% >0) {
        DeltaM%axis% := -(%Dir%-1)/2*DeltaDimM%axis% +(%Dir%+1)/2*DeltaM%axis%
        DeltaDimM%axis% :=0
    }
}

; Apply minimum width or height limits 
ShrinkLimit(axis, Dir)
{       
    If (DimM%axis% < MinDim%axis%) and (DeltaDimM%axis% <0) { 
        DimM%axis%  -= DeltaDimM%axis%
        KDE_Win%axis%1 += -(%Dir%-1)/2*DeltaDimM%axis%
        }
}

; Exchange Master and Slave titles
Swap(ByRef Master, ByRef Slave)
{
    temp := Master
    Master := Slave
    Slave := temp
}
;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;
; Get the index of the monitor containing the specified x and y co-ordinates.
; (From WindowPad by Lexikos)

GetMonitorAt(x, y, default=1)
{
    SysGet, m, MonitorCount
    ; Iterate through all monitors.
    Loop, %m%
    {   ; Check if the window is on this monitor.
        SysGet, Mon, Monitor, %A_Index%
        if (x >= MonLeft && x <= MonRight && y >= MonTop && y <= MonBottom)
            return A_Index
    }

    return default
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; This function is from http://www.autohotkey.com/forum/topic22178.html
IsOverTitleBar(x, y, hWnd) {
   SendMessage, 0x84,, (x & 0xFFFF) | (y & 0xFFFF) << 16,, ahk_id %hWnd%
   if ErrorLevel in 2,3,8,9,20,21
      return true
   else
      return false
}
;;;;;;;;;;;;;;;;;;;;;;;;
Back to top
View user's profile Send private message
Unambiguous



Joined: 18 May 2005
Posts: 16

PostPosted: Wed Feb 24, 2010 4:42 am    Post subject: well documented script Reply with quote

Good job with the script, I like the idea and the way that you implement clipping. I am looking for a way to implement this with a couple other hacks I have with my window manager, such as gravity effects on ALT + LBUTTON dragging, and clipping at borders to inhibit movement outside of monitor.
Back to top
View user's profile Send private message
DAT



Joined: 01 Dec 2008
Posts: 49
Location: UK

PostPosted: Wed Feb 24, 2010 12:24 pm    Post subject: Thanks for being the first to respond Reply with quote

Thanks, and I'm glad you like it.

Yours is the first and only response to Dock'n Drag in over a year. I thought the post had dropped into a bottomless pit... Even better, I was using it (to compare two scripts side-by-side) at the very moment notification of your post came in.

So thanks, Unambiguous, you've made my day Very Happy
Back to top
View user's profile Send private message
jimbone
Guest





PostPosted: Sun Sep 19, 2010 5:26 pm    Post subject: Reply with quote

i like it and will use it at work. however, im looking for an autohotkey that auto stacks or side by sides windows (vista lingo).. then, your tool will be complete..
Back to top
Ace Coder



Joined: 26 Oct 2009
Posts: 361

PostPosted: Sun Sep 19, 2010 7:22 pm    Post subject: Reply with quote

Idk why this script didn't get the attention it deserves but KUDOS!
Great script, i'll be using it frequently i'm sure. Thank you.
_________________
Check out the new AHK forum competition!
Back to top
View user's profile Send private message
DAT



Joined: 01 Dec 2008
Posts: 49
Location: UK

PostPosted: Tue Sep 21, 2010 9:52 pm    Post subject: Reply with quote

@ Ace Coder

Thanks for the kind remarks Smile

@ jimbone

Glad to know you're using Dock n'Drag. But regarding the enhancements you mention, are you using Lexikos's WindowPad? It does pretty much all I need for manipulating windows (except for D n'D of course Wink), and it's especially useful when you're using more than one monitor.
Back to top
View user's profile Send private message
Maestr0



Joined: 18 Oct 2008
Posts: 159

PostPosted: Sun Jan 30, 2011 7:52 pm    Post subject: Reply with quote

wow, very nice work!

I'm just missing a systray icon for verification that the script is running and maybe that a currently active window is linked, other than that, brilliantly done.
Back to top
View user's profile Send private message
MasterFocus



Joined: 08 Apr 2009
Posts: 3035
Location: Rio de Janeiro - RJ - Brasil

PostPosted: Mon Jan 31, 2011 12:26 am    Post subject: Reply with quote

Maestr0 wrote:
I'm just missing a systray icon for verification that the script is running

Remove #NoTrayIcon.
_________________
"Read the manual. Read it again. Search the forum.
Try something before asking. Show what you've tried.
"

Antonio França
My stuff: Google Profile
Back to top
View user's profile Send private message Visit poster's website
Maestr0



Joined: 18 Oct 2008
Posts: 159

PostPosted: Mon Jan 31, 2011 10:35 am    Post subject: Reply with quote

MasterFocus wrote:
Maestr0 wrote:
I'm just missing a systray icon for verification that the script is running

Remove #NoTrayIcon.


heh, thanks, I knew that Very Happy
Back to top
View user's profile Send private message
Skrell
Guest





PostPosted: Wed Aug 03, 2011 7:57 pm    Post subject: Reply with quote

i have ALWAYS wanted this functionality..but i cannot get the docking feature to work Sad Is it possible this script won't work with Autohotkey_L ?
Back to top
Skrell
Guest





PostPosted: Wed Aug 03, 2011 7:58 pm    Post subject: Reply with quote

nevermind i got it!
Back to top
Skrell
Guest





PostPosted: Wed Aug 03, 2011 8:02 pm    Post subject: Reply with quote

the ONLY thing i don't like right now is that the 2 docked windows have to be the same size. I wish they windows would stay their original sizes and just dock. Smile
Back to top
hoppfrosch



Joined: 25 Jan 2006
Posts: 190
Location: Froschtümpel

PostPosted: Thu Aug 04, 2011 5:21 am    Post subject: Reply with quote

Skrell wrote:
the ONLY thing i don't like right now is that the 2 docked windows have to be the same size. I wish they windows would stay their original sizes and just dock. Smile


Also have a look at DockA-Module in Majkinetors marvelous Forms Framework ...

_________________________

Code:
;     (.)~(.)   
;    (-------)                                   
;---ooO-----Ooo---------------------------------------------------
;    Hoppfrosch  - AHK 1.1.00 Unicode 32bit on Win7 Ultimate
;-----------------------------------------------------------------                       
;    ( )   ( )                           
;    /|\   /|\ 
Back to top
View user's profile Send private message Visit poster's website
sumon



Joined: 18 May 2010
Posts: 1016
Location: Sweden

PostPosted: Thu Aug 04, 2011 6:11 am    Post subject: Reply with quote

I would also like to get this working, the furthest I got (after modifying the modifier, since CapsLock seems unintuitive to use as a modifier, to me) was making !RightClick (dock) work, it played a sound and moved two windows paralelly. When moving a window afterward, the other didn't follow.

I also got an error message shortly after that "C:\Program Files\GPSoftware\Directory Opus\dopusrt.exe" couldn't be run, obviously since I don't have that. Did I miss any requirement?

AHK_L uni, Win7 x64.
Back to top
View user's profile Send private message Visit poster's website
Skrell
Guest





PostPosted: Thu Aug 04, 2011 12:45 pm    Post subject: Reply with quote

how does the Forms Framework help me?
Back to top
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page 1, 2  Next
Page 1 of 2

 
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