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 

[module] Dock 1.0        (testing 2.0 b3)
Goto page Previous  1, 2, 3 ... , 17, 18, 19  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
bmcclure



Joined: 24 Nov 2007
Posts: 446

PostPosted: Sun Jan 13, 2008 9:32 pm    Post subject: Reply with quote

I didn't even realize that; thanks for the tips. I'll try that now.

I'm also trying a prototype of the SteamTabs dock. I'll keep you informed Smile
_________________
-Ben

SteamLab
SteamLab Wiki

[Broken] - My industrial music [on GarageBand]
Back to top
View user's profile Send private message
bmcclure



Joined: 24 Nov 2007
Posts: 446

PostPosted: Sun Jan 13, 2008 9:42 pm    Post subject: Reply with quote

How can I instantiate a dock before the host exists if I don't know what size or position the dock needs to be in until the host exists?

I'm just a little confused. For instance, to create the SteamTabs I'm going to have to get the x and y of the Steam window and base their starting position off of that, but when I instantiate the dock a Steam window may very well not exist yet.
_________________
-Ben

SteamLab
SteamLab Wiki

[Broken] - My industrial music [on GarageBand]
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Sun Jan 13, 2008 9:49 pm    Post subject: Reply with quote

Quote:
How can I instantiate a dock before the host exists if I don't know what size or position the dock needs to be in until the host exists?

If host is not alive, dock toggles itself off and calls onhostdeath. That is, if you added at least 1 client. So you can safely add clients before Host is there and Dock will store information about them, and just return. You have to explicitely toggle it on when host is alive again.

You can do it this way because you normaly doesn't think about Hosts size. You use parametric definition and it addopts whatever size the Host happens to have in particular moment or session. You can't use this method only if you defined client position using fixed dimensions, for isntance "x() y() w(,125) h(,35)". if this 125 and 35 are dynamic, you just have to update the dock definiton with new parameters when you want change to have effect and nothing else.

So, use parametric part of dock definition so you don't have to think about Hosts size. Last parameter in every class is fixed value.
_________________
Back to top
View user's profile Send private message MSN Messenger
bmcclure



Joined: 24 Nov 2007
Posts: 446

PostPosted: Sun Jan 13, 2008 10:09 pm    Post subject: Reply with quote

Ok, I think I figured it out, perhaps.

This works for showing the SteamDock, however it does not show the dock if the Steam window already exists when opening the dock. But if I close and re-open the Steam window, the dock appears. Have I coded something wrong?

Code:
EnableSteamDock: ; Enable the dock and wait for the Steam window
   Dock_OnHostDeath := "SteamDockClose"
   If cfg_SteamDock_Enable {
      cfg_SteamDock_Enabled := true ; Mark the dock as enabled
      MMenu_Set(TrayActionsMenu,"trDockSteam","","","c") ; Check the tray menu item
      GoSub,ShowSteamDock ; Show the actual GUI window
      sdresult := Dock(Dock_s1, "x() y(1) w(1) h(,30)") ; Show the dock right below the Steam window
   }
Return

WaitSteamDock:
   If cfg_SteamDock_Enabled or cfg_SteamTabs_Enabled { ; Sanity check to make sure the dock is enabled
      DetectHiddenWindows, Off ; We don't want to find a hidden Steam window
      Process, Exist, Steam.exe ; Check if Steam exists
      WinGet, SteamIDList, List, STEAM - ahk_pid %ErrorLevel% ; Get a list of all STEAM windows
      Loop, %SteamIDList% ; Loop through Steam windows
      {   curClass := WinGetClass("ahk_id " . SteamIDList%A_Index%) ; Find the window class
         If (StringLeft(curClass, 9) = "USurface_") { ; If the class matches Steam's class
             Sleep,250 ; Wait for a moment to make sure the window stays open
             Dock_HostID := WinExist("STEAM - ahk_class " . curClass) ; Set Steam's ID to the host
             If Dock_HostID { ; Make sure we got a valid ID
                SetTimer, WaitSteamDock, Off ; Turn off the timer while the dock is showing
                Dock_Toggle(true)
                OnMessage(0x200, "BtnMouseMove")
               OnMessage(0x2A3, "BtnMouseLeave")
               OnMessage(0x202, "BtnMouseLeave")
               Break ; Dock is shown, no need for the loop
            }
         }
      }
   } Else SetTimer, WaitSteamDock, Off
Return

SteamDockClose:
   SetTimer, WaitSteamDock, 100, -100 ; Re-set the timer (low priority so it doesn't override the Steam Lock timer)
Return


Oh, and this is the GUI window (don't mind the functions, they don't affect dock(), I tested)
Code:
ShowSteamDock:
   Gui, 31:Default
   Gui, Destroy ; Just in case there was another GUI with this number (hope it was saved, heh)
   Gui, Color, 464646, 5E5E5E ; Steam-like colors
   Gui, -Caption +ToolWindow ; Get rid of default title bar
   Gui, Font, S8 CWhite, Tahoma
   Gui, Add, Text, x10 y6 +Backgroundtrans, %ProgramName%:
   Gui +LastFound
   Dock_s1 := WinExist() ; Set the new window ID for the dock
   LockSteamBtn_hwnd := ""
   CDKeysBtn_hwnd := ""
   BackupsBtn_hwnd := ""
   ScreensBtn_hwnd := ""
   UpdatesBtn_hwnd := ""
   SettingsBtn_hwnd := ""
   CloseBtn_hwnd := ""
   ExitBtn_hwnd := ""
   LoadImage_AGB(LockSteamBtnb1, "res\gui\btn\Lock.bmp", 28, 76)
   LoadImage_AGB(LockSteamBtnb1_ro, "res\gui\btn\Lock_RO.bmp", 28, 76)
   LoadImage_AGB(CDKeysBtnb1, "res\gui\btn\CDKeys.bmp", 28, 76)
   LoadImage_AGB(CDKeysBtnb1_ro, "res\gui\btn\CDKeys_RO.bmp", 28, 76)
   LoadImage_AGB(BackupsBtnb1, "res\gui\btn\Backups.bmp", 28, 76)
   LoadImage_AGB(BackupsBtnb1_ro, "res\gui\btn\Backups_RO.bmp", 28, 76)
   LoadImage_AGB(ScreensBtnb1, "res\gui\btn\Screens.bmp", 28, 76)
   LoadImage_AGB(ScreensBtnb1_ro, "res\gui\btn\Screens_RO.bmp", 28, 76)
   LoadImage_AGB(UpdatesBtnb1, "res\gui\btn\Updates.bmp", 28, 76)
   LoadImage_AGB(UpdatesBtnb1_ro, "res\gui\btn\Updates_RO.bmp", 28, 76)
   LoadImage_AGB(SettingsBtnb1, "res\gui\btn\Settings.bmp", 28, 76)
   LoadImage_AGB(SettingsBtnb1_ro, "res\gui\btn\Settings_RO.bmp", 28, 76)
   LoadImage_AGB(CloseBtnb1, "res\gui\btn\Close.bmp", 28, 76)
   LoadImage_AGB(CloseBtnb1_ro, "res\gui\btn\Close_RO.bmp", 28, 76)
   LoadImage_AGB(ExitBtnb1, "res\gui\btn\Exit.bmp", 28, 76)
   LoadImage_AGB(ExitBtnb1_ro, "res\gui\btn\Exit_RO.bmp", 28, 76)
   ActiveDockButtons := "LockSteamBtn|CDKeysBtn|BackupsBtn|ScreensBtn|UpdatesBtn|SettingsBtn|CloseBtn|ExitBtn"
   Loop, Parse, ActiveDockButtons,|
   {   %A_LoopField%_bH := 28
      %A_LoopField%_bW := 76
      If (A_LoopField = "LockSteamBtn")
         %A_LoopField%_bO := "x+20 y0 w76 h28"
      Else If (A_LoopField = "CDKeysBtn" or A_LoopField = "SettingsBtn")
         %A_LoopField%_bO := "x+10 yp+0 w76 h28"
      Else %A_LoopField%_bO := "x+0 yp+0 w76 h28"
   }
   LockSteamBtn_bG := "Locksteam"
   CDKeysBtn_bG := "ShowKeyManager"
   BackupsBtn_bG := "ShowBackupManager"
   ScreensBtn_bG := "ShowScreenshotManager"
   UpdatesBtn_bG := "ShowUpdates"
   SettingsBtn_bG := "ShowSettings"
   CloseBtn_bG := "DisableSteamDock"
   ExitBtn_bG := "Exit"
   AddGraphicButton("LockSteamBtn", LockSteamBtnb1, "x+20 y1 w76 h28 gLockSteam", 28, 76)
   AddGraphicButton("CDKeysBtn", CDKeysBtnb1, "x+10 yp+0 w76 h28 gShowKeyManager", 28, 76)
   AddGraphicButton("BackupsBtn", BackupsBtnb1, "x+0 yp+0 w76 h28 gShowBackupManager", 28, 76)
   ;AddGraphicButton("ScreensBtn", ScreensBtnb1, "x+0 yp+0 w76 h28 gShowScreenshotManager", 28, 76)
   AddGraphicButton("UpdatesBtn", UpdatesBtnb1, "x+0 yp+0 w76 h28 gShowUpdates", 28, 76)
   AddGraphicButton("SettingsBtn", SettingsBtnb1, "x+10 yp+0 w76 h28 gShowSettings", 28, 76)
   AddGraphicButton("CloseBtn", CloseBtnb1, "x+0 yp+0 w76 h28 gDisableSteamDock", 28, 76)
   AddGraphicButton("ExitBtn", ExitBtnb1, "x+0 yp+0 w76 h28 gExit", 28, 76)
   ActiveButtons .= "|" . ActiveDockButtons
   DetectHiddenWindows,On
   Gui, show, Hide
Return

I tried using Gui, show, NoActivate instead of Gui, show, Hide, however then it showed my dock window in the middle of the screen regardless if there is a HostID.



Also, would this place a top-most dock 346 pixels from the left side and 46 pixels from the top of the host while retaining its size? I just want to be sure I've got it right:
Code:
x(,,346) y(,,46) t

_________________
-Ben

SteamLab
SteamLab Wiki

[Broken] - My industrial music [on GarageBand]
Back to top
View user's profile Send private message
bmcclure



Joined: 24 Nov 2007
Posts: 446

PostPosted: Sun Jan 13, 2008 10:28 pm    Post subject: Reply with quote

It even happens if the steam window is not showing when I open the dock. The first time it opens, the dock doesn't show (I'm assuming because the dock window is still hidden from Gui, Show, Hide maybe?). Then if you close and open Steam, it shows up.

But if I use Gui, Show, NoActivate, it still shows the dock even if no Steam window exists, but it always shows it in the default position on the screen. It's like Dock() isn't toggling the window off when it doesn't receive a HostID (or maybe not, heh) or something.
_________________
-Ben

SteamLab
SteamLab Wiki

[Broken] - My industrial music [on GarageBand]
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Sun Jan 13, 2008 10:52 pm    Post subject: Reply with quote

Quote:
he first time it opens, the dock doesn't show (I'm assuming because the dock window is still hidden from Gui, Show, Hide maybe?).

yes, you must use "+" in dock registration

Quote:
Also, would this place a top-most dock 346 pixels from the left side and 46 pixels from the top of the host while retaining its size? I just want to be sure I've got it right:

yes

Quote:
But if I use Gui, Show, NoActivate, it still shows the dock even if no Steam window exists

You probably did something wrong here. Don't use Gui, Show NoActivate. Just hide the window initialy and use "+"

Without "+" dock was working but client was hidden as you use Hide command. Then when you killed the Host and restarted it, your timer showed the client so from that moment it worked ok.

Quote:
Have I coded something wrong?

Yes Very Happy Use "+".

I will update this section anyway to prevent such erorrs. So you will use "+" with every dock for perfect glue.
_________________
Back to top
View user's profile Send private message MSN Messenger
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Sun Jan 13, 2008 11:01 pm    Post subject: Reply with quote

In the meantime this is b2. It should fix the startup and set correct Z order when host is alive in the moment of registration, but in the background. I hope it works well:

Code:
; Title:     Dock
;            *Dock desired top level windows (dock clients) to any top level window (dock host)*
;

;
;          Using dock module you can glue your or third-party windows to any top level window.
;          Docked windows in module terminology are called Clients and the window that keeps their
;          position relative to itself is called Host. Once Clients are connected to the Host, this
;          group of windows will behave like single window - moving, sizing, focusing, hiding and other
;          OS events will be handled by the module so that the "composite window" behaves like the single window.
;
;          Module uses system hook to monitor windows changes, so it's idle when it is not arranging windows.
;
;----------------------------------------------------------------------------------------------------------------------------------
; Function:  Dock
;            Instantiate dock of given client upon host. Multiple clients per one host are supported.
;
;
; Parameters:
;
;            pClientId   - HWND of the Client GUI. Dock is created or updated (if already exists) for that hwnd.   
;                       If "+" is the first char of pClientId, Dock will first show the window if it is hidden (for smoother initialization)
;            pDockDef   - Dock definition, see bellow. To remove dock client pass "-".
;                       If you pass empty string, client will be docked to the host according to its current position relative to the host.
;            reset      - internal parameter, do not use.
;
; Globals:
;            Dock_HostID   - Sets docking host
;         Dock_OnHostDeath - Sets label that will be called when host dies. Afterwards, module will disable itself using Dock_Toggle(false).
;
; Dock definition: 
;         Dock definition is string containing unordered white space separated parameters which describe Client's position relative to the Host. The big number of parameters allow
;         for fine tuning of Client's position and basically every setup is possible. Parameters are grouped into 4 classes - x, y, w & h parameters.
;         Classes and their parameters are optional.
;         
;>       Syntax:      x(hw,cw,dx)  y(hh,ch,dy)  w(hw,dw)  h(hh,dh)  t
;
;
;            o The *X* coordinate of the top, left corner of the client window is computed as
;            *HostX + hw*HostWidth + cw*ClientWidth + dx*, with the parameters hw, cw & dx (shorten from host width and client width multipliers, delta x).
;
;            o The *Y* coordinate of the top, left corner of the client window is computed as
;            *HostY + hh*HostHeight + ch*ClientHeight + dy*, with the parameters hh, ch and dy.
;
;            o The width *W* of the client window is computed as *hw*HostWidth + dw*, with the parameters hw & wd.
;
;            o The height *H* of the client window is computed as *hh*HostHeight + dh*, with the parameters hh & hd.
;
;          o The topmost state of the client is *T*. Specify this option to to set the Client always on top the Host. This allows client to be positioned inside the Host.
;
;         If you omit any of the class parameters it will default to 0. So, the following expressions all have the same effect :
;>           x(0,0,0) = x(0,0) = x(0,0,) = x(0) = x(0,)= x(0,,) = x() = x(0,,0) = x(,0,0) = x(,,0) = ...
;>         y(0,1,0) = y(0,1) = y(,1) = y(,1,) = y(,1,0) = ...
;
;         Keep in mind that x() is not the same as omitting x entirely. First case is equal to x(0,0,0) so it will set Client's X coordinate to be equal as Host's.
;         In second case, x coordinate of the client will not be touched by the Dock module but Client will keep whatever x it had before.
;         
;
; Returns:
;            "OK" or "Err" with text describing last successful or failed action.
;
; Remarks:
;         You must set DetectHiddenWindows if Host is practicing hiding. Otherwise, Dock will treat Host hiding as death.
;         All clients will be hidden once host is terminated or it becomes hidden itself.
;
;         Use SetBatchLines, -1 with dock module for fluid client movement. You will experience delay in clients moving otherwise.
;         However, if CPU usage is very high, you might experience a delay in client movement anyway.
;
;         If you are using *Gui, Show* command immediately before registering client, make sure you specify *NoActivate* flag.
;
;         "Topmost" feature can be used to create additional caption buttons. Caption buttons are topmost clients containing only 1 button
;         and docked to the Host so that they appear in its caption. To setup caption button, only X class is needed. For instance
;         "x(1,0,-100)" can be used to set caption button 100 pixels from the right edge of the Host's caption.
;
; Example:
;>      Dock(Client1ID, "x(0,-1,-10)  y(0,0,0)  w(0,63)  h(1,0)")   ;top left, host height
;>      Dock(Client2ID, "x() y(,-1,-5) w(1)  h(,30)")            ;top above, host width, short definition
;>       Dock(hTitleBtn, "x(1,0,-80), y(0,0,5) w(0,20) h(0,15) t")   ;add title button, topmost client docked inside Host, on title, with 20x15 size
;
Dock(pClientID, pDockDef="", reset=0) {                    ;Reset is internal parameter, used by Dock_Shutdown
   local cnt, new, cx, cy, hx, hY, t, hP
   static init=0, idDel, classes="x|y|w|h"

    if (reset)                                             ;Used by Dock Shutdown to reset the function
      return init := 0

   if !init
      Dock_aClient[0] := 0

   cnt := Dock_aClient[0]

   ;remove dock client ?
   if (pDockDef="-") and (Dock_%pClientID%){

      idDel := Dock_%pClientID%

      loop, parse, classes, |
      loop, 3
         Dock_%pClientID%_%A_LoopField%%A_Index% := ""
     Dock_%pClientID% := ""         ; don't remove t Dock_%pClientID%_t :=
       
      ;move last one to the place of the deleted one
      Dock_aClient[%idDel%] := Dock_aClient[%cnt%], Dock_aClient[%cnt%] := "", Dock_aClient[0]--
      return "OK - Remove"
   }

    if (*&pClientID = 43)       ; 43 = "+"
   {
      pClientID := SubStr(pClientID, 2)
      ifWinExist ahk_id %Dock_HostId%         ;if host exists when dock is instantiated, position the client infront of it.
      {
        hP := DllCall("GetWindow", "uint", Dock_HostId, "uint", 3) ;use hwndprev in case host is in the background
        DllCall("SetWindowPos", "uint", pClientID, "uint", hP, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 0x40 | 19 | 0x4000) ;SWP_SHOWWINDOW ..., no activate
      }
   }


   if pDockDef =
   {
      WinGetPos hX, hY,,, ahk_id %Dock_HostID%
      WinGetPos cX, cY,,, ahk_id %pClientID%
      pDockDef := "x(0,0," cX - hX ")  y(0,0," cY - hY ")"
   }

   ;add new dock client if it not exists, or update its dock settings if it exists
   loop, parse, pDockDef, %A_Space%%A_Tab%
      if (A_LoopField != "") {
         t := A_LoopField, c := SubStr(t,1,1)
         if c not in x,y,w,h,t
            return "ERR: Bad dock definition"

         if c = t
         {
             Dock_%pClientID%_t := 1
         }
         else {         
            t := SubStr(t,3,-1)
            StringReplace, t, t,`,,|,UseErrorLevel
            t .= !ErrorLevel ? "||" : (ErrorLevel=1 ? "|" : "")
            loop, parse, t,|,%A_Space%%A_Tab%
               Dock_%pClientID%_%c%%A_Index% := A_LoopField ? A_LoopField : 0
         }
      }

   if !Dock_%pClientID% {
      Dock_%pClientID%   := ++cnt,
      Dock_aClient[%cnt%] := pClientId
      Dock_aClient[0]++
   }

   ;start the dock if its not already started
   If !init {
      init++, Dock_hookProcAdr := RegisterCallback("Dock_HookProc")
      Dock_Toggle(true)   
   }

   Dock_Update()
   return "OK"
}       


;-----------------------------------------------------------------------------------------------------
;Function:  Dock_Shutdown
;         Uninitialize dock module. This will clear all clients and internal data and unregister hooks.
;         Dock_OnHostDeath, Dock_HostId are kept on user values.
;
Dock_Shutdown() {
   local cID

   Dock_Toggle(false)
   DllCall("GlobalFree", "UInt", Dock_hookProcAdr), Dock_hookProcAdr := ""
   Dock(0,0,1)         ;reset dock function

   ;erase clients
   loop, % Dock_aClient[0]
   {
      cId := Dock_aClient[%A_Index%], Dock_aClient[%A_Index%] := ""
      Dock_%cID% := ""
      loop, 10
         Dock_%cID%_%A_Index% := ""
   }
}

;-----------------------------------------------------------------------------------------------------
;Function: Dock_Toggle
;          Toggles the dock module ON or OFF.
;
;Parameters:
;         enable - Set to true to set the dock ON, set to FALSE to turn it OFF. Skip to toggle.
;
;Remarks:
;         Use Dock_Toggle(false) to suspend the dock module (to unregister hook), leaving its internal data in place.
;         This is different from Dock_Shutdown as latest removes module completely from memory and
;         unregisters its clients.
;         
;         You can also use this function to temporary disable module when you don't want dock update routine to interrupt your time critical sections.
;
Dock_Toggle( enable="" ) {
   global

   if Dock_hookProcAdr =
      return "ERR - Dock must be loaded."

   if enable =
      enable := !Dock_hHook1
   else if (enable && Dock_hHook1)
      return   "ERR - Dock already enabled"

   if !enable
      API_UnhookWinEvent(Dock_hHook1), API_UnhookWinEvent(Dock_hHook2), API_UnhookWinEvent(Dock_hHook3), Dock_hHook3 := Dock_hHook1 := Dock_hHook2 := ""
   else  {
      Dock_hHook1 := API_SetWinEventHook(3,3,0,Dock_hookProcAdr,0,0,0)            ; EVENT_SYSTEM_FOREGROUND
      Dock_hHook2 := API_SetWinEventHook(0x800B,0x800B,0,Dock_hookProcAdr,0,0,0)   ; EVENT_OBJECT_LOCATIONCHANGE
     Dock_hHook3 := API_SetWinEventHook(0x8002,0x8003,0,Dock_hookProcAdr,0,0,0)   ; EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE

      if !(Dock_hHook1 && Dock_hHook2 && Dock_hHook3) {      ;some of them failed, unregister everything
         API_UnhookWinEvent(Dock_hHook1), API_UnhookWinEvent(Dock_hHook2), API_UnhookWinEvent(Dock_hHook3)
         return "ERR - Hook failed"
      }

    Dock_Update()
   }
   return enable
}
;==================================== INTERNAL ======================================================
Dock_Update() {
   local hX, hY, hW, hh, W, H, X, Y, cx, cy, cw, ch, fid, wd, cid
   static gid=0   ;fid & gid are function id and global id. I use them to see if the function interupted itself.

   wd := A_WinDelay
   SetWinDelay, -1
   fid := gid += 1
   WinGetPos hX, hY, hW, hH, ahk_id %Dock_HostID%
;   OutputDebug %hX% %hY% %hW% %hH%    %event%

   ;xhw,xw,xd,  yhh,yh,yd,  whw,wd,  hhh,hd
   loop, % Dock_aClient[0]
   {
     cId := Dock_aClient[%A_Index%]
      WinGetPos cX, cY, cW, cH, ahk_id %cID%
      W := Dock_%cId%_w1*hW + Dock_%cId%_w2,  H := Dock_%cId%_h1*hH + Dock_%cId%_h2
      X := hX + Dock_%cId%_x1*hW + Dock_%cId%_x2* (W ? W : cW) + Dock_%cId%_x3
      Y := hY + Dock_%cId%_y1*hH + Dock_%cId%_y2* (H ? H : cH) + Dock_%cId%_y3

     if (fid != gid)   {            ;some newer instance of the function was running, so just return (function was interupted by itself). Without this, older instance will continue with old host window position and clients will jump to older location. This is not so visible with WinMove as it is very fast, but SetWindowPos shows this in full light.
         SetWinDelay, %wd%
       break
      }

   DllCall("SetWindowPos", "uint", cId, "uint", 0, "uint", X ? X : cX, "uint", Y ? Y : cY, "uint", W ? W : cW, "uint", H ? H :cH, "uint", 4 | 0x10 | 0x400 ) ;4 | 0x10 | 0x400

;     if (W+H)            
;          WinMove ahk_id %cId%,,X,Y, W ? W : "" ,H ? H : ""
;      else WinMove ahk_id %cId%,,X,Y
  }
   SetTimer, Dock_SetZOrder, -20      ;set z-order in another thread (protects also from spaming z-order changes when host is rapidly moved).
   SetWinDelay, %wd%
}


Dock_SetZOrder:
;    OutputDebug setzorder
   loop, % Dock_aClient[0] 
   {
     _ := Dock_aClient[%A_Index%], _ := Dock_%_%_t
     if !_
         DllCall("SetWindowPos", "uint", Dock_aClient[%A_Index%], "uint", Dock_HostID, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 19 | 0x4000)
     else {
      cid := Dock_aClient[%A_Index%]
      _ := DllCall("GetWindowLong", "uint", Dock_aClient[%A_Index%], "int", -8)
      if !_
      {
         DllCall("SetWindowLong", "uint", Dock_aClient[%A_Index%], "int", -8, "uint", Dock_HostId)
         Sendmessage, 0x112, 0xf010+12, 0,, ahk_id %Dock_HostId%   ;SC_MOVE + HTTOP
      }
      }
   }

return

Dock_SetZOrder_OnClientFocus:
    ;OutputDebug setzorder on focus

   ;Set host just bellow focused client. If Client is topmost, this will make Host topmost, so return it back.
   res := DllCall("SetWindowPos", "uint", Dock_HostID, "uint", Dock_AClient, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 19) ;SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE ...
   res2:= DllCall("SetWindowPos", "uint", Dock_HostID, "uint", -2, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 19) ;SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE ...

;    OutputDebug setzorder on focus %res%  | %res2% | %dock_aclient%
   loop, % Dock_aClient[0]
   {
      _ := Dock_aClient[%A_Index%], _ := Dock_%_%_t
      if !_
          DllCall("SetWindowPos", "uint", Dock_aClient[%A_Index%], "uint", Dock_HostID, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 19) ;SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE
   }

return

SetTopmostClients( state ) {
   local id, ExStyle, id2, res, res2, hPrev
   static st
   
   critical
   if (st = state)
      return
   
   st := state
      hPrev := DllCall("GetWindow", "uint", Dock_HostID, "uint", 3) ;GW_HWNDPREV
   loop, % Dock_aClient[0]
   {
      id := Dock_aClient[%A_Index%]
      if !(Dock_%id%_t)
         continue                        

   ;set topmost or non topmost
   res := DllCall("SetWindowPos", "uint", id, "uint", state="off" ? -2 : -1, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 19 | 0x400 ) ;SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE  --- 0x400
;   outputdebug %state% %A_Index% %id% %res%
   WinGet, ExStyle, ExStyle, ahk_id %id%
   if (state = "on") and !(ExStyle & 0x8)  {

      ;For some reason, latest active on top client that is not last one created will fail to be set topmost
      ;For instance, if I create clients 1,2 and 3 in that order, 3 will work normaly and 1 and 2 will fail to be set on top from the moment
      ;they take focus and afterwards, until client 3 is clicked (?!?!?!?!?! - some threading shit or something...)
      ;So I simulate user activated last client created to "unfreeze" blocked client for which setwindowpos topmost failed.
      ;After setting the window on top (above) I am checking its exstyle to see if it is really on top, or it is freezed.
      ;If it is freezed, run the sequence bellow and unfreeze it. Its not certain to me if this is AHK or OS bug.
       id2 := Dock_aClient[0], id2 := Dock_aClient[%id2%]
       res := DllCall("SetForegroundWindow", "uint", id2)
       res2 := DllCall("SetWindowPos", "uint", id, "uint", -1, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 19 | 0x400) ;SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE
;       outputdebug %A_Index% %res% %res2%
   }
   
    ;Set infront of Dock_HostID. I must specify window directly above Host and if this line is executed, some third-party window is activated, so hPrev is not 0
      if state = off
          DllCall("SetWindowPos", "uint", id, "uint", hPrev, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 19 ) ;SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE


    }   
    critical off
}

;-----------------------------------------------------------------------------------------
; Events :
;         3     - Host is set to foreground
;         32779 - Location change
;         32770 - Show
;         32771 - Hide (also called on exit)
;
Dock_HookProc(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime ) {
   local e,     cls, style

   if idObject or idChild
      return
   WinGet Style, Style, ahk_id %hwnd%
   if (Style & 0x40000000)               ;RETURN if hwnd is child window, for some reason idChild may be 0 for some children ?!?! ( I hate ms )
      return

   WINGETCLASS, cls, ahk_id %hwnd%
   if cls in #32768,#32771,#32772,#32769,SysShadow,tooltips_class32
      return

   ;outputdebug % cls " " hwnd " " event
   if (event = 3)
   {
      ;check if client is taking focus
      loop, % Dock_aClient[0]
         if (hwnd = Dock_aClient[%A_Index%]){
         Dock_AClient := hwnd
         gosub Dock_SetZOrder_OnClientFocus
         return
      }
              

      ;some other app took focus, remove windows on top
;      If (hwnd != Dock_HostID)
;         SetTopmostClients( "off" )      ;its called down there so don't call it here

   }

   If (hwnd != Dock_HostID){
      if !WinExist("ahk_id " Dock_HostID) && IsLabel(Dock_OnHostDeath)
     {
        Dock_Toggle(false)
       gosub %Dock_OnHostDeath%
        loop, % Dock_aClient[0]
         DllCall("ShowWindow", "uint", Dock_aClient[%A_Index%], "uint",  0)
     }
      ;Must be called here, as brininging the minimised app using the taskbar button doesn't launch event=3
      ;due to the bug in WinEvents, but event=32770 (location change). So, topmost windows are set again on any
      ;window moving in the system, but SetTopMostClients just returns if T clients are already off, so no big deal.

      ;If Dock moved the client, it will still be reported
      loop, % Dock_aClient[0]
         if (hwnd = Dock_aClient[%A_Index%])
            return
      
      return
   }

   
   if event in 32770,32771
   {
      e := (event - 32771) * -5

      loop, % Dock_aClient[0]
         DllCall("ShowWindow", "uint", Dock_aClient[%A_Index%], "uint",  e)

;      SetTopmostClients( event=32770 ? "on" : "off")   ; Dock_Update calls this
   }

   Dock_Update()
}




API_SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags) {
   DllCall("CoInitialize", "uint", 0)
   return DllCall("SetWinEventHook", "uint", eventMin, "uint", eventMax, "uint", hmodWinEventProc, "uint", lpfnWinEventProc, "uint", idProcess, "uint", idThread, "uint", dwFlags)
}

API_UnhookWinEvent( hWinEventHook ) {
   return DllCall("UnhookWinEvent", "uint", hWinEventHook)
}

;---------------------------------------------------------------------------------------------------------------------
; Group: Presets
;       This section contains some common docking setups. You can just copy/paste dock definition strings in your script.
;
;      x(,-1) y()                  - top left, own size
;      x(,-1,10) y()               - top left, own size, 10px padding
;      x(,-1)  y() h(1)            - top left, use host's height, keep own width
;      x(,-1,20) y() w(,50) h(1)      - top left, use host's height, set width to 50 and padding to 20px
;      x(,-1)  y(.5,-.5)            - middle left, keep own size
;         
;      x(,-1)  y(1,-1) w(,20) h(,20)   - bottom left, fixed width & height to 20px
;      x(,-1)  y(1,-1) h(.5)         - bottom left, keep height half of the Host's height, keep own width
;      x(1,-1) y(1)  w(.25) h(.25)      - bottom right, width and height 1/5 of the Host
;      
;      x()   y(1) w(1) h(,100)         - below the host, use host's width, height = 100
;      x()   y(,-1,-5) w(1)            - above the host, use host's width, keep own height, 5px padding
;      x(.5,-.5) y(,-1) w(,200) h(,30)   - center above the host, width=200, height=30
;      x(.5,-.5) y(1) w(0.3) h(,30)   - center bellow the host, use 1/3 Host's width, height=30
;      
;      x(1) y()                  - top right, own size
;      x(1) y() w(,40) h(1)         - top right, use host's height, width = 40
;       x(1) y(.5,-.5)               - middle right, keep own size

;---------------------------------------------------------------------------------------------------------------------
; Group: About
;      o Ver 2.0 b2 by majkinetor. See http://www.autohotkey.com/forum/topic19400.html
;      o Thank You's: Laszlo, JGR, Joy2World, bmcclure
;      o Licenced under Creative Commons Attribution-Noncommercial <http://creativecommons.org/licenses/by-nc/3.0/>.


_________________
Back to top
View user's profile Send private message MSN Messenger
bmcclure



Joined: 24 Nov 2007
Posts: 446

PostPosted: Sun Jan 13, 2008 11:03 pm    Post subject: Reply with quote

Ok, with b2, and using this:
Code:
sdresult := Dock("+" . Dock_s1, "x() y(1) w(1) h(,30)") ; Show the dock right below the Steam window

I seem to have the same issue. Dock is not shown until Steam is closed and re-opened.
_________________
-Ben

SteamLab
SteamLab Wiki

[Broken] - My industrial music [on GarageBand]
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Sun Jan 13, 2008 11:07 pm    Post subject: Reply with quote

I will check it out troughly when I find time.
In the meantime. You may check the _Two toolbars.ahk sample (download latest archive on the first page), it does exactly what you need.

Thx.
_________________
Back to top
View user's profile Send private message MSN Messenger
bmcclure



Joined: 24 Nov 2007
Posts: 446

PostPosted: Sun Jan 13, 2008 11:27 pm    Post subject: Reply with quote

I'm looking at that example. The only real difference I can see is that I don't know what the host class is going to be until after the dock is shown, since Steam generates a class name which isn't determined until my timer label runs. But I don't think that should make a difference.

I don't know. I'll keep trying to change things in my code, but I can't seem to get it to work right like this.

Update: Fixed! I didn't realize I still needed to keep my AnimateWindow code in the timer that waits for Steam; I thought the Dock() would show itself when it was toggled, but adding that code seems to have resolved it for me. I just added my animatewindow line back in after Dock_Toggle() and now it fades in even if Steam is already open which is good. Thanks!
_________________
-Ben

SteamLab
SteamLab Wiki

[Broken] - My industrial music [on GarageBand]
Back to top
View user's profile Send private message
bmcclure



Joined: 24 Nov 2007
Posts: 446

PostPosted: Sun Jan 13, 2008 11:56 pm    Post subject: Reply with quote

OK, trying to show the SteamTabs as an overlay on the Steam window.

I can show the SteamTabs window by itself fine. But I can't add it as a topmost dock over Steam for some reason.

1. If I leave both definitions in there, like this:
Code:
sdresult := Dock("+" Dock_s1, "x() y(1) w(1) h(,30)")
stresult := Dock("+" Dock_s2, "x(,,346) y(,,46) t")
then when it goes to show the second dock, I get a "busy" cursor icon and it sits like that until I ctrl+alt+del. Then I can cancel that and it works fine, but the dock isn't there. Sometimes it doesn't do this, but it still doesn't show dock.

2. If I only define the second dock, and leave the first one out altogether, then it doesn't freeze anymore, but it still doesn't show the dock.

3. If I remove both dock dock definitions and just show the second dock, it displays in the middle of the screen fine. So the GUI seems OK.
_________________
-Ben

SteamLab
SteamLab Wiki

[Broken] - My industrial music [on GarageBand]
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Mon Jan 14, 2008 1:08 am    Post subject: Reply with quote

Quote:
Then when it goes to show the second dock, I get a "busy" cursor icon and it sits like that until I ctrl+alt+del.

Ye, this is definitely a bug, I got it myself sometimes....
Don't know about other things.. will see...

BTW, can you make it work on some other host ?
_________________
Back to top
View user's profile Send private message MSN Messenger
bmcclure



Joined: 24 Nov 2007
Posts: 446

PostPosted: Mon Jan 14, 2008 1:23 am    Post subject: Reply with quote

No, didn't show up on Notepad either.

But I just figured it out. I had to specify the w() and h() parameters. It apparently wasn't keeping the original GUI size and must have been showing it without any size.

It works like this: Dock("+" Dock_s2, "x(,,346) y(,,22) w(,300) h(,46) t")

But now it freezes every time it shows, as reported and confirmed above. So the dock works, but only if you press Ctrl+Alt+Del first. And this is on both Steam and Notepad.

This is exciting though... I now have added 3 tabs to the Steam window and they look like they belong there!



Now if I can just figure out a way to darken the previously active Steam tab, I'd be in business Smile

Hm, perhaps I could overlay another dock over the original buttons whenever a SteamLab tab is clicked that makes all of them look inactive. Then as soon as it is clicked it removes the overlay and passes a click through underneath. I'll give that a try.
_________________
-Ben

SteamLab
SteamLab Wiki

[Broken] - My industrial music [on GarageBand]
Back to top
View user's profile Send private message
bmcclure



Joined: 24 Nov 2007
Posts: 446

PostPosted: Mon Jan 14, 2008 4:36 am    Post subject: Reply with quote

I got it working, and I still have tons of tweaking, but:

- I create the 3 SteamTabs, all inactive, when Steam loads
- When a SteamTab is clicked, I create an overlay dock that makes all of the original tabs look inactive
- I then show a large overlay covering the rest of the window depending on which SteamTab was clicked
- If the overlay over the old tabs is clicked, the tab overlay and window overlay disappear and the SteamTabs again appear inactive

So basically, it works just like the original tabs (with a lot of hacks) Smile

I notice that sometimes it doesn't freeze, but usually it does. Sometimes it clears up on its own, and sometimes I have to push ctrl+alt+del

I am having a problem getting my GUI elements to size correctly.

I updated to the new release of Anchor() which uses WinAPI functions instead of Gui controls. This allowed my overlay resizing to work. But the problem is it never works the first time the overlay is shown. Everything is the default size, like the GuiSize label was never called since the Dock showed the window, even though it does need to be stretched.

Then if I re-open the overlay again, it gets sized properly. After that, however, it's hit or miss. Sometimes when it opens the GuiSize is called and everything is sized properly, and other times nothing gets resized at all.

Could it be some sort of timing thing?

Check out a full screenshot of how it works when it actually resizes properly here
_________________
-Ben

SteamLab
SteamLab Wiki

[Broken] - My industrial music [on GarageBand]
Back to top
View user's profile Send private message
majkinetor



Joined: 24 May 2006
Posts: 3626
Location: Belgrade

PostPosted: Mon Jan 14, 2008 11:16 am    Post subject: Reply with quote

I think b3 will solve the freezing problem. This version also makes "+" sign depricated. Remove it from your script. Make sure clients are hidden from now on when starting dock as it will handle them and there is no way client can remain hidden while Dock is active on it. To let the user hide the docked client you have to undock it for now. So, in your timer routine, you just have to return new host id and to toggle the dock on.

Code:

; Title:     Dock
;            *Dock desired top level windows (dock clients) to any top level window (dock host)*
;

;
;          Using dock module you can glue your or third-party windows to any top level window.
;          Docked windows in module terminology are called Clients and the window that keeps their
;          position relative to itself is called Host. Once Clients are connected to the Host, this
;          group of windows will behave like single window - moving, sizing, focusing, hiding and other
;          OS events will be handled by the module so that the "composite window" behaves like the single window.
;
;          Module uses system hook to monitor windows changes, so it's idle when it is not arranging windows.
;
;----------------------------------------------------------------------------------------------------------------------------------
; Function:  Dock
;            Instantiate dock of given client upon host. Multiple clients per one host are supported.
;
;
; Parameters:
;
;            pClientId   - HWND of the Client GUI. Dock is created or updated (if already exists) for that hwnd.   
;            pDockDef   - Dock definition, see bellow. To remove dock client pass "-".
;                       If you pass empty string, client will be docked to the host according to its current position relative to the host.
;            reset      - internal parameter, do not use.
;
; Globals:
;            Dock_HostID   - Sets docking host
;         Dock_OnHostDeath - Sets label that will be called when host dies. Afterwards, module will disable itself using Dock_Toggle(false).
;
; Dock definition: 
;         Dock definition is string containing unordered white space separated parameters which describe Client's position relative to the Host. The big number of parameters allow
;         for fine tuning of Client's position and basically every setup is possible. Parameters are grouped into 4 classes - x, y, w & h parameters.
;         Classes and their parameters are optional.
;         
;>       Syntax:      x(hw,cw,dx)  y(hh,ch,dy)  w(hw,dw)  h(hh,dh)  t
;
;
;            o The *X* coordinate of the top, left corner of the client window is computed as
;            *HostX + hw*HostWidth + cw*ClientWidth + dx*, with the parameters hw, cw & dx (shorten from host width and client width multipliers, delta x).
;
;            o The *Y* coordinate of the top, left corner of the client window is computed as
;            *HostY + hh*HostHeight + ch*ClientHeight + dy*, with the parameters hh, ch and dy.
;
;            o The width *W* of the client window is computed as *hw*HostWidth + dw*, with the parameters hw & wd.
;
;            o The height *H* of the client window is computed as *hh*HostHeight + dh*, with the parameters hh & hd.
;
;          o The topmost state of the client is *T*. Specify this option to to set the Client always on top the Host. This allows client to be positioned inside the Host.
;
;         If you omit any of the class parameters it will default to 0. So, the following expressions all have the same effect :
;>           x(0,0,0) = x(0,0) = x(0,0,) = x(0) = x(0,)= x(0,,) = x() = x(0,,0) = x(,0,0) = x(,,0) = ...
;>         y(0,1,0) = y(0,1) = y(,1) = y(,1,) = y(,1,0) = ...
;
;         Keep in mind that x() is not the same as omitting x entirely. First case is equal to x(0,0,0) so it will set Client's X coordinate to be equal as Host's.
;         In second case, x coordinate of the client will not be touched by the Dock module but Client will keep whatever x it had before.
;         
;
; Returns:
;            "OK" or "Err" with text describing last successful or failed action.
;
; Remarks:
;         You must set DetectHiddenWindows if Host is practicing hiding. Otherwise, Dock will treat Host hiding as death.
;         All clients will be hidden once host is terminated or it becomes hidden itself.
;
;         Use SetBatchLines, -1 with dock module for fluid client movement. You will experience delay in clients moving otherwise.
;         However, if CPU usage is very high, you might experience a delay in client movement anyway.
;
;         If you are using *Gui, Show* command immediately before registering client, make sure you specify *NoActivate* flag.
;
;         "Topmost" feature can be used to create additional caption buttons. Caption buttons are topmost clients containing only 1 button
;         and docked to the Host so that they appear in its caption. To setup caption button, only X class is needed. For instance
;         "x(1,0,-100)" can be used to set caption button 100 pixels from the right edge of the Host's caption.
;
; Example:
;>      Dock(Client1ID, "x(0,-1,-10)  y(0,0,0)  w(0,63)  h(1,0)")   ;top left, host height
;>      Dock(Client2ID, "x() y(,-1,-5) w(1)  h(,30)")            ;top above, host width, short definition
;>       Dock(hTitleBtn, "x(1,0,-80), y(0,0,5) w(0,20) h(0,15) t")   ;add title button, topmost client docked inside Host, on title, with 20x15 size
;
Dock(pClientID, pDockDef="", reset=0) {                    ;Reset is internal parameter, used by Dock_Shutdown
   local cnt, new, cx, cy, hx, hY, t, hP
   static init=0, idDel, classes="x|y|w|h"

    if (reset)                                             ;Used by Dock Shutdown to reset the function
      return init := 0

   if !init
      Dock_aClient[0] := 0

   cnt := Dock_aClient[0]

   ;remove dock client ?
   if (pDockDef="-") and (Dock_%pClientID%){

      idDel := Dock_%pClientID%

      loop, parse, classes, |
      loop, 3
         Dock_%pClientID%_%A_LoopField%%A_Index% := ""
     Dock_%pClientID% := ""         ; don't remove t Dock_%pClientID%_t :=
       
      ;move last one to the place of the deleted one
      Dock_aClient[%idDel%] := Dock_aClient[%cnt%], Dock_aClient[%cnt%] := "", Dock_aClient[0]--
      return "OK - Remove"
   }

   if pDockDef =
   {
      WinGetPos hX, hY,,, ahk_id %Dock_HostID%
      WinGetPos cX, cY,,, ahk_id %pClientID%
      pDockDef := "x(0,0," cX - hX ")  y(0,0," cY - hY ")"
   }

   ;add new dock client if it not exists, or update its dock settings if it exists
   loop, parse, pDockDef, %A_Space%%A_Tab%
      if (A_LoopField != "") {
         t := A_LoopField, c := SubStr(t,1,1)
         if c not in x,y,w,h,t
            return "ERR: Bad dock definition"

         if c = t
         {
             Dock_%pClientID%_t := 1
         }
         else {         
            t := SubStr(t,3,-1)
            StringReplace, t, t,`,,|,UseErrorLevel
            t .= !ErrorLevel ? "||" : (ErrorLevel=1 ? "|" : "")
            loop, parse, t,|,%A_Space%%A_Tab%
               Dock_%pClientID%_%c%%A_Index% := A_LoopField ? A_LoopField : 0
         }
      }

   if !Dock_%pClientID% {
      Dock_%pClientID%   := ++cnt,
      Dock_aClient[%cnt%] := pClientId
      Dock_aClient[0]++
   }

   ;start the dock if its not already started
   If !init {
      init++, Dock_hookProcAdr := RegisterCallback("Dock_HookProc")
      Dock_Toggle(true)   
   }

   Dock_Update()
   return "OK"
}       


;-----------------------------------------------------------------------------------------------------
;Function:  Dock_Shutdown
;         Uninitialize dock module. This will clear all clients and internal data and unregister hooks.
;         Dock_OnHostDeath, Dock_HostId are kept on user values.
;
Dock_Shutdown() {
   local cID

   Dock_Toggle(false)
   DllCall("GlobalFree", "UInt", Dock_hookProcAdr), Dock_hookProcAdr := ""
   Dock(0,0,1)         ;reset dock function

   ;erase clients
   loop, % Dock_aClient[0]
   {
      cId := Dock_aClient[%A_Index%], Dock_aClient[%A_Index%] := ""
      Dock_%cID% := ""
      loop, 10
         Dock_%cID%_%A_Index% := ""
   }
}

;-----------------------------------------------------------------------------------------------------
;Function: Dock_Toggle
;          Toggles the dock module ON or OFF.
;
;Parameters:
;         enable - Set to true to set the dock ON, set to FALSE to turn it OFF. Skip to toggle.
;
;Remarks:
;         Use Dock_Toggle(false) to suspend the dock module (to unregister hook), leaving its internal data in place.
;         This is different from Dock_Shutdown as latest removes module completely from memory and
;         unregisters its clients.
;         
;         You can also use this function to temporary disable module when you don't want dock update routine to interrupt your time critical sections.
;
Dock_Toggle( enable="" ) {
   global

   if Dock_hookProcAdr =
      return "ERR - Dock must be loaded."

   if enable =
      enable := !Dock_hHook1
   else if (enable && Dock_hHook1)
      return   "ERR - Dock already enabled"

   if !enable
      API_UnhookWinEvent(Dock_hHook1), API_UnhookWinEvent(Dock_hHook2), API_UnhookWinEvent(Dock_hHook3), Dock_hHook3 := Dock_hHook1 := Dock_hHook2 := ""
   else  {
      Dock_hHook1 := API_SetWinEventHook(3,3,0,Dock_hookProcAdr,0,0,0)            ; EVENT_SYSTEM_FOREGROUND
      Dock_hHook2 := API_SetWinEventHook(0x800B,0x800B,0,Dock_hookProcAdr,0,0,0)   ; EVENT_OBJECT_LOCATIONCHANGE
     Dock_hHook3 := API_SetWinEventHook(0x8002,0x8003,0,Dock_hookProcAdr,0,0,0)   ; EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE

      if !(Dock_hHook1 && Dock_hHook2 && Dock_hHook3) {      ;some of them failed, unregister everything
         API_UnhookWinEvent(Dock_hHook1), API_UnhookWinEvent(Dock_hHook2), API_UnhookWinEvent(Dock_hHook3)
         return "ERR - Hook failed"
      }

    Dock_Update()
   }
   return enable
}
;==================================== INTERNAL ======================================================
Dock_Update() {
   local hX, hY, hW, hh, W, H, X, Y, cx, cy, cw, ch, fid, wd, cid
   static gid=0   ;fid & gid are function id and global id. I use them to see if the function interupted itself.

   wd := A_WinDelay
   SetWinDelay, -1
   fid := gid += 1
   WinGetPos hX, hY, hW, hH, ahk_id %Dock_HostID%
;   OutputDebug %hX% %hY% %hW% %hH%    %event%

   ;xhw,xw,xd,  yhh,yh,yd,  whw,wd,  hhh,hd
   loop, % Dock_aClient[0]
   {
      cId := Dock_aClient[%A_Index%]
      WinGetPos cX, cY, cW, cH, ahk_id %cID%
      W := Dock_%cId%_w1*hW + Dock_%cId%_w2,  H := Dock_%cId%_h1*hH + Dock_%cId%_h2
      X := hX + Dock_%cId%_x1*hW + Dock_%cId%_x2* (W ? W : cW) + Dock_%cId%_x3
      Y := hY + Dock_%cId%_y1*hH + Dock_%cId%_y2* (H ? H : cH) + Dock_%cId%_y3

      if (fid != gid)             ;some newer instance of the function was running, so just return (function was interupted by itself). Without this, older instance will continue with old host window position and clients will jump to older location. This is not so visible with WinMove as it is very fast, but SetWindowPos shows this in full light.
         break

;      DllCall("SetWindowPos", "uint", cId, "uint", 0, "uint", X ? X : cX, "uint", Y ? Y : cY, "uint", W ? W : cW, "uint", H ? H :cH, "uint", 1044) ;4 | 0x10 | 0x400
      WinMove ahk_id %cId%,,X ? X:"" ,Y ? Y:"", W ? W : "" ,H ? H : ""
   }     
   SetTimer, Dock_SetZOrder, -40      ;set z-order in another thread (protects also from spaming z-order changes when host is rapidly moved).
   SetWinDelay, %wd%
}

Dock_SetZOrder:
;    OutputDebug setzorder
   loop, % Dock_aClient[0] 
   {
     _ := Dock_aClient[%A_Index%], _ := Dock_%_%_t
     if !_
         DllCall("SetWindowPos", "uint", Dock_aClient[%A_Index%], "uint", Dock_HostID, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 19 | 0x4000 | 0x40)
     else
      ;Set owned clients if they are not already set. Host may not exist here.
      if !DllCall("GetWindowLong", "uint", Dock_aClient[%A_Index%], "int", -8) and WinExist("ahk_id " Dock_HostId)
      {
         DllCall("SetWindowLong", "uint", Dock_aClient[%A_Index%], "int", -8, "uint", Dock_HostId)
         _ := DllCall("GetWindow", "uint", Dock_HostId, "uint", 3) ;use hwndprev
         DllCall("SetWindowPos", "uint", Dock_aClient[%A_Index%], "uint", _, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 0x40 | 19 | 0x4000) ;SWP_SHOWWINDOW ..., no activate
      }

   }

return

Dock_SetZOrder_OnClientFocus:
    ;OutputDebug setzorder on focus

   ;Set host just bellow focused client.
   res := DllCall("SetWindowPos", "uint", Dock_HostID, "uint", Dock_AClient, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 19) ;SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE ...

   ;Set the non-T children , T children are handled normaly by OS as owned.
   loop, % Dock_aClient[0]
   {
      _ := Dock_aClient[%A_Index%], _ := Dock_%_%_t
      if !_
          DllCall("SetWindowPos", "uint", Dock_aClient[%A_Index%], "uint", Dock_HostID, "uint", 0, "uint", 0, "uint", 0, "uint", 0, "uint", 19) ;SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE
   }
return

;-----------------------------------------------------------------------------------------
; Events :
;         3     - Host is set to foreground
;         32779 - Location change
;         32770 - Show
;         32771 - Hide (also called on exit)
;
Dock_HookProc(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime ) {
   local e, cls, style

   if idObject or idChild
      return
   WinGet, style, Style, ahk_id %hwnd%
   if (style & 0x40000000)               ;RETURN if hwnd is child window, for some reason idChild may be 0 for some children ?!?! ( I hate ms )
      return

;   WINGETCLASS, cls, ahk_id %hwnd%
;   if cls in #32768,#32771,#32772,#32769,SysShadow,tooltips_class32      ;skip some windows classes, just to speed it up
;      return

   ;outputdebug % cls " " hwnd " " event
   if (event = 3)
   {
      ;check if client is taking focus
      loop, % Dock_aClient[0]
         if (hwnd = Dock_aClient[%A_Index%]){
         Dock_AClient := hwnd
         gosub Dock_SetZOrder_OnClientFocus
         return
      }      
   }

   If (hwnd != Dock_HostID){
      if !WinExist("ahk_id " Dock_HostID) && IsLabel(Dock_OnHostDeath)
     {
        Dock_Toggle(false)
       gosub %Dock_OnHostDeath%
        loop, % Dock_aClient[0]
         DllCall("ShowWindow", "uint", Dock_aClient[%A_Index%], "uint",  0)
     }
     return
   }

   
   if event in 32770,32771
   {
      e := (event - 32771) * -5
      loop, % Dock_aClient[0]
         DllCall("ShowWindow", "uint", Dock_aClient[%A_Index%], "uint",  e)
   }

   Dock_Update()
}




API_SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags) {
   DllCall("CoInitialize", "uint", 0)
   return DllCall("SetWinEventHook", "uint", eventMin, "uint", eventMax, "uint", hmodWinEventProc, "uint", lpfnWinEventProc, "uint", idProcess, "uint", idThread, "uint", dwFlags)
}

API_UnhookWinEvent( hWinEventHook ) {
   return DllCall("UnhookWinEvent", "uint", hWinEventHook)
}

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