[Library] Class_Gui (object-oriented GUI)

Post your working scripts, libraries and tools for AHK v1.1 and older
wbm1113
Posts: 18
Joined: 28 Aug 2018, 11:19

[Library] Class_Gui (object-oriented GUI)

10 Feb 2020, 19:00

This library allows for an object-oriented approach to GUI design in v1. It has a similar feel to building GUIs in v2.

https://github.com/wbm1113/Class_Gui.ahk

Note: I recommend picking up Class_CtlColors (https://www.autohotkey.com/boards/viewtopic.php?t=2197&p=54087) to take advantage of the full functionality.

The main idea behind this library is to automatically create control handles when controls are created, and provide an easy interface to access controls directly by their handles. This contrasts with the more traditional reliance on ClassNN values and WinTitles, which can change both while your script runs and as your project grows, and eventually become very difficult to manage. Here is a very simple example:

Code: Select all

TestGui := new GuiObj("Test GUI")

TestGui.Add("Text", "Sample Text", "s12", "MyTextCtrl1")

SampleText := TestGui.Controls["MyTextCtrl1"].GetText()

MsgBox % SampleText ; "Sample Text"
Many GUI commands and control types are supported. More can easily be added in the GuiCtrlObj's Install() method. There is also ListView support, with numerous methods that mirror/enhance the built-in ListView functions.

Gui functionality that is not directly supported can still be added. For example:

Code: Select all

Gui, % TestGui.Name ":Default"
Gui, % TestGui.Name ":Add", Tab3, % "Hwnd" TestGui.Name "TabCtrlHwnd", Tab1|Tab2|Tab3
Here's the class:

Code: Select all

class GuiObj
{
    __new(Name) {
        This.Name := Name
        This.Title := "MyGui - " Name
        This.Controls := Object()
        This.DataControls := Object()
        Gui, % This.Name ":+LastFound"
        This.Hwnd := WinExist()
        This.SetMargins(), This.SetFont()
    }

    Add(CtrlType, CtrlText:="", Options:="", Label:="", DataControl:=1) {
        if(Label="")
            Label := RegExReplace(CtrlText, "[^A-z0-9_]")
        else Label := RegExReplace(Label, "[^A-z0-9_]")

        Gui, % This.Name ":Default"

        if(CtrlType="ListView")
            This.Controls[Label] := new LV_GuiCtrlObj(This, CtrlType, CtrlText, Options, Label)
        else This.Controls[Label] := new GuiCtrlObj(This, CtrlType, CtrlText, Options, Label)
        
        if(DataControl=1)
            This.DataControls[Label] := This.Controls[Label]
    }

    AddTextField(CtrlType, LabelText, FieldText:="", Width:="", TextOptions:="", FieldOptions:="", DataControl:=1) {
        This.Add("Text", LabelText, "+Section w" Width " " TextOptions,, DataControl)
        This.Add(CtrlType, FieldText, "w" Width " " FieldOptions, LabelText, DataControl)
    }

    ClearContents() { 
        for Name, CtrlObj in This.DataControls
            CtrlObj.SetText()
    }

    CheckForContents() { 
        for Name, CtrlObj in This.DataControls
            if(CtrlObj.GetText()!="")
                return 1
        return 0
    }

    Activate() { 
        WinActivate % "ahk_id " This.Hwnd
    }

    Show(Options:="") {
        if(This.GuiX!="" and This.GuiY!="")
            Gui, % This.Name ":Show", % Options " x" This.GuiX " y" This.GuiY, % This.Title
        else
            Gui, % This.Name ":Show", % Options, % This.Title
    }

    Hide() {
        Gui, % This.Name ":Hide"
    }

    Minimize() { 
        WinMinimize % "ahk_id " This.Hwnd
    }

    SetTitle(NewTitle:="") {
        This.Title := "ROW - " This.Name
        This.Title := This.Title NewTitle
        Gui, % This.Name ":Show",, % This.Title
        return
    }

    SetMargins(X:=4, Y:=4) {
        Gui, % This.Name ":Margin", %X%, %Y%
    }

    SetFont(Size:=10, Font:="", Options:="") {
        Gui, % This.Name ":Font", s%Size% %Options%, %Font%
    }

    SetOptions(Options) { 
        Gui, % This.Name ":" Options
    }

    SetCoords(X, Y) { 
        This.GuiX := X
        This.GuiY := Y
    }

    SetPos(X, Y) {
        DetectHiddenWindows, On
        WinMove, % "ahk_id " This.Hwnd,, % x, % y
        DetectHiddenWindows, Off
    }
}

class GuiCtrlObj
{
    __new(Parent, CtrlType, CtrlText, Options, Label) {
        This.Parent := Parent
        This.Type := CtrlType
        This.Text := CtrlText
        This.Label := Label
        This.Options := Options " Hwnd" This.Label "Hwnd"
        This.Install()
    }

    Install() {
        if This.Type="Text"
            Gui, Add, Text, % "0x200 " This.Options, % This.Text
        else if This.Type="Picture"
            Gui, Add, Picture, % This.Options, % This.Text
        else if This.Type="Edit"
            Gui, Add, Edit, % This.Options
        else if This.Type="ComboBox"
            Gui, Add, ComboBox, % This.Options, % This.Text
        else if This.Type="Button"
            Gui, Add, Button, % This.Options, % This.Text
        else if This.Type="ListView"
            Gui, Add, ListView, % This.Options " -LV0x10 -Multi +AltSubmit", % This.Text
        else if This.Type="ListBox"
            Gui, Add, ListBox, % This.Options, % This.Text

        HwndRef := This.Label "Hwnd"
        This.Hwnd := %HwndRef%
    }

    BindMethod(Binding) {
        GuiControl, +G, % This.Hwnd, % Binding
    }

    GetText() {
        ControlGetText, T,, % "ahk_id " This.Hwnd
        return T
    }

    SetText(T:="") {
        ControlSetText,, % T, % "ahk_id " This.Hwnd
    }

    SetSelect(Set, Select:="") {
        if IsObject(Set)
            Set := Set.Call()
        GuiControl,, % This.Hwnd, % "|" Set
        if(Select!="")
            GuiControl, Choose, % This.Hwnd, % Select  
    }

    ShowDropDown() { 
        Control, ShowDropDown,,, % "ahk_id " This.Hwnd
    }

    HideDropDown() { 
        Control, HideDropDown,,, % "ahk_id " This.Hwnd
    }

    Focus() {
        ControlFocus,, % "ahk_id " This.Hwnd
    }

    IsFocused() { 
        ControlGetFocus, RetVal, % "ahk_id " This.Parent.Hwnd
        Gui, % This.Parent.Name ":Default"
        GuiControlGet, RetVal2, Hwnd, % RetVal
        if(RetVal2=This.Hwnd)
            return 1
        return 0
    }

    SetOptions(Option) {
        GuiControl, %Option%, % This.Hwnd
    }

    Restyle(BGColor:="", TextColor:="", FontOptions:="", Text:="") {
        if(BGColor="") { 
            This.SetColor()
            This.Parent.SetFont(,, "Normal")
            GuiControl, Font, % This.Hwnd
            return
        }

        This.SetColor(BGColor, TextColor)
        This.SetText(Text)

        This.Parent.SetFont(,, FontOptions)
        GuiControl, Font, % This.Hwnd
        This.Parent.SetFont(,, "Normal")
    }

    SetColor(Background:="", Foreground:="") {
        if(Background="" and Foreground="")
            CtlColors.Detach(This.Hwnd)
        else CtlColors.Change(This.Hwnd, Background, Foreground)
    }
}

class LV_GuiCtrlObj extends GuiCtrlObj
{
    __call(Method, Params*) { 
        Gui, % This.Parent.Name ":Default"
    }
    
    LV_Add(Options, Cols*) {
        LV_Add(Options, Cols*)
    }

    LV_ModifyCol(Col, Options) { 
        LV_ModifyCol(Col, Options)
    }

    LV_GetText(Row, Col) { 
        LV_GetText(RetVal, Row, Col)
        return RetVal
    }

    LV_Modify(Row, OptionCols*) { ; have to do it like this, otherwise can't set only options because it expects 3 params (see SetIcons() in payments)
        LV_Modify(Row, OptionCols*)
    }

    LV_Select(Row) {
        if(Row=0) { 
            LV_Modify(1, "Select"), LV_Modify(1, "Focus"), LV_Modify(1, "-Select"), LV_Modify(1, "-Focus")
            return
        } else LV_Modify(Row, "Select"), LV_Modify(Row, "Focus")  
    }

    LV_GetCount(Options:="") { 
        return LV_GetCount(Options)
    }

    LV_LookupRow(LookupValue, Col) {
        Loop % This.LV_GetCount() { 
            FoundText := This.LV_GetText(A_Index, Col)
            if(LookupValue=FoundText)
                return A_Index
        }
        return 0
    }

    LV_Delete(Row:="") { 
        if(Row=0 or Row="")
            LV_Delete()
        else LV_Delete(Row)
    }

    LV_ImageSetup(Size) { 
        This.LVImageList := IL_Create(Size)
        LV_SetImageList(This.LVImageList)
    }

    LV_ImageAdd(File, Index:="") { 
        IL_Add(This.LVImageList, File, Index)
    }
}
And the more thorough script going through some sample uses. I recommend reading through it as you run it.

Code: Select all

#SingleInstance, Force



; create new instance, adjust font
; SetMargins(), SetCoords(), SetPos(), SetOptions() methods are also available
TestGui := new GuiObj("MyGui")
TestGui.SetFont(12, "Arial", "cBlue")



; Add()
;
; str param1 = control type
;
; str param2 = text of the control
;
; str param3 = control options
;
; str param4 = the control's label, used to access the control's object
;              example: TestGui.Controls["Label"].SetText("sample text")
;              this parameter defaults to a 'stripped' version of the control's text
;              for example, control text of "Hello World!" would correspond to
;              a control label of "HelloWorld."  anything that is not a letter,
;              number, or underscore is stripped to create the label
;
; int param5 = specifies whether the control is a data control. see below

TestGui.Add("Text", "Hello World!", "+Section",, 0)
TestGui.Add("Edit", "MyEditField", "xs")


; AddTextField()
; creates a text control with a control of your choice directly beneath it
TestGui.AddTextField("ComboBox", "ComboBox1", "Option1|Option2", 160, "xs +Center")



; 'BindMethod' supports handling events with a class method
TestGui.Add("Button", "Activate Method", "w160 xs",, 0)
TestGui.Controls["ActivateMethod"].BindMethod(TrapCard.Activate.Bind(TrapCard))

; specify a gLabel to use a subroutine instead
TestGui.Add("Button", "Activate Subroutine", "w160 xs gTestRoutine",, 0)



; the 'Controls' property of the Gui class holds key-value pairs of all controls and their labels
for Label, CtrlObj in TestGui.Controls
    MsgBox % "Ctrl Label = " Label " | Ctrl Text = " CtrlObj.GetText()


; the 'DataControls' property holds key-value pairs of only specified controls.
; the 5th parameter of Add() specifies which controls are DataControls
; this is useful for populating only controls that normally hold data
for Label, CtrlObj in TestGui.DataControls { 
    CtrlObj.SetColor(000000, "ffffff")
    CtrlObj.SetText("data control text")
}



; Hide(), Minimize(), Activate() are also available
TestGui.SetTitle("Custom Title")
TestGui.Show()



; GetText() to retrieve control data
MsgBox % TestGui.Controls["MyEditField"].GetText()



; Focus() and IsFocused() 
TestGui.Controls["MyEditField"].Focus()
if TestGui.Controls["MyEditField"].IsFocused()
    MsgBox Focused!
else
    MsgBox Not focused!

return

#Include %A_ScriptDir%\Class_Gui.ahk
#Include %A_ScriptDir%\Class_CtlColors.ahk

class TrapCard
{
    __new(Name) { 
        This.Name := Name
    }

    Activate(CtrlHwnd, GuiEvent, EventInfo, ErrLevel:="") { 
        MsgBox % "You've really done it now.`r`nCtrlHwnd= " CtrlHwnd "`r`nGuiEvent = " GuiEvent "`r`nEventInfo = " EventInfo "`r`nErrLevel = " ErrLevel
    }
}

TestRoutine:
    MsgBox % "You've activated the test subroutine!"
return
Last edited by wbm1113 on 12 Feb 2020, 17:31, edited 1 time in total.
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: [Library] Class_Gui (object-oriented GUI)

11 Feb 2020, 14:37

Thanks. That's very useful.
burque505
Posts: 1736
Joined: 22 Jan 2017, 19:37

Re: [Library] Class_Gui (object-oriented GUI)

12 Feb 2020, 11:59

@wbm1113, thanks for sharing this. A couple of humble suggestions:
1) In the class posted online, the final "}" is missing, although it's there in the github code.

2) There doesn't seem to be a graceful way to kill the GUI. I added the following code

Code: Select all

    Close() {
        WinKill % "ahk_id " This.Hwnd
        ExitApp
    }
which works if I call it via e.g.

Code: Select all

Escape::
TestGui.Close()
but I haven't yet figured out how to make it do a "GuiClose:" - like exit.

3) Running the online example, I get the following error, for which I have no workaround yet:
Capture.PNG
Capture.PNG (11.79 KiB) Viewed 1626 times
Once again, thanks for your work on this and for sharing.
Regards,
burque505
wbm1113
Posts: 18
Joined: 28 Aug 2018, 11:19

Re: [Library] Class_Gui (object-oriented GUI)

12 Feb 2020, 18:48

@burque505
I fixed the bracket--thank you.

A simple fix would look something like this:

Code: Select all

v := new GuiObj("MainWindow")

v.Show("w300 h300")
return

MainWindowGuiClose() { 
	ExitApp
}
An event handler for various GUI events would be nice, will probably add that in when the mood strikes me.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 217 guests