I had a play around encapsulating edits, radios, DDLs, checkboxes and listboxes and here is a little script which I thought some people might find useful.
It wraps most guicontrols, loads/saves settings to file, and provides callbacks on change.
It also allows out-of-order radio groups, and provides callbacks both for individual radios (fires 0 on old radio, 1 on new radio) and for the radio group (fires new index for radio group callback).
Code: Select all
; =========================================== SAMPLE SCRIPT =====================================================
#SingleInstance force
OutputDebug DBGVIEWCLEAR
History := []
ShowTooltips := 1
TooltipTimerOn := 0
TooltipTimerFn := Func("TooltipTimer")
gw := new GuiWrapper()
gw.Show("x0 y0 w220 h350")
; Demo out-of-order radio groups
gw.StartRadioGroup("Numbers", 1, Func("Changed").Bind("RG-Numbers"))
gw.AddControl("Radio1", "Radio", , "Radio1" , Func("Changed").Bind("Radio1"))
gw.AddControl("Edit1", "Edit", "w200", 100, Func("Changed").Bind("Edit1"))
gw.AddControl("Radio2", "Radio", , "Radio2", Func("Changed").Bind("Radio2"))
gw.AddControl("Edit2", "Edit", "w200", 200, Func("Changed").Bind("Edit2"))
gw.EndRadioGroup()
; Second out-of-order radio group. Defaults to option 2
gw.StartRadioGroup("Letters", 2, Func("Changed").Bind("RG-Letters"))
gw.AddControl("RadioA", "Radio", , "RadioA", Func("Changed").Bind("RadioA"))
gw.AddControl("EditA", "Edit", "w200", "AAA", Func("Changed").Bind("EditA"))
gw.AddControl("RadioB", "Radio", , "RadioB", Func("Changed").Bind("RadioB"))
gw.AddControl("EditB", "Edit", "w200", "BBB", Func("Changed").Bind("EditB"))
gw.EndRadioGroup()
; Checkbox - defaults on.
gw.AddControl("Check1", "Checkbox", "checked", "Check1", Func("Changed").Bind("Check1"))
; Name-based DDL
gw.AddControl("DDL1", "DDL", ,"A||B|C", Func("Changed").Bind("DDL1"))
; Index-based DDL
gw.AddControl("DDL2", "DDL", "AltSubmit","One||Two|Three", Func("Changed").Bind("DDL2"))
; Listbox
gw.AddControl("LB1", "listbox", ,"Apple|Banana|Cherry||", Func("Changed").Bind("LB1"))
;gw.Show("x0 y0")
return
GuiClose:
ExitApp
; Called whenever a GuiControl changes.
; This includes once at load-time with the initial value
Changed(ctrl, value){
global ShowTooltips, History, TooltipTimerFn
OutputDebug % "AHK| Callback fired for control " ctrl " with value " value
if (ShowTooltips){
History.push({ctrl: ctrl, value: value})
if (!TooltipTimerOn){
SetTimer, % TooltipTimerFn, 500
}
}
}
; Handles showing when callbacks are fired
ToolTipTimer(){
global History, TooltipTimerOn, TooltipTimerFn
max := History.length()
str := ""
for i, obj in History {
str .= "Callback fired for control " obj.ctrl " with value " obj.value "`n"
}
ToolTip % str
if (max == 0){
SetTimer, % TooltipTimerFn, Off
TooltipTimerOn := 0
} else {
History.RemoveAt(1)
}
}
; =========================================== WRAPPER CLASS =====================================================
class GuiWrapper {
static _CheckTypes := {checkbox: 1, radio: 1}
static _ListTypes := {ddl: 1, dropdownlist: 1, combobox: 1, listbox: 1}
GuiControls := {}
RadioGroups := {}
_CurrentRadioGroup := 0
_RadioGroupCount := 0
__New(hwnd := 0){
if (hwnd == 0){
Gui, +Hwndhwnd
}
this.hwnd := hwnd
this.IniName := RegExReplace(A_ScriptName, (A_IsCompiled ? "\.exe" : "\.ahk"), ".ini")
}
AddControl(name, aParams*){
; Load the value from the INI file, or get default
is_radio := (aParams[1] = "radio")
is_radio_in_group := (is_radio && this._RadioGroupCount)
is_checktype := ObjHasKey(this._CheckTypes, aParams[1])
is_listtype := ObjHasKey(this._ListTypes, aParams[1])
if (is_checktype){
; Is of a type the uses "Checked" in the options to signify default value
; strip checked from options, use it as default value
aParams[2] := RegExReplace(aParams[2], "\bchecked\b", "", checked)
; If not a radio that is in a group, get it's value from the INI file
if (!is_radio_in_group){
checked := this._ReadSetting(name, (checked) )
aParams[2] .= (checked ? " checked" : "")
}
} else if (!is_listtype){
; Load value from settings file into aParams[3]. Use current value of aParams[3] as default
aParams[3] := this._ReadSetting(name, aParams[3])
}
; If we are mid-radio group, and this is not the first radio, then remove WS_GROUP
if (this._RadioGroupCount > 1){
aParams[2] .= " -0x20000"
}
; Create the GuiControl
ctrl := new this.GuiControl(this, name, aParams*)
this.GuiControls[name] := ctrl
; List Types current values are set after creation of the GuiControl.
if (is_listtype){
value := this._ReadSetting(name, "")
if (value != ""){
ctrl.Set(value, 1, 0, 0)
}
}
; If this is a radio and we are mid radio group, then add it to the list and progress _RadioGroupCount
if (is_radio_in_group){
this.RadioGroups[this._CurrentRadioGroup]._AddRadio(ctrl)
ctrl._RadioGroupName := this._CurrentRadioGroup
this._RadioGroupCount++
}
; If this is not a radio group radio, and has a callback, fire it now.
if (!(is_radio && this._CurrentRadioGroup) && IsObject(aParams[4])){
ctrl._ChangeValueCallback.Call(ctrl.Get())
}
return ctrl
}
StartRadioGroup(name, default := 1, callback := 0){
this.RadioGroups[name] := new this.RadioGroup(this, name, default, callback)
this._CurrentRadioGroup := name
this._RadioGroupCount := 1
return this.RadioGroups[name]
}
EndRadioGroup(){
cg := this.RadioGroups[this._CurrentRadioGroup]
; Load settings from the ini file, fire initial callback for group
cg.Set(this._ReadSetting(this._CurrentRadioGroup, cg.Default), 0)
this._CurrentRadioGroup := 0
this._RadioGroupCount := 0
}
Show(aParams*){
Gui, % this.hwnd ":Show", % aParams[1], % aParams[2]
}
_ControlChanged(ctrl){
this._WriteSetting(ctrl.name, ctrl.Get())
}
_RadioGroupChanged(name, index){
this.RadioGroups[name].Set(index)
this._WriteSetting(name, index)
}
_ReadSetting(name, default := ""){
IniRead, value, % this.IniName, GuiControls, % name, % default
if (value == "ERROR")
value := default
OutputDebug % "AHK| _ReadSetting got value '" value "' for setting " name
return value
}
_WriteSetting(name, value){
OutputDebug % "AHK| _WriteSetting writing value '" value "' to setting " name
IniWrite, % value, % this.IniName, GuiControls, % name
}
class GuiControl {
_ChangeValueCallback := 0
_RadioGroupName := 0
_RadioGroupIndex := 0
; aParams 1 2 3
; Gui, Add, ControlType [, Options, Text]
__New(ParentGui, Name, aParams*){
this.ParentGui := ParentGui
Gui, % ParentGui.hwnd ":Add", % aParams[1], % "hwndhwnd " aParams[2], % aParams[3]
this.hwnd := hwnd
this.Name := name
this.Type := aParams[1]
this.IsListType := ObjHasKey(this.ParentGui._ListTypes, this.Type)
this.IsAltSumbit := RegExMatch(aParams[2], "\bAltSubmit\b")
if (IsObject(aParams[4])){
this._ChangeValueCallback := aParams[4]
}
fn := this._ChangedValue.Bind(this)
GuiControl, +g, % this.hwnd, % fn
}
; The user interacted with the guicontrol
_ChangedValue(aParams*){
if (this._RadioGroupName){
this.ParentGui._RadioGroupChanged(this._RadioGroupName, this._RadioGroupIndex)
} else {
this.Set(this.Get(), 0) ; Don't update the GuiControl
}
}
Get(){
GuiControlGet, value, , % this.hwnd
return value
}
Set(value, update_guicontrol := 1, update_ini := 1, fire_callback := 1){
if (update_guicontrol){
if (this.IsListType){
opt := "choose"
}
GuiControl, % opt , % this.hwnd, % value
}
if (update_ini){
this.ParentGui._ControlChanged(this)
}
if (fire_callback){
if (this._ChangeValueCallback != 0){
this._ChangeValueCallback.Call(value)
}
}
}
}
class RadioGroup {
Name := ""
ParentGui := 0
Radios := []
ChangeValueCallback := 0
__New(ParentGui, Name, default := 1, callback := 0){
this.ChangeValueCallback := callback
this.ParentGui := ParentGui
this.Name := name
this.Default := default
}
_AddRadio(ctrl){
this.Radios.push(ctrl)
ctrl._RadioGroupIndex := this.Radios.length()
}
Set(index){
for i, radio in this.Radios {
if (i == index)
continue
radio.Set(0, 1, 0) ; Don't update the ini
}
this.Radios[index].Set(1, 1, 0)
if (this.ChangeValueCallback != 0){
this.ChangeValueCallback.Call(index)
}
}
}
}