[Class] GuiReSizer - Position and Resize Gui Controls

Post your working scripts, libraries and tools.
User avatar
FanaticGuru
Posts: 1907
Joined: 30 Sep 2013, 22:25

[Class] GuiReSizer - Position and Resize Gui Controls

14 Feb 2023, 19:08

[Class] GuiReSizer v2

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
Last edited by FanaticGuru on 13 Mar 2023, 16:02, edited 2 times in total.
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
flyingDman
Posts: 2828
Joined: 29 Sep 2013, 19:01

Re: [Function] GuiReSizer - Gui Controls Position and Resize

20 Feb 2023, 21:50

Thank you FG! I was also porting a large script to V2 and realized that resizing controls when the window is resized is not as straight forward as I had hoped. Your script came to the rescue...
14.3 & 1.3.7
nnrxin
Posts: 8
Joined: 07 May 2018, 06:02

Re: [Function] GuiReSizer - Gui Controls Position and Resize

21 Feb 2023, 10:15

Thank you FG, It is powerful and easy to use :thumbup: :thumbup: :thumbup:

But It can not work correctly when the GuiControl is in Tab3. :crazy:
Maybe the reason is that in Move() X,Y is relative to Tab client area but in GetPos() X,Y is relative to Gui client area.

Code: Select all

;{[Function] GuiReSizer
; Fanatic Guru
; Version 2023 02 15
;
; Update 2023 02 15:  Add more Min Max properties and renamed some properties
;
; #Requires AutoHotkey 2.0+
;
; Function to Handle the Resizing of Gui and
; Move and Resize Controls
;
;------------------------------------------------
;
; Method:
;   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)
;
;	Control Properties:
; 		X			X positional offset from margins or anchor object (percentage allowed)
;		Y			Y positional offset from margins or anchor object (percentage allowed)
;		OriginX		control origin X defaults to 0 or left side of control, this relocates the origin (percentage allowed)
;		OriginY		control origin Y defaults to 0 or top side of control, this relocates the origin (percentage allowed)
;		Width		width of control (percentage allowed)
;		Height		height of control (percentage allowed)
;		MinX		mininum X offset
;		MaxX		maximum X offset
;		MinY		minimum Y offset
;		MaxY		maximum Y offset
;		MinWidth	minimum control width
;		MaxWidth	maximum control width
;		MinHeight	minimum control height
;		MaxHeight	maximum control height
;		Anchor		normally controls are position in relation to the margins, this positions in relation to another control
;		Function	custom function that can be called for this control
;		Cleanup		redraw this control each time to cleanup artifacts, normally not requires and causes flickering
;
;		Where pecentage is allowed then a number greater than -1 but less than 1 can be entered.
;		.5 would be 50% to the right or below, -.25 would be 25% to the left or above.
;		An Origin of .5 would be 50% or the center of the control along that axis.
;
;	Gui Properties:
;		Init		{Gui}.Init := true, will cause all controls of the Gui to be redrawn on next function call
;
GuiReSizer(GuiObj, WindowMinMax, Width, Height)
{
	; On Initial display of Gui use redraw to cleanup first positioning
	try
		Init := GuiObj.Init
	catch
		Init := true

	if WindowMinMax = -1
        return

	For Hwnd, CtrlObj in GuiObj
	{
		if CtrlObj.HasProp("Anchor")
		{
			CtrlObj.Anchor.GetPos(&AnchorX, &AnchorY, &AnchorW, &AnchorH)
			Anchored := true
		}
		else
			Anchored := false

		CtrlObj.GetPos(&CtrlX, &CtrlY, &CtrlW, &CtrlH)

		if CtrlObj.HasProp("Width")
		{
			; Check for Propeties: Min Max
			CtrlObj.HasProp("MinWidth") ? MinW := CtrlObj.MinWidth : MinW := 0
			CtrlObj.HasProp("MaxWidth") ? MaxW := CtrlObj.MaxWidth : MaxW := 999999

			; Check for Percentage
			If CtrlObj.Width > -1 and CtrlObj.Width < 1
				CtrlW := Width * CtrlObj.Width
			else
				CtrlW := CtrlObj.Width
			if CtrlW < 0
				CtrlW := Width - CtrlX + CtrlW
			CtrlW := MinMax(CtrlW, MinW, MaxW)
		}

		if CtrlObj.HasProp("OriginX")
		{
			If CtrlObj.OriginX > -1 and CtrlObj.OriginX < 1
				OriginX := CtrlW * CtrlObj.OriginX
			else
				OriginX := CtrlObj.OriginX
		}
		else
			OriginX := 0

		if CtrlObj.HasProp("X")
		{
			; Check for Propeties: Min Max
			CtrlObj.HasProp("MinX") ? MinX := CtrlObj.MinX : MinX := -999999
			CtrlObj.HasProp("MaxX") ? MaxX := CtrlObj.MaxX : MaxX := 999999

			If CtrlObj.X > -1 and CtrlObj.X < 1
				X := Width * CtrlObj.X
			else
				X := CtrlObj.X

			; Adjust X for Control Width and Which Side
			if Anchored and X < 0
				CtrlX := AnchorX - CtrlW + X + OriginX
			else if Anchored and X => 0
				CtrlX := AnchorX + AnchorW + X - OriginX
			else if !Anchored and X < 0
				CtrlX := Width - CtrlW + X + OriginX
			else ; !Anchored and X => 0
				CtrlX := X - OriginX
			CtrlX := MinMax(CtrlX, MinX, MaxX)
		}

		if CtrlObj.HasProp("Height")
		{
			; Check for Propeties: Min Max
			CtrlObj.HasProp("MinHeight") ? MinH := CtrlObj.MinHeight : MinH := 0
			CtrlObj.HasProp("MaxHeight") ? MaxH := CtrlObj.MaxHeight : MaxH := 999999

			; Check for Percentage
			If CtrlObj.Height > -1 and CtrlObj.Height < 1
				CtrlH := Height * CtrlObj.Height
			else
				CtrlH := CtrlObj.Height
			if CtrlH < 0
				CtrlH := Height - CtrlY + CtrlH
			CtrlH := MinMax(CtrlH, MinH, MaxH)
		}

		if CtrlObj.HasProp("OriginY")
		{
			If CtrlObj.OriginY > -1 and CtrlObj.OriginY < 1
				OriginY := CtrlH * CtrlObj.OriginY
			else
				OriginY := CtrlObj.OriginY
		}
		else
			OriginY := 0

		if CtrlObj.HasProp("Y")
		{
			; Check for Propeties: Min Max
			CtrlObj.HasProp("MinY") ? MinY := CtrlObj.MinY : MinY := -999999
			CtrlObj.HasProp("MaxY") ? MaxY := CtrlObj.MaxY : MaxY := 999999

			; Check for Percentage
			If CtrlObj.Y > -1 and CtrlObj.Y < 1
				Y := Height * CtrlObj.Y
			else
				Y := CtrlObj.Y

			; Adjust Y for Control Height and Which Side
			if Anchored and Y < 0
				CtrlY := AnchorY - CtrlH + Y + OriginY
			else if Anchored and Y => 0
				CtrlY := AnchorY + AnchorH + Y - OriginY
			else if !Anchored and Y < 0
				CtrlY := Height - CtrlH + Y + OriginY
			else ; !Anchored and Y => 0
				CtrlY := Y - OriginY
			CtrlY := MinMax(CtrlY, MinY, MaxY)
		}

		; Move and Size
		CtrlObj.Move(CtrlX, CtrlY, CtrlW, CtrlH)
		if Init or (CtrlObj.HasProp("Cleanup") and CtrlObj.Cleanup = true) ; Cleanup on Init
			CtrlObj.Redraw()

		if CtrlObj.HasProp("Function")
			CtrlObj.Function(GuiObj) ; CtrlObj is hidden 'this' first parameter
	}

	GuiObj.Init := false
	MinMax(Num, MinNum, MaxNum) => Min(Max(Num, MinNum), MaxNum)
}





guiList := Gui(,"Test - List")
guiList.Opt("+Resize +MinSize250x150")
guiList.OnEvent("Size", GuiReSizer)
guiList.One := guiList.Add("Tab3", "x10 y10 w300 h200", ["One"])
guiList.Two := guiList.Add("Button", "x10 y10", "Two")   

guiList.Show()

guiList.OnEvent("Close", CLOSEGUI)
CLOSEGUI(*)
{
	ExitApp
}
User avatar
FanaticGuru
Posts: 1907
Joined: 30 Sep 2013, 22:25

Re: [Function] GuiReSizer - Gui Controls Position and Resize

21 Feb 2023, 16:54

nnrxin wrote:
21 Feb 2023, 10:15
Thank you FG, It is powerful and easy to use :thumbup: :thumbup: :thumbup:

But It can not work correctly when the GuiControl is in Tab3. :crazy:
Maybe the reason is that in Move() X,Y is relative to Tab client area but in GetPos() X,Y is relative to Gui client area.

Yea, there is a problem with Tab3. I did not design for tabs. I will see what I can do about that.

You can force it by putting in positioning based on the overall Gui.

Code: Select all

guiTab1 := Gui(,"Tab 1"), guiTab1.Opt("+Resize")
guiTab1.OnEvent("Size", GuiReSizer)
guiTab1.Tab3 := guiTab1.Add("Tab3","w500 h500",["General","View","Settings"])
guiTab1.Tab3.Width := -10
guiTab1.Tab3.Height := -10
guiTab1.Tab3.Button := {}
guiTab1.Tab3.UseTab(1)
guiTab1.Tab3.Button.One := guiTab1.Add("Button", "Default x50 y50", "One")
guiTab1.Tab3.Button.One.X := .25
guiTab1.Tab3.Button.One.Y := -50
guiTab1.Tab3.UseTab(2)
guiTab1.Tab3.Button.Two := guiTab1.Add("Button", "Default x50 y50", "Two")
guiTab1.Tab3.Button.Two.X := .5
guiTab1.Tab3.Button.Two.Y := -100
guiTab1.Tab3.UseTab(3)
guiTab1.Tab3.Button.Three := guiTab1.Add("Button", "Default x50 y50", "Three")
guiTab1.Tab3.Button.Three.X := .75
guiTab1.Tab3.Button.Three.Y := -150
guiTab1.Show("x50 y50")

It also makes me aware that I might need to add an Inside Anchor option. Currently Anchor attaches to the outside of another control (so much above, below, left, right) but I need an option to anchor to the inside of another control. This might also cleanup the current code that basically positions inside when anchored to the margins and outside when anchored to another control. Things anchored to a tab will often need to be so far inside the tab not a distance away from the tab.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
burque505
Posts: 1736
Joined: 22 Jan 2017, 19:37

Re: [Function] GuiReSizer - Gui Controls Position and Resize

21 Feb 2023, 17:20

Thanks, @FanaticGuru! This will really help a lot.
Regards,
burque505
william_ahk
Posts: 499
Joined: 03 Dec 2018, 20:02

Re: [Function] GuiReSizer - Gui Controls Position and Resize

22 Feb 2023, 05:56

Amazing! Will use this in my next project.
User avatar
Relayer
Posts: 160
Joined: 30 Sep 2013, 13:09
Location: Delaware, USA

Re: [Function] GuiReSizer - Gui Controls Position and Resize

22 Feb 2023, 13:40

Hi,

Many of my scripts used Anchor to resize guis. I took some time and converted Anchor to v2 for those who are interested.

Relayer

Code: Select all

/*
	Function: Anchor (translated to v2 by Relayer 2023-02-22)
		Defines how controls should be automatically positioned relative to the new dimensions of a window when resized.

	Parameters:
		i - 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 directly 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 <http://www.autohotkey.net/~Titan/#anchor>
		- New BSD License <http://www.autohotkey.net/~Titan/license.txt>
*/
;64 bit compatible
Anchor(i, a := "", r := false)
{
	static c, cs := 12, cx := 255, cl := 0, g, gs := 8, gl := 0, gpi := "", gw, gh, z := 0, ptr
	cf := gf := 0
	if z = 0
	{
		g := Buffer(gs * 99, 0), c := Buffer(cs * cx, 15)
		ptr := A_PtrSize ? "Ptr" : "UInt", z := true
	}
	gi := Buffer(68, 0)
	DllCall("GetWindowInfo", "UInt", gp := DllCall("GetParent", "UInt", i), ptr, gi) ; &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), "UInt") == gp
			{
				gw := NumGet(g, cb + 4, "Short"), gh := NumGet(g, cb + 6, "Short"), gf := 1
				break
			}
		if !gf
			NumPut("UInt", gp, g, gl), NumPut("Short", gw := giw, g, gl + 4), NumPut("Short", gh := gih, g, gl + 6), gl += gs
	}
	ControlGetPos &dx, &dy, &dw, &dh, i

	Loop cl
		if NumGet(c, cb := cs * (A_Index - 1), "UInt") == i
		{
			if (a = "")
			{
				cf := 1
				break
			}
			giw -= gw, gih -= gh, ass := 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")

			d := map( "x", dx
					, "y", dy
					, "w", dw
					, "h", dh )

			Loop Parse, a, "xywh"
				if A_Index > 1
				{
					av := SubStr(a, ass, 1), ass += 1 + StrLen(A_LoopField)
					d[av] += (InStr("yh", av) ? gih : giw) * (isNumber(A_LoopField) ? A_LoopField : 1)
				}
			dx := d["x"], dy := d["y"], dw := d["w"], dh := d["h"]
			DllCall("SetWindowPos", "UInt", i, "UInt", 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
	if cf = 1
		dw -= giw - gw, dh -= gih - gh
	NumPut("UInt", i, c, cb)
	NumPut("Short", dx, c, cb + 4)
	NumPut("Short", dy, c, cb + 6)
	NumPut("Short", dw, c, cb + 8)
	NumPut("Short", dh, c, cb + 10)
	return true
}
User avatar
kczx3
Posts: 1648
Joined: 06 Oct 2015, 21:39

Re: [Function] GuiReSizer - Gui Controls Position and Resize

22 Feb 2023, 14:41

@Relayer I'd suggest you post that as a separate topic to increase its searchability. It would be much better of in its own topic for feedback and questions.
User avatar
Brujah4
Posts: 19
Joined: 11 Feb 2023, 03:27

Re: [Function] GuiReSizer - Gui Controls Position and Resize

03 Mar 2023, 18:37

@FanaticGuru Wow, great function. I like it very much. Many thanks to you. :D

I just added a little addition to your function:
Use AnchorType for specifying whether Anchor applies to X and/or Y. If AnchorType is omitted or set to "X Y" the function assumes X and Y like usual.
My changes are in the following lines/blocks:
42
66-79
121-124
167-170

[Function] GuiReSizer

Code: Select all

;{[Function] GuiReSizer
; Fanatic Guru
; Version 2023 02 15
;
; Update 2023 02 15:  Add more Min Max properties and renamed some properties
;
; #Requires AutoHotkey 2.0+
;
; Function to Handle the Resizing of Gui and
; Move and Resize Controls
;
;------------------------------------------------
;
; Method:
;   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)
;
;	Control Properties:
; 		X			X positional offset from margins or anchor object (percentage allowed)
;		Y			Y positional offset from margins or anchor object (percentage allowed)
;		OriginX		control origin X defaults to 0 or left side of control, this relocates the origin (percentage allowed)
;		OriginY		control origin Y defaults to 0 or top side of control, this relocates the origin (percentage allowed)
;		Width		width of control (percentage allowed)
;		Height		height of control (percentage allowed)
;		MinX		mininum X offset
;		MaxX		maximum X offset
;		MinY		minimum Y offset
;		MaxY		maximum Y offset
;		MinWidth	minimum control width
;		MaxWidth	maximum control width
;		MinHeight	minimum control height
;		MaxHeight	maximum control height
;		Anchor		normally controls are position in relation to the margins, this positions in relation to another control
;		AnchorType	specifies whether anchor is used for X or Y (standard: "X Y")
;		Function	custom function that can be called for this control
;		Cleanup		redraw this control each time to cleanup artifacts, normally not requires and causes flickering
;
;		Where pecentage is allowed then a number greater than -1 but less than 1 can be entered.
;		.5 would be 50% to the right or below, -.25 would be 25% to the left or above.
;		An Origin of .5 would be 50% or the center of the control along that axis.
;
;	Gui Properties:
;		Init		{Gui}.Init := true, will cause all controls of the Gui to be redrawn on next function call
;
GuiReSizer(GuiObj, WindowMinMax, Width, Height)
{
	; On Initial display of Gui use redraw to cleanup first positioning
	try
		local Init := GuiObj.Init
	catch
		local Init := true

	if WindowMinMax = -1
        return

	For Hwnd, CtrlObj in GuiObj
	{
		if CtrlObj.HasProp("Anchor")
		{
			CtrlObj.Anchor.GetPos(&AnchorX, &AnchorY, &AnchorW, &AnchorH)

			if CtrlObj.HasProp("AnchorType")
			{
				AnchoredX := InStr(CtrlObj.AnchorType, "X"),
				AnchoredY := InStr(CtrlObj.AnchorType, "Y")
			}
			else
				AnchoredX := AnchoredY := true
		}
		else
			AnchoredX := AnchoredY := false

		CtrlObj.GetPos(&CtrlX, &CtrlY, &CtrlW, &CtrlH)

		if CtrlObj.HasProp("Width")
		{
			; Check for Propeties: Min Max
			CtrlObj.HasProp("MinWidth") ? MinW := CtrlObj.MinWidth : MinW := 0
			CtrlObj.HasProp("MaxWidth") ? MaxW := CtrlObj.MaxWidth : MaxW := 999999

			; Check for Percentage
			If CtrlObj.Width > -1 and CtrlObj.Width < 1
				CtrlW := Width * CtrlObj.Width
			else
				CtrlW := CtrlObj.Width
			if CtrlW < 0
				CtrlW := Width - CtrlX + CtrlW
			CtrlW := MinMax(CtrlW, MinW, MaxW)
		}

		if CtrlObj.HasProp("OriginX")
		{
			If CtrlObj.OriginX > -1 and CtrlObj.OriginX < 1
				OriginX := CtrlW * CtrlObj.OriginX
			else
				OriginX := CtrlObj.OriginX
		}
		else
			OriginX := 0

		if CtrlObj.HasProp("X")
		{
			; Check for Propeties: Min Max
			CtrlObj.HasProp("MinX") ? MinX := CtrlObj.MinX : MinX := -999999
			CtrlObj.HasProp("MaxX") ? MaxX := CtrlObj.MaxX : MaxX := 999999

			If CtrlObj.X > -1 and CtrlObj.X < 1
				X := Width * CtrlObj.X
			else
				X := CtrlObj.X

			; Adjust X for Control Width and Which Side
			if AnchoredX
				CtrlX := (X < 0) ? AnchorX - CtrlW + X + OriginX : AnchorX + AnchorW + X - OriginX
			else
				CtrlX := (X < 0) ? Width - CtrlW + X + OriginX : X - OriginX
			CtrlX := MinMax(CtrlX, MinX, MaxX)
		}

		if CtrlObj.HasProp("Height")
		{
			; Check for Propeties: Min Max
			CtrlObj.HasProp("MinHeight") ? MinH := CtrlObj.MinHeight : MinH := 0
			CtrlObj.HasProp("MaxHeight") ? MaxH := CtrlObj.MaxHeight : MaxH := 999999

			; Check for Percentage
			If CtrlObj.Height > -1 and CtrlObj.Height < 1
				CtrlH := Height * CtrlObj.Height
			else
				CtrlH := CtrlObj.Height
			if CtrlH < 0
				CtrlH := Height - CtrlY + CtrlH
			CtrlH := MinMax(CtrlH, MinH, MaxH)
		}

		if CtrlObj.HasProp("OriginY")
		{
			If CtrlObj.OriginY > -1 and CtrlObj.OriginY < 1
				OriginY := CtrlH * CtrlObj.OriginY
			else
				OriginY := CtrlObj.OriginY
		}
		else
			OriginY := 0

		if CtrlObj.HasProp("Y")
		{
			; Check for Propeties: Min Max
			CtrlObj.HasProp("MinY") ? MinY := CtrlObj.MinY : MinY := -999999
			CtrlObj.HasProp("MaxY") ? MaxY := CtrlObj.MaxY : MaxY := 999999

			; Check for Percentage
			If CtrlObj.Y > -1 and CtrlObj.Y < 1
				Y := Height * CtrlObj.Y
			else
				Y := CtrlObj.Y

			; Adjust Y for Control Height and Which Side
			if AnchoredY
				CtrlY := (Y < 0) ? AnchorY - CtrlH + Y + OriginY : AnchorY + AnchorH + Y - OriginY
			else
				CtrlY := (Y < 0) ? Height - CtrlH + Y + OriginY : Y - OriginY
			CtrlY := MinMax(CtrlY, MinY, MaxY)
		}

		; Move and Size
		CtrlObj.Move(CtrlX, CtrlY, CtrlW, CtrlH)
		if Init or (CtrlObj.HasProp("Cleanup") and CtrlObj.Cleanup = true) ; Cleanup on Init
			CtrlObj.Redraw()

		if CtrlObj.HasProp("Function")
			CtrlObj.Function(GuiObj) ; CtrlObj is hidden 'this' first parameter
	}

	GuiObj.Init := false
	MinMax(Num, MinNum, MaxNum) => Min(Max(Num, MinNum), MaxNum)
}

Cheers
User avatar
FanaticGuru
Posts: 1907
Joined: 30 Sep 2013, 22:25

Re: [Function] GuiReSizer - Gui Controls Position and Resize

03 Mar 2023, 19:49

Brujah4 wrote:
03 Mar 2023, 18:37
I just added a little addition to your function:
Use AnchorType for specifying whether Anchor applies to X and/or Y. If AnchorType is omitted or set to "X Y" the function assumes X and Y like usual.

I will definitely check this out. I actually thought of maybe needing something like that.

I have a couple of different versions since I posted this with some more streamlined code and some additional features.

The problem is not so much how to make it do stuff but more how to efficiently collect the information from the user of what's wanted.

I been thinking of getting rid of Anchor all together. It adds a lot of complications from the user having to set options to then the code having to implement those options. It is cool but not sure it is worth it. Which side do you want to anchor to, do you want it to be anchored inside or outside the other control. Like with a GroupBox, you probably would want another control anchored to the inside of that control. Plus you have to move all the unanchored controls first then move all the anchored controls so basically the function has to loop through twice on each resize.

I mean when you anchor to another control. The user could just use some math to use a set X plus a percentage X to replicate the second control moving in sync with the first control as if it was anchored. Right now you can only use a set X or a percentage X. I need to modify it where a control can be given both a X and X percentage. For example the control's X is at 150 pixels plus 10% of the overall Gui width. I think everything would need a set and percentage amount.

With Anchor, Control one is at 50% X with a 100 width, you want control two 10 off the right side, now anchor two to one with an X of 10. The script figures out which side and adjust for control widths. But without Anchor, math could be used to put control two at X 50% plus X 110 (position plus width plus offset).

Right now, it actually gets the X and width of control one each resize and then positions control two from there. Maybe the anchor option could just pre-do the math for the user but then the function actual uses X and X percentage without ever actually checking the position of control one every resize. I have also thought of having some methods in addition to properties.

I am kind of thinking out loud as I type (which has actual helped me have some new ideas), but I am going to give it some more thought and see what I can come up with.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
User avatar
Brujah4
Posts: 19
Joined: 11 Feb 2023, 03:27

Re: [Function] GuiReSizer - Gui Controls Position and Resize

04 Mar 2023, 08:33

FanaticGuru wrote:
03 Mar 2023, 19:49
I am kind of thinking out loud as I type (which has actual helped me have some new ideas), but I am going to give it some more thought and see what I can come up with.
Glad that I could help you with my addition.

Code: Select all

CtrlObj.Function(GuiObj) ; CtrlObj is hidden 'this' first parameter
By the way, what's up with this hidden parameter? Can you give me a link to the section in the documentation where i can read about this?

Cheers
User avatar
FanaticGuru
Posts: 1907
Joined: 30 Sep 2013, 22:25

Re: [Function] GuiReSizer - Gui Controls Position and Resize

04 Mar 2023, 20:15

Brujah4 wrote:
04 Mar 2023, 08:33

Code: Select all

CtrlObj.Function(GuiObj) ; CtrlObj is hidden 'this' first parameter
By the way, what's up with this hidden parameter? Can you give me a link to the section in the documentation where i can read about this?

CtrlObj.Function(GuiObj) ; CtrlObj is hidden 'this' first parameter one parameter in call but
ListView_Columns(CtrlObj, GuiObj) two parameters are received

A hidden 'this' gets inserted as the first parameter when a function is called that is stored in an object.

Code: Select all

Obj := {}
Obj.Stuff := "Stuff"
Obj.Funky := Func2 ; assign function to an object

Func1(Obj) ; call function and pass object

Func1(A) {
    MsgBox "Func1 " A.Stuff
    A.Funky(5)     ; call the function stored in Obj.Funky ie. call Func2
   ; when a function is called that is stored in an object, the object doing the storing is inserted as the first parameter, ie. 'this'
 }

Func2(B, C) { ; 'this' is Obj which gets assigned to B, C is the next parameter passed
    Msgbox "Func2 " B.Stuff
    MsgBox  C
}

Not sure where it is in the documents but I am pretty sure it is in there somewhere.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
nnrxin
Posts: 8
Joined: 07 May 2018, 06:02

Re: [Function] GuiReSizer - Gui Controls Position and Resize

04 Mar 2023, 22:41

Brujah4 wrote:
04 Mar 2023, 08:33
FanaticGuru wrote:
03 Mar 2023, 19:49
I am kind of thinking out loud as I type (which has actual helped me have some new ideas), but I am going to give it some more thought and see what I can come up with.
Glad that I could help you with my addition.

Code: Select all

CtrlObj.Function(GuiObj) ; CtrlObj is hidden 'this' first parameter
By the way, what's up with this hidden parameter? Can you give me a link to the section in the documentation where i can read about this?

Cheers
https://www.autohotkey.com/docs/v2/Objects.htm#Extended_Usage
Attachments
2023-03-05.png
2023-03-05.png (52.6 KiB) Viewed 4654 times
User avatar
Brujah4
Posts: 19
Joined: 11 Feb 2023, 03:27

Re: [Function] GuiReSizer - Gui Controls Position and Resize

08 Mar 2023, 12:02

@FanaticGuru It might be a good idea to leave your function as it is and just add a "parent" property.
If this is empty, the window is assumed, otherwise you can specify a Tab, for example. Then the position is aligned within this tab.

I think the anchors can remain as before, but as you already wrote, they should only be evaluated in the second pass to avoid position errors.

Cheers
User avatar
FanaticGuru
Posts: 1907
Joined: 30 Sep 2013, 22:25

Re: [Class] GuiReSizer - Position and Resize Gui Controls

13 Mar 2023, 15:49

Updated in First Post

Change Log: Update 2023 03 13
  • major rewrite of script
Streamlined and expanded a lot of the code. The biggest difference from a use point of view is that Anchor no longer tries to adjust for putting to the left, right, top, or bottom edge of anchor control. And also, whether to anchor inside or outside. Meaning for example like with a GroupBox as an anchor a position of X := -10, did you want to go 10 to the left of the left edge outside the box or 10 to the left of the right edge of the box putting it inside the box. Now the user has to specify with little assumptions. Generally, it requires a little more math from the user. Might look at adding some methods that do some smart positioning and calculating for the user.

FG
Hotkey Help - Help Dialog for Currently Running AHK Scripts
AHK Startup - Consolidate Multiply AHK Scripts with one Tray Icon
Hotstring Manager - Create and Manage Hotstrings
[Class] WinHook - Create Window Shell Hooks and Window Event Hooks
GamesOfFreak
Posts: 28
Joined: 15 Sep 2020, 03:51
Location: Germany
Contact:

Re: [Class] GuiReSizer - Position and Resize Gui Controls

18 May 2023, 16:06

Am I stupid or why I get this Error:
This value of type "String" has no property named "Anchor".

026: mcCommandGeneratorAboutGUI.Text.Three.Anchor := mcCommandGeneratorAboutGUI.Text.Two

Here is the Code:

Code: Select all

#Requires AutoHotkey v2.0
#include extensions/GuiReSizer.ahk

FullVersion := "0.1.230518"
DonationLink := "https://www.paypal.com/donate/?hosted_button_id=SQBG32AWNWTY8"

; Create the main GUI Menu Bar
mcCommandGeneratorHelpMenu := Menu()
mcCommandGeneratorHelpMenu.Add("About", mcCommandGeneratorAbout)
mcCommandGeneratorMenus := MenuBar()
mcCommandGeneratorMenus.Add("Help", mcCommandGeneratorHelpMenu)

mcCommandGeneratorAbout(*) {
    mcCommandGeneratorAboutGUI.Show("w317.5 h177.5 AutoSize")
    return
}

; Create the About GUI
mcCommandGeneratorAboutGUI := Gui("+Owner", "About Minecraft Command Kit")
mcCommandGeneratorAboutGUI.BackColor := 0xF5F5F5
mcCommandGeneratorAboutGUI.OnEvent("Size", GuiReSizer)
mcCommandGeneratorAboutGUI.Text := {}
mcCommandGeneratorAboutGUI.Text.One := mcCommandGeneratorAboutGUI.Add("Text", "x15 y15 wp218 hp25", "Minecraft Command Kit 0.1").SetFont("s12 w600")
mcCommandGeneratorAboutGUI.Text.Two := mcCommandGeneratorAboutGUI.Add("Text", "x15 y45", "Full Version:")
mcCommandGeneratorAboutGUI.Text.Three := mcCommandGeneratorAboutGUI.Add("Text", "x75 y45 w70", FullVersion).SetFont("w600")
mcCommandGeneratorAboutGUI.Text.Three.Anchor := mcCommandGeneratorAboutGUI.Text.Two
mcCommandGeneratorAboutGUI.Text.Three.AnchorIn := false
mcCommandGeneratorAboutGUI.Add("Text", "x15 y65 w170", "Developed by SparkleArts.").SetFont("w600")
mcCommandGeneratorAboutGUI.Add("Button", "x15 y95", "License").OnEvent("Click", mcCommandGeneratorLicense)

mcCommandGeneratorLicense(*) {
    return
}

; Create the main GUI
mcCommandGenerator := Gui(, "Minecraft Command Kit")
mcCommandGenerator.BackColor := 0xF5F5F5
mcCommandGenerator.OnEvent("Size", GuiReSizer)
mcCommandGenerator.Button := {}
mcCommandGenerator.Button.One := mcCommandGenerator.Add("Button", "xp0.50", "Give Command Generator").OnEvent("Click", OpenGiveGenerator)
mcCommandGenerator.MenuBar := mcCommandGeneratorMenus
mcCommandGenerator.Show("w220 h100 AutoSize")
OpenGiveGenerator(*) {
    SoundPlay(A_ScriptDir "\sounds\click.mp3")
    mcGiveGenerator.Show("w810 h322")
    return
}

mcCommandGenerator.OnEvent("Close", MainGUIClose)
MainGUIClose(*) {
    ExitApp
    return
}
; Create the Minecraft Give Command Generator GUI
mcGiveGenerator := Gui("+Owner", "Minecraft Command Kit: Give Command")
mcGiveGenerator.BackColor := 0xF5F5F5
mcGiveItemSearch := mcGiveGenerator.Add("Edit", "x35 y15 w370 h20 +Lowercase -Multi vItemSearch")
mcGiveGenerator.Add("Picture", "w20 h20 x15 y15", A_ScriptDir "\icons\search.png")
mcGiveGenerator.Add("Text", "x475 y18", "Minecraft Version:")
mcGiveMCVersion := mcGiveGenerator.Add("DropDownList", "x623 y15 w172 vMCVersion", ["Java 1.12", "Java 1.13"])
mcGiveEntityTarget := mcGiveGenerator.Add("Edit", "x35 y60 w370 h20 +Lowercase -Multi vEntityTarget")
mcGiveGenerator.Add("Picture", "w20 h20 x15 y60", A_ScriptDir "\icons\player.png")
mcGiveGenerator.Add("Text", "x475 y63", "Give Amount:")
mcGiveGenerator.Add("Edit", " x623 y60 w172 +Lowercase +Number -Multi")
mcGiveItemAmount := mcGiveGenerator.Add("UpDown", "vGiveItemAmount Range1-64", "1")
mcGiveCommand := mcGiveGenerator.Add("Edit", "")
ntepa
Posts: 434
Joined: 19 Oct 2022, 20:52

Re: [Class] GuiReSizer - Position and Resize Gui Controls

18 May 2023, 22:20

@GamesOfFreak
mcCommandGeneratorAboutGUI.Text.Three is being assigned to the return value of SetFont instead of a text control. SetFont returns an empty string, and you tried to assign the property Anchor to it.
Assign the text control in parentheses before calling SetFont:

Code: Select all

(mcCommandGeneratorAboutGUI.Text.Three := mcCommandGeneratorAboutGUI.Add("Text", "x75 y45 w70", FullVersion)).SetFont("w600")
Or do it in 2 lines:

Code: Select all

 mcCommandGeneratorAboutGUI.Text.Three := mcCommandGeneratorAboutGUI.Add("Text", "x75 y45 w70", FullVersion)
 mcCommandGeneratorAboutGUI.Text.Three.SetFont("w600")
sashaatx
Posts: 342
Joined: 27 May 2021, 08:27
Contact:

Re: [Class] GuiReSizer - Position and Resize Gui Controls

18 Nov 2023, 14:25

couldnt consolidate comments
Last edited by sashaatx on 21 Nov 2023, 21:54, edited 1 time in total.
https://github.com/samfisherirl
? /Easy-Auto-GUI-for-AHK-v2 ? /Useful-AHK-v2-Libraries-and-Classes : /Pulovers-Macro-Creator-for-AHKv2 :
sashaatx
Posts: 342
Joined: 27 May 2021, 08:27
Contact:

Re: [Class] GuiReSizer - Position and Resize Gui Controls

19 Nov 2023, 05:50

question:

What's the best method for a parent GUI relationship.

Code: Select all

MainGui := Gui()
MainGui.Show("w500 h500")
childGUI := Gui("+Parent" MainGui.hwnd " -Caption")
MainGui.Opt("+Resize +ToolWindow +MinSize250x150")
MainGui.OnEvent("Size", GuiReSizer)
childGUI.OnEvent("Size", GuiReSizer)
ogcButtonOK := childGUI.Add("Button", "Default", "&OK")

ogcButtonOK.WidthP := 0.48
ogcButtonOK.X := 10 
childGUI.Show("x0 y0")

I couldnt figure out how to handle that relationship @FanaticGuru

Threw in a few params and opts and couldnt get the button size to stick
https://github.com/samfisherirl
? /Easy-Auto-GUI-for-AHK-v2 ? /Useful-AHK-v2-Libraries-and-Classes : /Pulovers-Macro-Creator-for-AHKv2 :
sashaatx
Posts: 342
Joined: 27 May 2021, 08:27
Contact:

Re: [Class] GuiReSizer - Position and Resize Gui Controls

21 Nov 2023, 21:47

I couldnt consolidate my comments. @FanaticGuru not sure if it matters, but I took your excellent documentation, and formatted in a way that is compatible with programs like vscode sublime text, npp. that way, on mouse over you get the info. if you use, just make sure theres no space above the class, after the commented-out docs.

Code: Select all

/**
 * @class GuiReSizer
 * @author Fanatic Guru
 * @version 2023-03-13
 * @description Class to Handle the Resizing of Gui and Move and Resize Controls.
 *   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+
 *
 * @param {Object} GuiObj - Gui Object.
 * @param {Number} WindowMinMax - Window status. 0 = neither minimized nor maximized, 1 = maximized, -1 = minimized.
 * @param {Number} Width - Width of GuiObj.
 * @param {Number} Height - Height of GuiObj.
 * @returns {Class} GuiReSizer instance.
 *
 * @example
 * 
 * ; GuiObj.OnEvent("Size", GuiReSizer)
 * ; 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 
 * 
 * @property {Number} X - X positional offset from margins.
 * @property {Number} Y - Y positional offset from margins.
 * @property {Number} XP - X positional offset from margins as a percentage of Gui width.
 * @property {Number} YP - Y positional offset from margins as a percentage of Gui height.
 * @property {Number} OriginX (OX) - Control origin X, defaults to 0 (left side of control), this relocates the origin.
 * @property {Number} OriginXP (OXP) - Control origin X as a percentage of Gui width, defaults to 0 (left side of control), this relocates the origin.
 * @property {Number} OriginY (OY) - Control origin Y, defaults to 0 (top side of control), this relocates the origin.
 * @property {Number} OriginYP (OYP) - Control origin Y as a percentage of Gui height, defaults to 0 (top side of control), this relocates the origin.
 * @property {Number} Width (W) - Width of control.
 * @property {Number} WidthP (WP) - Width of control as a percentage of Gui width.
 * @property {Number} Height (H) - Height of control.
 * @property {Number} HeightP (HP) - Height of control as a percentage of Gui height.
 * @property {Number} MinX - Minimum X offset.
 * @property {Number} MaxX - Maximum X offset.
 * @property {Number} MinY - Minimum Y offset.
 * @property {Number} MaxY - Maximum Y offset.
 * @property {Number} MinWidth (MinW) - Minimum control width.
 * @property {Number} MaxWidth (MaxW) - Maximum control width.
 * @property {Number} MinHeight (MinH) - Minimum control height.
 * @property {Number} MaxHeight (MaxH) - Maximum control height.
 * @property {Boolean} Cleanup (C) - When set to true, will redraw this control each time to clean up artifacts (normally not required and causes flickering).
 * @property {Function} Function (F) - Custom function that will be called for this control.
 * @property {Object} Anchor (A) - Control object anchor so that size and position commands are in relation to another control.
 * @property {Boolean} AnchorIn (AI) - Controls where the control is restricted to the inside of another control.
 *
 * @method Now
 * @description Forces a manual Call now for {GuiObj}.
 * @param {Object} GuiObj - Gui Object.
 *
 * @method Opt
 * @description Same as Options method.
 * @param {Object} switches - Switches for Options method.
 *
 * @method Options
 * @description All options are set as a string with each switch separated by a space "x10 yp50 oCM".
 * @param {Object} switches - Switches for setting options.
 * @param {Number} x - X.
 * @param {Number} y - Y.
 * @param {Number} xp - XP.
 * @param {Number} yp - YP.
 * @param {Number} wp - WidthP.
 * @param {Number} hp - HeightP.
 * @param {Number} w - Width.
 * @param {Number} h - Height.
 * @param {Number} minx - MinX.
 * @param {Number} maxx - MaxX.
 * @param {Number} miny - MinY.
 * @param {Number} maxy - MaxY.
 * @param {Number} minw - MinWidth.
 * @param {Number} maxw - MaxWidth.
 * @param {Number} minh - MinHeight.
 * @param {Number} maxh - MaxHeight.
 * @param {Number} oxp - OriginXP.
 * @param {Number} oyp - OriginYP.
 * @param {Number} ox - OriginX.
 * @param {Number} oy - OriginY.
 * @param {String} o - Origin: "L" left, "C" center, "R" right, "T" top, "M" middle, "B" bottom; may use 1 or 2 letters.
 *
 * @property {Object} Gui Properties:
 * @property {Number} Init - {Gui}.Init := 1, will cause all controls of the Gui to be redrawn on the next function call.
 *                           {Gui}.Init := 2, will also reinitialize abbreviations.
 */

with formatting:
image.png
image.png (90.48 KiB) Viewed 2167 times

(mouseover now)
image.png
image.png (152.85 KiB) Viewed 2167 times
https://github.com/samfisherirl
? /Easy-Auto-GUI-for-AHK-v2 ? /Useful-AHK-v2-Libraries-and-Classes : /Pulovers-Macro-Creator-for-AHKv2 :

Return to “Scripts and Functions (v2)”

Who is online

Users browsing this forum: Descolada, kunkel321, xroot and 33 guests