WinPos_ : What you see is what you move.

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

WinPos_ : What you see is what you move.

12 Jun 2020, 16:19

A set functions to move a DWM composed (or not) windows.
Note: Windows are moved/resized based on their apparent position/dimensions rather than actual values.


WinPos_Get(hWnd)
Returns X,Y,W,H as an Object. This function is a an alternative to WinGetPos command.
  • Example:
    The following example shows the apparent position as well as the adjustments applied to derive X,Y,W,H

    Code: Select all

    Gui +hWndhWnd
    Gui, Show, x0 y0 w200 h200
    G := WinPos_Get(hWnd)
    Msgbox %  G.X  . A_Tab . G.Y  . A_Tab . G.W  . A_Tab . G.H . "`n" 
           .  G.XA . A_Tab . G.YA . A_Tab . G.WA . A_Tab . G.HA
    


WinPos_Set(hWnd, X, Y, W, H)
Moves/resizes a window and returns a true when successful. This function is an alternative to WinMove command.
You may not use X,Y,W,H returned by WinGetPos command.. Use WinPos_Get() instead.
  • Example:

    Code: Select all

    Run, Notepad.exe
    WinWait ahk_class Notepad
    hWnd := WinExist()
    WinPos_Set(hWnd, 0, 0, 400, 400)
    


WinPos_OffEdge(hWnd, XOffset, YOffset, MonitorNumber)
Moves a window relative to an edge of a monitor. Returns true when successful.
If XOffset or YOffset is zero or greater, they are an offset from left,top edges.
If XOffset or YOffset is a negative number, they are an offset from bottom, right edges.
The windows will be centered on the respective axis if XOffset or YOffset or both are omitted.
If MonitorNumber is omitted, window move will default to Primary monitor.

  • XOffset,YOffset for common edges
    Image

  • Example:
    WinPos_OffEdge() is API driven and will move a hidden window. One problem in Windows 10 is that DWM does not compose hidden windows.
    It applies effects only when window is shown. Therefore, a window has to be visible for correct calculation.
    The following example demonstrates showing a window offscreen and then move it. You may refer What is the maximum desktop resolution in Windows.

    Code: Select all

    #NoEnv
    #SingleInstance, Force
    Gui  +hwndhGui       
    Gui, Show, x64000 y64000 w200 h200 
    WinPos_OffEdge(hGui, -1, -1) ; move window to right-bottom corner of primary monitor
    Return
    



The functions:

Code: Select all

WinPos_Get(hWnd) {                                  ; ver 0.50 by SKAN on D36A/D36C @ tiny.cc/winpos
Local N:=VarSetCapacity(R,16,0), X1,Y1,W1,H1, X2,Y2,W2,H2, XA,YA,WA,HA 
  If !(DllCall("GetWindowRect", "Ptr",hWnd, "Ptr",&R) && NumGet(R,12,"Int")>0)
    Return 
  X1:=NumGet(R,0,"Int"), Y1:=NumGet(R,4,"Int"), W1:=NumGet(R,8,"Int")-X1,  H1:=NumGet(R,12,"Int")-Y1
  If !DllCall("dwmapi\DwmGetWindowAttribute", "Ptr",hWnd, "Int",9, "Ptr",&R, "Int",16)=0
    Return {"X":X1, "Y":Y1, "W":W1, "H":H1, "XA":0, "YA":0, "WA":0, "HA":0}
  X2:=NumGet(R,0,"Int"), Y2:=NumGet(R,4,"Int"), W2:=NumGet(R,8,"Int")-X2,  H2:=NumGet(R,12,"Int")-Y2
  XA:=X2-X1, YA:=Y2-Y1, WA:=W2-W1, HA:=H2-H1  
Return {"X":X1+XA, "Y":Y1+YA, "W":W1+WA, "H":H1+HA, "XA":X1-X2, "YA":Y1-Y2, "WA":W1-W2, "HA":H1-H2}
}

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

WinPos_Set(hWnd, X:="", Y:="", W:="", H:="") {      ; ver 0.50 by SKAN on D36A/D36C @ tiny.cc/winpos
Local
  If !G:=WinPos_Get(hWnd)
    Return 0
  X:=X!="" ? X : G.X, Y:=Y!="" ? Y : G.Y, W:=W!="" ? W : G.W, H:=H!="" ? H : G.H
Return DllCall("MoveWindow","Ptr",hWnd, "Int",X+G.XA,"Int",Y+G.YA,"Int",W+G.WA,"Int",H+G.HA,"Int",1)      
}

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

WinPos_OffEdge(hWnd, X:="",Y:="", M:="") {          ; ver 0.51 by SKAN on D36A/D37S @ tiny.cc/winpos
Local
  SysGet, N, MonitorPrimary
  SysGet, m, MonitorWorkArea, % M := ( Round(M) ? Round(M) : N )
  If !(G:=WinPos_Get(hWnd)) || (mLeft="")
    Return 0

  If (M=N) { 
    VarSetCapacity(R,48,0)
    DllCall("GetWindowRect","Ptr",WinExist("ahk_class Shell_TrayWnd"), "Ptr",&R)
    DllCall("SetRect", "Ptr",&R+16, "Int",MLeft, "Int",MTop, "Int",MRight, "Int",MBottom)
    DllCall("SubtractRect", "Ptr",&R+32, "Ptr",&R+16, "Ptr",&R)
    mLeft  := NumGet(R,32,"Int"),   mTop    := NumGet(R,36,"Int") 
    mRight := NumGet(R,40,"Int"),   mBottom := NumGet(R,44,"Int")
  }   

  X:= mLeft + (X="" ? ((mRight-mLeft)//2)-(G.W//2) : X<0 ? mRight-mLeft-G.W+1 : X),         W := G.W 
  Y:= mTop  + (Y="" ? ((mBottom-mTop)//2)-(G.H//2) : Y<0 ? mBottom-mTop-G.H+1 : Y),         H := G.H
Return DllCall("MoveWindow","Ptr",hWnd, "Int",X+G.XA,"Int",Y+G.YA,"Int",W+G.WA,"Int",H+G.HA,"Int",1)      
}

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
My Scripts and Functions: V1  V2
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: WinPos_ : What you see is what you move.

13 Jun 2020, 17:29

I earlier wrote:One problem in Windows 10 is that DWM does not compose hidden windows.
It applies effects only when window is shown. Therefore, a window has to be visible for correct calculation.
The following example demonstrates how to keep a GUI pseudo-hidden until it is moved.

Code: Select all

#NoEnv
#SingleInstance, Force
DetectHiddenWindows, On
Gui +hwndhGui       
WinSet, Trans, 1,   ahk_id %hGui%
DetectHiddenWindows, Off
Gui, Show, W200 h200
WinPos_OffEdge(hGui, -1,-1)
WinSet, Trans, 255, ahk_id %hGui%
 
DWM will compose if a window is cloaked (DWMWA_CLOAK) instead of being hidden.
But since DWMWA_CLOAK is not available in Windows 7, I will stick to the original example, except the last line maybe changed to

Code: Select all

WinSet, ExStyle, -0x80000, ahk_id %hGui% ; Remove WS_EX_LAYERED
My Scripts and Functions: V1  V2
Skrell
Posts: 302
Joined: 23 Jan 2014, 12:05

Re: WinPos_ : What you see is what you move.

24 Jun 2020, 08:57

I don't understand the advantages of this over the default autohotkey functions? Is it possible the x,y,w,h values could be different ?
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: WinPos_ : What you see is what you move.

24 Jun 2020, 09:15

Skrell wrote:I don't understand the advantages of this over the default autohotkey functions?
When you have the time, read the following topic
 
WinMove is broken on Windows 10
https://www.autohotkey.com/boards/viewtopic.php?t=17955

 
Skrell wrote:Is it possible the x,y,w,h values could be different ?
Yes. In Win 7, if Aero is enabled, windows might apparently be few pixels larger (depending on window (ex)styles)
In Win 10, the windows are apparently smaller than reported by AHK spy.

You may try this:

Code: Select all

Gui, Show, x0 y0 w200 h100
My Scripts and Functions: V1  V2
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

WinPos_OffEdge() : Code updated v0.51

28 Jul 2020, 13:57

Thanks to @jNizM for reporting a math error on OSDTIP_Alert() which used the same formula of WinPos_OffEdge()

WinPos_OffEdge() has been fixed and should work properly on multi-monitor setup.
Also, the example in title post has been simplified to:
 

Code: Select all

#NoEnv
#SingleInstance, Force
Gui  +hwndhGui       
Gui, Show, x64000 y64000 w200 h200 
WinPos_OffEdge(hGui, -1, -1) ; move window to right-bottom corner of primary monitor
Return
My Scripts and Functions: V1  V2
User avatar
boiler
Posts: 16902
Joined: 21 Dec 2014, 02:44

Re: WinPos_ : What you see is what you move.

28 Jul 2020, 15:10

The actual displayed window positions that are retrieved and set by WinPos_Get() and WinPos_Set() are so fundamental to what AHK is often used for that it seems to me they should be incorporated into the language. Basically, the latest versions of Windows have broken WinGetPos and WinMove, and we've just been living with it since. The language needs to evolve to handle significant OS changes like this. Has anyone ever suggested this as a wish list request? I would add one, but I just posted one. I should probably not wear out my welcome and hit it with more than one request at once. :)

Thanks for posting these. :thumbup:
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: WinPos_ : What you see is what you move.

28 Jul 2020, 18:36

Hi @boiler :)
You wrote: The actual displayed window positions that are retrieved and set by WinPos_Get() and WinPos_Set() are so fundamental to what AHK is often used for that it seems to me they should be incorporated into the language. Basically, the latest versions of Windows have broken WinGetPos and WinMove, and we've just been living with it since.
 
Very true. A newbie would most probably presume AHK is broken. :(
 
You wrote:The language needs to evolve to handle significant OS changes like this. Has anyone ever suggested this as a wish list request?


lexikos gives solid reasons here on why he wouldn't incorporate this. So, I don't think A wish/request would help.
While I'm +1 with him, the doc should mention about this and maybe link to any/his own functions that provide a workaround.
 
You wrote:Thanks for posting these. :thumbup:
 
:) :thumbup:
My Scripts and Functions: V1  V2
User avatar
boiler
Posts: 16902
Joined: 21 Dec 2014, 02:44

Re: WinPos_ : What you see is what you move.

28 Jul 2020, 19:37

Thanks for the link to the other thread. It was both informative and entertaining. :D
Skrell
Posts: 302
Joined: 23 Jan 2014, 12:05

Re: WinPos_ : What you see is what you move.

28 Jul 2020, 20:46

I'm sure this is a stupid question, but what do you enter for hWnd to make use of these functions? Is the only way to get this ID by using WinExist()?
User avatar
boiler
Posts: 16902
Joined: 21 Dec 2014, 02:44

Re: WinPos_ : What you see is what you move.

28 Jul 2020, 21:05

Skrell wrote:
28 Jul 2020, 20:46
what do you enter for hWnd to make use of these functions? Is the only way to get this ID by using WinExist()?
There are different ways to get it, but that is one of the easiest ways. Another way is to use WinGet with the ID sub-command. With either method, you need to identify the WinTitle. The easiest case is for the active window. In that case you can get it like this:

Code: Select all

ActiveWinID := WinExist("A")
You can put it directly in as the parameter, such as this:

Code: Select all

PosObj := WinPos_Get(WinExist("A"))
User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: WinPos_ : What you see is what you move.

29 Jul 2020, 16:30

Thanks @boiler
My Scripts and Functions: V1  V2
Skrell
Posts: 302
Joined: 23 Jan 2014, 12:05

Re: WinPos_ : What you see is what you move.

31 Jul 2020, 09:22

@boiler thank you!
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: WinPos_ : What you see is what you move.

03 Aug 2020, 15:02

SKAN wrote:
28 Jul 2020, 18:36
Very true. A newbie would most probably presume AHK is broken. :(
A novice assumes that there is something wrong with the code he wrote. :)

I have learned the truth now. :)

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: Epoch and 51 guests