I have been working on my first real script in AutoHotkey v2 and I noticed I had a need for resizing my Gui controls. After I had about 4 custom Gui_Size functions, I concluded I needed a better more flexible way. So this is my solution to that problem.
I kept expanding the properties beyond what I actually needed at the time but hopefully I did not over complicate it. The hard part was not necessarily the actual sizing and positioning but figuring out the best way to get the options and what they mean from the user. The good thing is it would be pretty easy to add new property options.
I assume this does something similar to Anchor and AutoXYWH in v1 although I have not really used those. AHK v2 has a very different Gui structure so starting from scratch seemed a better way to go.
[Class] GuiReSizer
Code: Select all
;{ [Class] GuiReSizer
; Fanatic Guru
; Version 2023 03 13
;
; Update 2023 02 15: Add more Min Max properties and renamed some Properties
; Update 2023 03 13: Major rewrite. Converted to Class to allow for Methods
;
; #Requires AutoHotkey v2.0.2+
;
; Class to Handle the Resizing of Gui and
; Move and Resize Controls
;
;------------------------------------------------
;
; Class GuiReSizer
;
; Call: GuiReSizer(GuiObj, WindowMinMax, Width, Height)
;
; Parameters:
; 1) {GuiObj} Gui Object
; 2) {WindowMinMax} Window status, 0 = neither minimized nor maximized, 1 = maximized, -1 = minimized
; 3) {Width} Width of GuiObj
; 4) {Height} Height of GuiObj
;
; Normally parameters are passed by a callback from {gui}.OnEvent("Size", GuiReSizer)
;
; Properties: Abbr Description
; X X positional offset from margins
; Y Y positional offset from margins
; XP X positional offset from margins as percentage of Gui width
; YP Y positional offset from margins as percentage of Gui height
; OriginX OX control origin X defaults to 0 or left side of control, this relocates the origin
; OriginXP OXP control origin X as percentage of Gui width defaults to 0 or left side of control, this relocates the origin
; OriginY OY control origin Y defaults to 0 or top side of control, this relocates the origin
; OriginYP OYP control origin Y as percentage of Gui height defaults to 0 or top side of control, this relocates the origin
; Width W width of control
; WidthP WP width of control as percentage of Gui width
; Height H height of control
; HeightP HP height of control as percentage of Gui height
; MinX mininum X offset
; MaxX maximum X offset
; MinY minimum Y offset
; MaxY maximum Y offset
; MinWidth MinW minimum control width
; MaxWidth MaxW maximum control width
; MinHeight MinH minimum control height
; MaxHeight MaxH maximum control height
; Cleanup C {true/false} when set to true will redraw this control each time to cleanup artifacts, normally not required and causes flickering
; Function F {function} custom function that will be called for this control
; Anchor A {contol object} anchor control so that size and position commands are in relation to another control
; AnchorIn AI {true/false} controls where the control is restricted to the inside of another control
;
; Methods:
; Now(GuiObj) will force a manual Call now for {GuiObj}
; Opt({switches}) same as Options method
; Options({switches}) all options are set as a string with each switch separated by a space "x10 yp50 oCM"
; Flags:
; x{number} X
; y{number} Y
; xp{number} XP
; yp{number} YP
; wp{number} WidthP
; hp{number} HeightP
; w{number} Width
; h{number} Height
; minx{number} MinX
; maxx{number} MaxX
; miny{number} MinY
; maxy{number} MaxY
; minw{number} MinWidth
; maxw{number} MaxWidth
; minh{number} MinHeight
; maxh{number} MaxHeight
; oxp{number} OriginXP
; oyp{number} OriginYP
; ox{number} OriginX
; oy{number} OriginY
; o{letters} Origin: "L" left, "C" center, "R" right, "T" top, "M" middle, "B" bottom; may use 1 or 2 letters
;
; Gui Properties:
; Init {Gui}.Init := 1, will cause all controls of the Gui to be redrawn on next function call
; {Gui}.Init := 2, will also reinitialize abbreviations
;
Class GuiReSizer
{
;{ Call GuiReSizer
Static Call(GuiObj, WindowMinMax, GuiW, GuiH)
{
; On Initial display of Gui use redraw to cleanup first positioning
Try
(GuiObj.Init)
Catch
GuiObj.Init := 2 ; Redraw Twice on Initial Call(called on initial Show)
If WindowMinMax = -1 ; Do nothing if window minimized
Return
;{ Loop through all Controls of Gui
For Hwnd, CtrlObj in GuiObj
{
;{ Initializations on First Call
If GuiObj.Init = 2
{
Try CtrlObj.OriginX := CtrlObj.OX
Try CtrlObj.OriginXP := CtrlObj.OXP
Try CtrlObj.OriginY := CtrlObj.OY
Try CtrlObj.OriginYP := CtrlObj.OYP
Try CtrlObj.Width := CtrlObj.W
Try CtrlObj.WidthP := CtrlObj.WP
Try CtrlObj.Height := CtrlObj.H
Try CtrlObj.HeightP := CtrlObj.HP
Try CtrlObj.MinWidth := CtrlObj.MinW
Try CtrlObj.MaxWidth := CtrlObj.MaxW
Try CtrlObj.MinHeight := CtrlObj.MinH
Try CtrlObj.MaxHeight := CtrlObj.MaxH
Try CtrlObj.Function := CtrlObj.F
Try CtrlObj.Cleanup := CtrlObj.C
Try CtrlObj.Anchor := CtrlObj.A
Try CtrlObj.AnchorIn := CtrlObj.AI
If !CtrlObj.HasProp("AnchorIn")
CtrlObj.AnchorIn := true
}
;}
;{ Initialize Current Positions and Sizes
CtrlObj.GetPos(&CtrlX, &CtrlY, &CtrlW, &CtrlH)
LimitX := AnchorW := GuiW, LimitY := AnchorH := GuiH, OffsetX := OffsetY := 0
;}
;{ Check for Anchor
If CtrlObj.HasProp("Anchor")
{
If Type(CtrlObj.Anchor) = "Gui.Tab"
{
CtrlObj.Anchor.GetPos(&AnchorX, &AnchorY, &AnchorW, &AnchorH)
Offset(CtrlObj, &TabX, &TabY)
CtrlX := CtrlX - TabX, CtrlY := CtrlY - TabY
AnchorW := AnchorW + AnchorX - TabX, AnchorH := AnchorH + AnchorY - TabY
}
Else
{
CtrlObj.Anchor.GetPos(&AnchorX, &AnchorY, &AnchorW, &AnchorH)
If CtrlObj.HasProp("X") or CtrlObj.HasProp("XP")
OffsetX := AnchorX
If CtrlObj.HasProp("Y") or CtrlObj.HasProp("YP")
OffsetY := AnchorY
}
If CtrlObj.AnchorIn
LimitX := AnchorW, LimitY := AnchorH
}
;}
;{ OriginX
If CtrlObj.HasProp("OriginX") and CtrlObj.HasProp("OriginXP")
OriginX := CtrlObj.OriginX + (CtrlW * CtrlObj.OriginXP)
Else If CtrlObj.HasProp("OriginX") and !CtrlObj.HasProp("OriginXP")
OriginX := CtrlObj.OriginX
Else If !CtrlObj.HasProp("OriginX") and CtrlObj.HasProp("OriginXP")
OriginX := CtrlW * CtrlObj.OriginXP
Else
OriginX := 0
;}
;{ OriginY
If CtrlObj.HasProp("OriginY") and CtrlObj.HasProp("OriginYP")
OriginY := CtrlObj.OriginY + (CtrlH * CtrlObj.OriginYP)
Else If CtrlObj.HasProp("OriginY") and !CtrlObj.HasProp("OriginYP")
OriginY := CtrlObj.OriginY
Else If !CtrlObj.HasProp("OriginY") and CtrlObj.HasProp("OriginYP")
OriginY := CtrlH * CtrlObj.OriginYP
Else
OriginY := 0
;}
;{ X
If CtrlObj.HasProp("X") and CtrlObj.HasProp("XP")
CtrlX := Mod(LimitX + CtrlObj.X + (AnchorW * CtrlObj.XP) - OriginX, LimitX)
Else If CtrlObj.HasProp("X") and !CtrlObj.HasProp("XP")
CtrlX := Mod(LimitX + CtrlObj.X - OriginX, LimitX)
Else If !CtrlObj.HasProp("X") and CtrlObj.HasProp("XP")
CtrlX := Mod(LimitX + (AnchorW * CtrlObj.XP) - OriginX, LimitX)
;}
;{ Y
If CtrlObj.HasProp("Y") and CtrlObj.HasProp("YP")
CtrlY := Mod(LimitY + CtrlObj.Y + (AnchorH * CtrlObj.YP) - OriginY, LimitY)
Else If CtrlObj.HasProp("Y") and !CtrlObj.HasProp("YP")
CtrlY := Mod(LimitY + CtrlObj.Y - OriginY, LimitY)
Else If !CtrlObj.HasProp("Y") and CtrlObj.HasProp("YP")
CtrlY := Mod(LimitY + AnchorH * CtrlObj.YP - OriginY, LimitY)
;}
;{ Width
If CtrlObj.HasProp("Width") and CtrlObj.HasProp("WidthP")
(CtrlObj.Width > 0 and CtrlObj.WidthP > 0 ? CtrlW := CtrlObj.Width + AnchorW * CtrlObj.WidthP : CtrlW := CtrlObj.Width + AnchorW + AnchorW * CtrlObj.WidthP - CtrlX)
Else If CtrlObj.HasProp("Width") and !CtrlObj.HasProp("WidthP")
(CtrlObj.Width > 0 ? CtrlW := CtrlObj.Width : CtrlW := AnchorW + CtrlObj.Width - CtrlX)
Else If !CtrlObj.HasProp("Width") and CtrlObj.HasProp("WidthP")
(CtrlObj.WidthP > 0 ? CtrlW := AnchorW * CtrlObj.WidthP : CtrlW := AnchorW + AnchorW * CtrlObj.WidthP - CtrlX)
;}
;{ Height
If CtrlObj.HasProp("Height") and CtrlObj.HasProp("HeightP")
(CtrlObj.Height > 0 and CtrlObj.HeightP > 0 ? CtrlH := CtrlObj.Height + AnchorH * CtrlObj.HeightP : CtrlH := CtrlObj.Height + AnchorH + AnchorH * CtrlObj.HeightP - CtrlY)
Else If CtrlObj.HasProp("Height") and !CtrlObj.HasProp("HeightP")
(CtrlObj.Height > 0 ? CtrlH := CtrlObj.Height : CtrlH := AnchorH + CtrlObj.Height - CtrlY)
Else If !CtrlObj.HasProp("Height") and CtrlObj.HasProp("HeightP")
(CtrlObj.HeightP > 0 ? CtrlH := AnchorH * CtrlObj.HeightP : CtrlH := AnchorH + AnchorH * CtrlObj.HeightP - CtrlY)
;}
;{ Min Max
(CtrlObj.HasProp("MinX") ? MinX := CtrlObj.MinX : MinX := -999999)
(CtrlObj.HasProp("MaxX") ? MaxX := CtrlObj.MaxX : MaxX := 999999)
(CtrlObj.HasProp("MinY") ? MinY := CtrlObj.MinY : MinY := -999999)
(CtrlObj.HasProp("MaxY") ? MaxY := CtrlObj.MaxY : MaxY := 999999)
(CtrlObj.HasProp("MinWidth") ? MinW := CtrlObj.MinWidth : MinW := 0)
(CtrlObj.HasProp("MaxWidth") ? MaxW := CtrlObj.MaxWidth : MaxW := 999999)
(CtrlObj.HasProp("MinHeight") ? MinH := CtrlObj.MinHeight : MinH := 0)
(CtrlObj.HasProp("MaxHeight") ? MaxH := CtrlObj.MaxHeight : MaxH := 999999)
CtrlX := MinMax(CtrlX, MinX, MaxX)
CtrlY := MinMax(CtrlY, MinY, MaxY)
CtrlW := MinMax(CtrlW, MinW, MaxW)
CtrlH := MinMax(CtrlH, MinH, MaxH)
;}
;{ Move and Size
CtrlObj.Move(CtrlX + OffsetX, CtrlY + OffsetY, CtrlW, CtrlH)
;}
;{ Redraw on Cleanup or GuiObj.Init
If GuiObj.Init or (CtrlObj.HasProp("Cleanup") and CtrlObj.Cleanup = true)
CtrlObj.Redraw()
;}
;{ Custom Function Call
If CtrlObj.HasProp("Function")
CtrlObj.Function(GuiObj) ; CtrlObj is hidden 'this' first parameter
;}
}
;}
;{ Reduce GuiObj.Init Counter and Check for Call again
If (GuiObj.Init := Max(GuiObj.Init - 1, 0))
{
GuiObj.GetClientPos(, , &AnchorW, &AnchorH)
GuiReSizer(GuiObj, WindowMinMax, AnchorW, AnchorH)
}
;}
;{ Functions: Helpers
MinMax(Num, MinNum, MaxNum) => Min(Max(Num, MinNum), MaxNum)
Offset(CtrlObj, &OffsetX, &OffsetY)
{
Hwnd := CtrlObj.Hwnd
hParentWnd := DllCall("GetParent", "Ptr", Hwnd, "Ptr")
RECT := Buffer(16, 0)
DllCall("GetWindowRect", "Ptr", hParentWnd, "Ptr", RECT)
DllCall("MapWindowPoints", "Ptr", 0, "Ptr", DllCall("GetParent", "Ptr", hParentWnd, "Ptr"), "Ptr", RECT, "UInt", 1)
OffsetX := NumGet(RECT, 0, "Int"), OffsetY := NumGet(RECT, 4, "Int")
}
;}
}
;}
;{ Methods:
;{ Options
Static Opt(CtrlObj, Options) => GuiReSizer.Options(CtrlObj, Options)
Static Options(CtrlObj, Options)
{
For Option in StrSplit(Options, " ")
{
For Abbr, Cmd in Map(
"xp", "XP", "yp", "YP", "x", "X", "y", "Y",
"wp", "WidthP", "hp", "HeightP", "w", "Width", "h", "Height",
"minx", "MinX", "maxx", "MaxX", "miny", "MinY", "maxy", "MaxY",
"minw", "MinWidth", "maxw", "MaxWidth", "minh", "MinHeight", "maxh", "MaxHeight",
"oxp", "OriginXP", "oyp", "OriginYP", "ox", "OriginX", "oy", "OriginY")
If RegExMatch(Option, "i)^" Abbr "([\d.-]*$)", &Match)
{
CtrlObj.%Cmd% := Match.1
Break
}
; Origin letters
If SubStr(Option, 1, 1) = "o"
{
Flags := SubStr(Option, 2)
If Flags ~= "i)l" ; left
CtrlObj.OriginXP := 0
If Flags ~= "i)c" ; center (left to right)
CtrlObj.OriginXP := 0.5
If Flags ~= "i)r" ; right
CtrlObj.OriginXP := 1
If Flags ~= "i)t" ; top
CtrlObj.OriginYP := 0
If Flags ~= "i)m" ; middle (top to bottom)
CtrlObj.OriginYP := 0.5
If Flags ~= "i)b" ; bottom
CtrlObj.OriginYP := 1
}
}
}
;}
;{ Now
Static Now(GuiObj, Redraw := true, Init := 2)
{
If Redraw
GuiObj.Init := Init
GuiObj.GetClientPos(, , &Width, &Height)
GuiReSizer(GuiObj, WindowMinMax := 1, Width, Height)
}
;}
;}
}
;}
Example Usage
Code: Select all
guiList := Gui(, "Test - List"), guiList.Opt("+Resize +MinSize250x150")
guiList.OnEvent("Size", GuiReSizer) ; assign GuiReSizer to handle all size changes for this Gui
guiList.Button := {} ; required because you are going to have Button sub definititions, ie. more than one Button
guiList.Button.One := guiList.Add("Button", "Default", "One")
guiList.Button.One.X := 10 ; 10 from Left Margin
guiList.Button.Two := guiList.Add("Button", "yp", "Two")
guiList.Button.Two.X := 20 ; 20 from Left Margin
guiList.Button.Two.XP := 0.2 ; 20% from Left Margin
guiList.Button.Three := guiList.Add("Button", "yp", "Three")
guiList.Button.Three.XP := -0.4 ; 40% from Right Margin
guiList.Button.Four := guiList.Add("Button", "yp", "Four")
guiList.Button.Four.X := -10 ; -10 from Right Margin
guiList.Button.Four.OriginXP := 1 ; OriginX is 100% of Width of control ie. Right Side
guiList.ListView := guiList.Add("ListView", "+Grid -Multi xm r20 w750", ["This", "That", "Other"])
guiList.ListView.Function := ListView_Columns ; Call Custom Function to Adjust Column Width
guiList.ListView.Width := -10 ; 10 from Left Margin
guiList.ListView.HeightP := -0.60 ; 60% from Bottom Margin
guiList.GroupBox := guiList.Add("GroupBox", , "Boxxie")
guiList.GroupBox.XP := 0.20 ; 20% from Left Margin
guiList.GroupBox.YP := 0.45 ; 45% from Top Margin
guiList.GroupBox.WidthP := -0.50 ; Right Edge maintain 50% from Right Margin
guiList.GroupBox.HeightP := -0.30 ; Bottom Edge maintain 30% from Bottom Margin
SimpleNameForEdit := guiList.Add("Edit", , "In the Box") ; do not need to use an Object structure for naming, 'guiList.Edit' would just be my preferred convention
; SimpleNameForEdit.OriginXP := 0.5 ; Origin to Center
; SimpleNameForEdit.OriginYP := 0.5 ; Origin to Middle
; SimpleNameForEdit.XP := 0.35 ; Boxxie center of .20 to .50
; SimpleNameForEdit.YP := 0.575 ; Boxxie middle of .45 to .70
; SimpleNameForEdit.Y := 3 ; Boxxie actual box is slightly off center due to Title text
GuiReSizer.Opt(SimpleNameForEdit, "oCM xp.35 yp.575 y3") ; use Options method to set position same as above
guiList.Button.TopLeft := guiList.Add("Button", "Default", "TopLeft")
guiList.Button.TopLeft.XP := 0.20 ; 20% from Left Margin
guiList.Button.TopLeft.YP := 0.70 ; 70% from Top Margin
guiList.Button.TopLeft.WidthP := 0.20 ; 20% Width of Gui Width
guiList.Button.TopLeft.Height := 20 ; 20 Height of Gui Height
guiList.Button.BottomLeft := guiList.Add("Button", "Default", "BottomLeft")
guiList.Button.BottomLeft.XP := 0.20 ; 20% Left Margin, Margin to OriginX of .5 (Middle)
guiList.Button.BottomLeft.YP := -0.02 ; 2% from Bottom Margin, OriginY which is set to 100% of height or bottom
guiList.Button.BottomLeft.OriginXP := 0.5 ; X Origin Adjust to 50% (Middle of Button)
guiList.Button.BottomLeft.OriginYP := 1 ; Origin of Button is Bottom Left
guiList.Button.BottomLeft.WidthP := 0.25 ; 25% Width of Gui Width
guiList.Button.BottomLeft.HeightP := 0.05 ; 5% Height of Gui Height
guiList.Button.BottomLeft.MinHeight := 20 ; Minimum Height of 15
guiList.Button.TopRight := guiList.Add("Button", "Default", "TopRight")
guiList.Button.TopRight.X := -80 ; 80 from Right Margin (Width of Control Below)
guiList.Button.TopRight.XP := -0.15 ; 15% from Right Margin (this plus above effectively positions 15% edge to Right Margin)
guiList.Button.TopRight.YP := -0.58 ; 58% from Bottom Margin
guiList.Button.TopRight.MaxX := 1200 ; Max X Position of 1200
guiList.Button.TopRight.Width := 80 ; 80 Width
guiList.Button.TopRight.Height := 20 ; 20 Height of Gui Height
guiList.Button.BottomRight := guiList.Add("Button", "Default", "BottomRight") ; Centered below TopRight Right Edge
guiList.Button.BottomRight.OriginXP := 0.5 ; X Origin Adjusted to 50% (Middle of Button)
guiList.Button.BottomRight.XP := -0.15 ; 15% from Right Margin (Same as TopRight Button but with center OriginX)
guiList.Button.BottomRight.MaxX := 1130 ; Max X Position of 1130 (1200 + 80 - 150 = TopRight Right Edge to Center of Button)
guiList.Button.BottomRight.YP := -0.20 ; 20% from Bottom Margin
guiList.Button.BottomRight.WidthP := 0.25 ; 25% Width of Gui Width
guiList.Button.BottomRight.MaxWidth := 300 ; Max Width of 300
guiList.Button.BottomRight.MinWidth := 75 ; Minimum Width of 75
guiList.Button.BottomRight.Height := 20 ; 20 Height
ListView_Columns(CtrlObj, GuiObj) ; custom called function
{
CtrlObj.ModifyCol(3, "AutoHdr")
}
guiTab := Gui(, "Tab 1"), guiTab.Opt("+Resize +MinSize250x150")
guiTab.OnEvent("Size", GuiReSizer)
guiTab.Text := guiTab.Add("Text", , "Tabs ->")
guiTab.Text.XP := 0.02 ; 2% from Left Margin
guiTab.Text.YP := 0.35 ; 35% from Left Margin
guiTab.Tab := guiTab.Add("Tab3", "x100 y100 w500 h500", ["General", "View", "Settings"])
guiTab.Tab.XP := 0.25 ; 25% from Left Margin
guiTab.Tab.YP := 0.35 ; 35% from Top Margin
guiTab.Tab.C := true ; Force Redraw everytime with this GUI (usually not needed)
guiTab.Tab.W := -10 ; Adjust Width to maintain 10 from Right Margin
guiTab.Tab.H := -10 ; Adjust Height to maintain 10 from Bottom Margin
guiTab.Tab.Button := {}
guiTab.Tab.UseTab()
guiTab.Tab.Button.NoTabOne := guiTab.Add("Button", , "One No Tab (Click to Move)")
guiTab.Tab.Button.NoTabOne.XP := 0.25 ; 25% width of Gui Width
guiTab.Tab.Button.NoTabOne.Y := 5 ; 5 from Top Margin of Gui
guiTab.Tab.Button.NoTabOne.WidthP := 0.70 ; 70% Width of Gui Width
guiTab.Tab.Button.NoTabOne.OnEvent("Click", guiTab_Click_to_Move)
guiTab_Click_to_Move(GuiCtrlObj, Info)
{
If guiTab.Tab.Button.NoTabOne.XP = 0.25
guiTab.Tab.Button.NoTabOne.XP := 0.10
Else
guiTab.Tab.Button.NoTabOne.XP := 0.25
GuiReSizer.Now(guiTab) ; forced resize call to adjust position
}
guiTab.Tab.Button.NoTabTwo := guiTab.Add("Button", "x100 y100", "Two Anchored")
guiTab.Tab.Button.NoTabTwo.Anchor := guiTab.Tab.Button.NoTabOne
guiTab.Tab.Button.NoTabTwo.AnchorIn := false
guiTab.Tab.Button.NoTabTwo.XP := 0.50 ; 50% from Anchor Top Left Corner
guiTab.Tab.Button.NoTabTwo.Y := 25 ; 25 Below Anchor Top Left Corner
guiTab.Tab.Button.NoTabTwo.WP := 0.5 ; 50% Width of Anchor Width
guiTab.Tab.Button.NoTabTwo.OriginXP := 0.5 ; Origin X at 50% or Center
guiTab.Tab.UseTab(1)
guiTab.Tab.Button.One := guiTab.Add("Button", "Default x150 y150", "One in General")
guiTab.Tab.Button.One.Anchor := guiTab.Tab ; need to set Anchor Tab for Controls in Tabs
guiTab.Tab.Button.One.XP := 0.50 ; 50% width of Anchor Tab
guiTab.Tab.Button.One.YP := -0.25 ; 25% width of Anchor Tab
guiTab.Tab.Button.One.OXP := 0.5 ; X Origin at 50% of Button Width, Center
guiTab.Tab.Button.One.OYP := 0.5 ; Y Origin at 50% of Button Height, Middle
guiTab.Tab.UseTab(2)
guiTab.Tab.Button.Two := guiTab.Add("Button", "Default x150 y150", "Two in View")
guiTab.Tab.Button.Two.Anchor := guiTab.Tab ; need to set Anchor Tab for Controls in Tabs
guiTab.Tab.Button.Two.X := -10 ; 10 from Right Margin of Anchor Tab
guiTab.Tab.Button.Two.Y := -10 ; 10 from Bottom Margin of Anchor Tab
guiTab.Tab.Button.Two.OXP := 1 ; X Origin to 100%, Right Edge
guiTab.Tab.Button.Two.OYP := 1 ; Y Origin to 100%, Bottom Edge (Origin is now Bottom Right corner)
guiTab.Tab.Button.Two.WP := 0.50 ; 50% width of Anchor Tab
guiTab.Tab.UseTab(3)
guiTab.Tab.Button.Three := guiTab.Add("Button", "Default x150 y150", "Three")
guiTab.Tab.Button.Three.Anchor := guiTab.Tab ; need to set Anchor Tab for Controls in Tabs
guiTab.Tab.Button.Three.XP := 0.7 ; 70% of Anchor Tab Width
guiTab.Tab.Button.Three.Y := -300 ; 300 from Bottom of Anchor Tab
guiTab.Tab.Button.Three.MinY := 20 ; 20 Min Y
guiList.Show("x10 y10 w500 h300")
guiTab.Show("x1000 y100")
Esc:: ExitApp
This example just uses lots of different properties and not necessarily in the best way. There has not been any extensive testing but it seems to work.
Gui commands reference all positions internally by the top left corner. The 'Anchor' option reference all positions to the top left of the anchor control. References that use width and height will be of the anchor control. Controls in a Tab have to be manually Anchored to the Tab control even if they are not repositioned during a resize. I have found no good way to automatically determine if a control is in a Tab.
You don't really need to define positions much at all in the creation of the Gui. The first time you show the Gui, the function will position everything that has been defined. You can just let the controls default to location and pile all up together and then let the function reposition them. This "piling up" if not spaced correctly to start with is why the function does a Redraw on everything the first time. Most of the controls I did not give any position or size information in the control creation. But as shown by the "yp" in the top row of buttons, I defined the Y position in the Gui and then just never set a offset for Y so the function never changes the Y of those controls even though it is changing the X positions.
If you drag the window aggressively it is possible to get controls drawn on top of each other momentarily and create artifacts. That is just the nature of things and not really specific to this function. You can use the method GuiReSizer.Options({GuiObj}) and manually force a one-time cleanup just like the first time a Gui is displayed. Maybe when the mouse button is released. If I have artifact issues in actual use, I will look into it more.
FG