I had to move some things around. You'll have to find a happy balance between 'IncrementSize' and 'IncrementSpeed' to get the rate/smoothness you desire. What do you think?
Code:
;This one has resize speed control!
;Thread that I got "InsertIntegerAtAddress" and "WM_SIZING_Edge" from: http://www.autohotkey.com/forum/viewtopic.php?t=2441
;Uses Anchor 4.60a by Titan
Gui +Resize +LastFound
gui1ID := WinExist()
Gui, Add, Edit, w350 h250 vg1Edit, Hello
Gui, Add, Button, vg1Ok, Ok
Gui, Add, Button, x+ yp vg1Exit, Exit
Gui, Add, Button, x+ yp vg1Close, Close
Gui, Show,, MinMax`, No Aspect
Gui 2:+Resize +LastFound
gui2ID := WinExist()
Gui, 2:Add, Edit, w300 h200 vg2Edit, Hello
Gui, 2:Add, Button, vg2Ok, Ok
Gui, 2:Add, Button, x+ yp vg2Exit, Exit
Gui, 2:Add, Button, x+ yp vg2Close, Close
Gui, 2:Show,, MinMax`, With Aspect
Gui 3:+Resize +LastFound
gui3ID := WinExist()
Gui, 3:Add, Edit, w150 h100 vg3Edit, Hello
Gui, 3:Add, Button, vg3Ok, Ok
Gui, 3:Add, Button, x+ yp vg3Exit, Exit
Gui, 3:Add, Button, x+ yp vg3Close, Close
Gui, 3:Show,, NOT HANDLED
setGuiSize(gui1ID, "90%", "", "200%", "", False)
setGuiSize(gui2ID, "300", "", "550", "", True)
Return
GuiClose:
ExitApp
Return
GuiSize:
anchor("g1Edit","wh")
anchor("g1Ok","y")
anchor("g1Exit","y")
anchor("g1Close","y")
Return
WM_SIZING(wParam, lParam, msg)
{
global
Critical
static guiID, debounce
WM_SIZING_Edge := wParam
guiSizeAddr := lParam
If GetKeyState("LButton","P") and (!debounce) ;for each resizing, only get the gui id and info the first time through
{ debounce=1
CoordMode, MOUSE, SCREEN
MouseGetPos,,, guiID
getGuiSize(guiID, _ratio, _min1, _min2, _max1, _max2, _aspect) ;get the ratio/min/max info for this gui
SetTimer, WinSlowerDowner, %IncrementSpeed%
}
If !GetKeyState("LButton","P") ;mouse released - reset
{ debounce=0
SetTimer, WinSlowerDowner, Off
}
IfEqual, _ratio, , Return False ;if not a handled gui, exit func
WinGetPos, CurrX, CurrY, CurrW, CurrH, ahk_id %guiID% ;Get current x,y,w,h
newCurrW := CurrX + CurrW
newCurrH := CurrY + CurrH
InsertIntegerAtAddress(CurrX, lParam, Rect_left, 4) ;set x coord of left side of gui - these make sure the drag does not happen!
InsertIntegerAtAddress(CurrY, lParam, Rect_top, 4) ;set y coord of top side of gui
InsertIntegerAtAddress(newCurrW, lParam, Rect_right, 4) ;set x coord of right side of gui
InsertIntegerAtAddress(newCurrH, lParam, Rect_bottom, 4) ;set y coord of bottom side of gui
Return True
WinSlowerDowner:
Critical
SetWinDelay, 0
If !GetKeyState("LButton","P")
{ Exit
}
tmpBL := A_BatchLines
SetBatchLines, 20ms
CoordMode, MOUSE, SCREEN
MouseGetPos, tX, tY
WinGetPos, CurrX, CurrY, CurrW, CurrH, ahk_id %guiID% ;Get current x,y,w,h
If WM_SIZING_Edge in 2,8 ;right and bottom-right border drag
{ idealW := Abs(CurrX-tX) ;what the width should be
tmpCurrW := CurrW ;current width
If(tmpCurrW < idealW) ;if current is less than it should be
{ tmpCurrW += IncrementSize
idealW := idealW < _max1 ? idealW : _max1 ;keep ideal width within max
tmpCurrW := tmpCurrW > idealW ? idealW : tmpCurrW ;if greater than ideal width, make equal
tmpCurrH := _aspect ? Round(tmpCurrW * (1/_Ratio)) : CurrH ;preserve aspect ratio or not
WinMove, ahk_id %guiID% ,,,, tmpCurrW, tmpCurrH ;do the deed
}
Else If(tmpCurrW > idealW) ;current is greater than it should be
{ tmpCurrW -= IncrementSize
idealW := idealW > _min1 ? idealW : _min1 ;keep ideal width within min
tmpCurrW := tmpCurrW < idealW ? idealW : tmpCurrW ;if less than ideal width, make equal
tmpCurrH := _aspect ? Round(tmpCurrW * (1/_Ratio)) : CurrH ;preserve aspect ratio or not
WinMove, ahk_id %guiID% ,,,, tmpCurrW, tmpCurrH ;do the deed
}
}
If(WM_SIZING_Edge = 6) ;bottom border drag
{ idealH := Abs(CurrY-tY) ;what the height should be
tmpCurrH := CurrH ;current height
If(tmpCurrH < idealH) ;if current is less than it should be
{ tmpCurrH += IncrementSize
idealH := idealH < _max2 ? idealH : _max2 ;keep ideal height within max
tmpCurrH := tmpCurrH > idealH ? idealH : tmpCurrH ;if greater than ideal height, make equal
tmpCurrW := _aspect ? Round(tmpCurrH * _Ratio) : CurrW ;preserve aspect ratio or not
WinMove, ahk_id %guiID% ,,,, tmpCurrW, tmpCurrH ;do the deed
}
Else If(tmpCurrH > idealH) ;current is greater than it should be
{ tmpCurrH -= IncrementSize
idealH := idealH > _min2 ? idealH : _min2 ;keep ideal height within min
tmpCurrH := tmpCurrH < idealH ? idealH : tmpCurrH ;if less than ideal height, make equal
tmpCurrW := _aspect ? Round(tmpCurrH * _Ratio) : CurrW ;preserve aspect ratio or not
WinMove, ahk_id %guiID% ,,,, tmpCurrW, tmpCurrH ;do the deed
}
}
;~ TODO:
;~ If(WM_SIZING_Edge = 3) ;top border drag
;~ If(WM_SIZING_Edge = 4) ;top-left border
;~ If WM_SIZING_Edge In 1,7 ;left, bottom-left borders
SetBatchLines, %tmpBL%
Return
/*
From MSDN (with edits):
lParam - RECT Structure containing window coords
typedef struct _RECT {
LONG left; (0) offset in structure
LONG top; (4)
LONG right; (8)
LONG bottom; (12)
} RECT, *PRECT;
wParam - Specifies which edge of the window is being sized. This
parameter can be one of the following values.
WMSZ_LEFT (1) Left edge
WMSZ_RIGHT (2) Right edge
WMSZ_TOP (3) Top edge
WMSZ_TOPLEFT (4) Top-left corner
WMSZ_TOPRIGHT (5) Top-right corner
WMSZ_BOTTOM (6) Bottom edge
WMSZ_BOTTOMLEFT (7) Bottom-left corner
WMSZ_BOTTOMRIGHT (8) Bottom-right corner
*/
}
getGuiSize(_0, ByRef _1="", ByRef _2="", ByRef _3="", ByRef _4="", ByRef _5="", ByRef _6="", ByRef _7="", ByRef _8="", ByRef _9="", ByRef _10="", ByRef _11="", ByRef _12="", ByRef _13="", ByRef _14="") ;get the info for the given gui - vars return blank if no match - allow up to 15 params (maybe the basis of another array handler?)
{
global
local tmp1
Rect_left = 0 ;set up some constants for the sizing
Rect_top = 4
Rect_right = 8
Rect_bottom = 12
IncrementSize = 15
IncrementSpeed = 50
IfEqual, _0,, Return Please provide an ID or Gui Number
If _0 Is Digit ;must have passed gui number instead of ID
{ Gui, %_0%:+LastFound
_0 := WinExist()
}
StringSplit, _ggsStuff, %_0%_ggsArray, `,
IfEqual, _ggsStuff0, 0, Return ;if not a match, exit func
Loop, %_ggsStuff0%
{ tmp1 = _%A_Index%
%tmp1% := _ggsStuff%A_Index%
}
}
/*
defaults:
_ID = gui1 id
_minwidth = guiID.width
_minheight = guiID.height
_maxWidth = system window max width
_maxHeight = system window max height
*/
setGuiSize(_ID=1, _minWidth="", _minHeight="", _maxWidth="", _maxHeight="", _aspect=True) ;set the ratio/min/max info for the given gui
{
SysGet, SM_CXMAXTRACK, 59 ;get largest window size
SysGet, SM_CYMAXTRACK, 60
If _ID Is Digit ;must have passed gui number instead of window ID
{ Gui, %_ID%:+LastFound
_ID := WinExist()
}
WinGetPos,,,guiW, guiH, ahk_id %_ID% ;get gui w,h and calc the size ratio
_Ratio := guiW / guiH ;multiply the current width by _Ratio to get the new height, multiply the current height by (1/_Ratio) to get the new width
;this sets the vars for max w,h to keep aspect ratio
If(_minWidth) ;if minimum width is given
{ IfInString, _minWidth, `% ;if a percent is specified
{ StringReplace, _minWidth, _minWidth, `%,,All
_minWidth := _minWidth / 100 ;did it this way because _minWidth/100 produced '0' - an integer
_min1 := Round(guiW * _minWidth) ;this is the width
_min2 := Round(_min1 * (1/_Ratio)) ;this is the height
}
Else ;must be 'px'
{ StringReplace, _minWidth, _minWidth, px,,All ;just in case
_min1 := _minWidth ;this is the width
_min2 := Round(_min1 * (1/_Ratio)) ;this is the height
}
}
Else If(_minHeight) ;_minHeight given
{ IfInString, _minHeight, `% ;if a percent is specified
{ StringReplace, _minHeight, _minHeight, `%,,All
_minHeight := _minHeight / 100 ;did it this way because _minHeight/100 produced '0' - an integer
_min2 := guiH * _minHeight ;this is the height
_min1 := Round(_min2 * _Ratio) ;this is the width
}
Else ;must be 'px'
{ StringReplace, _minHeight, _minHeight, px,,All ;just in case
_min2 := _minHeight ;this is the height
_min1 := Round(_min2 * _Ratio) ;this is the width
}
}
Else ;no min given
{ _min1 := guiW ;set min w,h = initial gui w,h
_min2 := guiH
}
If(_maxWidth) ;if maximum width is given
{ IfInString, _maxWidth, `% ;if a percent is specified
{ StringReplace, _maxWidth, _maxWidth, `%,,All
_maxWidth /= 100
_max1 := guiW * _maxWidth ;this is the width
_max2 := (_max1 * (1/_Ratio)) ;this is the height
}
Else ;must be 'px'
{ StringReplace, _maxWidth, _maxWidth, px,,All ;just in case
_max1 := _maxWidth ;this is the width
_max2 := Round(_max1 * (1/_Ratio)) ;this is the height
}
}
Else If(_maxHeight) ;_maxHeight given
{ IfInString, _maxHeight, `% ;if a percent is specified
{ StringReplace, _maxHeight, _maxHeight, `%,,All
_maxHeight /= 100
_max2 := guiH * _maxHeight ;this is the height
_max1 := (_max2 * _Ratio) ;this is the width
}
Else ;must be 'px'
{ StringReplace, _maxHeight, _maxHeight, px,,All ;just in case
_max2 := _maxHeight ;this is the height
_max1 := Round(_max2 * _Ratio) ;this is the width
}
}
Else ;no max given
{ _max1 := SM_CXMAXTRACK ;largest window possible in system
_max2 := SM_CYMAXTRACK
}
%_ID%_ggsArray := _ratio "," _min1 "," _min2 "," _max1 "," _max2 "," _aspect
globalWrapper(_ID "_ggsArray", %_ID%_ggsArray)
OnMessage(0x214,"WM_SIZING") ;this will happen once for every window set, but that shouldn't cause any problems
}
globalWrapper(victim, content) ;this turns a dynamic local variable into a global variable
{
global
%victim% := content
}
InsertIntegerAtAddress(pInteger, pAddress, pOffset = 0, pSize = 4) ;update this to putnum
{
mask := 0xFF ; This serves to isolate each byte, one by one.
Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
{
DllCall("RtlFillMemory", UInt, pAddress + pOffset + A_Index - 1, UInt, 1
, UChar, (pInteger & mask) >> 8 * (A_Index - 1))
mask := mask << 8 ; Set it up for isolation of the next byte.
}
}
/*
Function: Anchor
Defines how controls should be automatically positioned relative to the new dimensions of a window when resized.
Parameters:
cl - a control HWND, associated variable name or ClassNN to operate on
a - (optional) one or more of the anchors: 'x', 'y', 'w' (width) and 'h' (height),
optionally followed by a relative factor, e.g. "x h0.5"
r - (optional) true to redraw controls, recommended for GroupBox and Button types
Examples:
> "xy" ; bounds a control to the bottom-left edge of the window
> "w0.5" ; any change in the width of the window will resize the width of the control on a 2:1 ratio
> "h" ; similar to above but directrly proportional to height
Remarks:
To assume the current window size for the new bounds of a control (i.e. resetting) simply omit the second and third parameters.
However if the control had been created with DllCall() and has its own parent window,
the container AutoHotkey created GUI must be made default with the +LastFound option prior to the call.
For a complete example see anchor-example.ahk.
License:
- Version 4.60a by Titan <http://www.autohotkey.net/~Titan/#anchor>
- GNU General Public License 3.0 or higher <http://www.gnu.org/licenses/gpl-3.0.txt>
*/
Anchor(i, a = "", r = false) {
static c, cs = 12, cx = 255, cl = 0, g, gs = 8, gl = 0, gpi, gw, gh, z = 0, k = 0xffff
If z = 0
VarSetCapacity(g, gs * 99, 0), VarSetCapacity(c, cs * cx, 0), z := true
If (!WinExist("ahk_id" . i)) {
GuiControlGet, t, Hwnd, %i%
If ErrorLevel = 0
i := t
Else ControlGet, i, Hwnd, , %i%
}
VarSetCapacity(gi, 68, 0), DllCall("GetWindowInfo", "UInt", gp := DllCall("GetParent", "UInt", i), "UInt", &gi)
, giw := NumGet(gi, 28, "Int") - NumGet(gi, 20, "Int"), gih := NumGet(gi, 32, "Int") - NumGet(gi, 24, "Int")
If (gp != gpi) {
gpi := gp
Loop, %gl%
If (NumGet(g, cb := gs * (A_Index - 1)) == gp) {
gw := NumGet(g, cb + 4, "Short"), gh := NumGet(g, cb + 6, "Short"), gf := 1
Break
}
If (!gf)
NumPut(gp, g, gl), NumPut(gw := giw, g, gl + 4, "Short"), NumPut(gh := gih, g, gl + 6, "Short"), gl += gs
}
ControlGetPos, dx, dy, dw, dh, , ahk_id %i%
Loop, %cl%
If (NumGet(c, cb := cs * (A_Index - 1)) == i) {
If a =
{
cf = 1
Break
}
giw -= gw, gih -= gh, as := 1, dx := NumGet(c, cb + 4, "Short"), dy := NumGet(c, cb + 6, "Short")
, cw := dw, dw := NumGet(c, cb + 8, "Short"), ch := dh, dh := NumGet(c, cb + 10, "Short")
Loop, Parse, a, xywh
If A_Index > 1
av := SubStr(a, as, 1), as += 1 + StrLen(A_LoopField)
, d%av% += (InStr("yh", av) ? gih : giw) * (A_LoopField + 0 ? A_LoopField : 1)
DllCall("SetWindowPos", "UInt", i, "Int", 0, "Int", dx, "Int", dy
, "Int", InStr(a, "w") ? dw : cw, "Int", InStr(a, "h") ? dh : ch, "Int", 4)
If r != 0
DllCall("RedrawWindow", "UInt", i, "UInt", 0, "UInt", 0, "UInt", 0x0101) ; RDW_UPDATENOW | RDW_INVALIDATE
Return
}
If cf != 1
cb := cl, cl += cs
bx := NumGet(gi, 48), by := NumGet(gi, 16, "Int") - NumGet(gi, 8, "Int") - gih - NumGet(gi, 52)
If cf = 1
dw -= giw - gw, dh -= gih - gh
NumPut(i, c, cb), NumPut(dx - bx, c, cb + 4, "Short"), NumPut(dy - by, c, cb + 6, "Short")
, NumPut(dw, c, cb + 8, "Short"), NumPut(dh, c, cb + 10, "Short")
Return, true
}