Here is the latest (and hopefully last?) two issues I have found with this script... The "tip" does not seem to be working. I have created a simple demo using multiple input controls. My demo script is below (which includes the script from the previous message):
#SingleInstance force
#Warn
#f10::
; can use either the pipe-delimited string or the array
inputItems := "Normal Input:|Multi-line Input:|lower only:|UPPER ONLY:|Numbers Only:|Password:|File Only:|Folder Only:"
inputArray := ["Normal Input:", "Multi-line Input:", "lower only:", "UPPER ONLY:", "Numbers Only:", "Password:", "File Only:", "Folder Only:"]
title := "Multi-input Demo"
opts := "w600 Font(Segoe UI|s9 c0F0F80)"
inputFont := " Font(Calibri|s10 cBlack Normal)"
inputNormal := "R1 Default(Just a simple sentence.) Tip(111)" . inputFont
inputMultiline := "R3 -Wrap HScroll Tip(This control accepts multiple lines of text)" . inputFont
inputLower := "R1 Lower Tip(333)" . inputFont
inputUpper := "R1 Upper Tip(444)" . inputFont
inputNumber := "R1 Number Tip(555)" . inputFont
inputPassword := "R1 Password Tip(666)" . inputFont
inputFile := "R1 Cue(Select a text file...) File(|Text File:*.txt) Tip(777)" . inputFont
inputFolder := "R1 Cue(Select a folder...) Folder(Computer) Tip(888)" . inputFont
ibx := new InputBoxEx(inputItems, title, opts, "", inputNormal, inputMultiline, inputLower, inputUpper, inputNumber, inputPassword, inputFile, inputFolder)
ibx.show()
if (ErrorLevel == "OK") {
askOK(ibx.Output)
}
else if (ErrorLevel == "Cancel") {
askCancel()
}
else if (ErrorLevel == "Close") {
askClose()
}
else if (ErrorLevel == "Timeout") {
askTimeout()
}
return
askOK(values) {
message(toString(values))
}
askCancel() {
message("CANCEL was clicked.")
}
askClose() {
message("CLOSE was clicked.")
}
askTimeout() {
message("Timeout occurred.")
}
message(msg) {
MsgBox % msg
}
toString(array, depth:=6, indent:="") {
static EOL := "`n"
result := ""
if (IsObject(array)) {
for key, value in array {
result .= "`t" . indent . key
if (IsObject(value) && depth > 1) {
result .= EOL . toString(value, depth - 1, indent . "`t")
}
else {
result .= "`t= [" . value . "]" . EOL
}
}
}
else {
result := array
}
return result
}
/*
CLASS: InputBoxEx
DESCRIPTION: Provides users an easy way to create customized/dynamic InputBoxes.
REQUIREMENT(s): AutoHotkey v. 1.1.9.x (I'm using the ":=" operator for optional parameters in functions/methods)
FEATURES: All options available in the standard AHK InputBox+more(see below)
- Option to create multiple input fields
- GUI can be customized [color and font]
- Each input field can be customized
* prompt
* font[name,color,size,style]
* no. of rows[single or multiline]
* input type[numbers only]
* display type[password, uppercase, lowercase]
* attach tooltip
* set cue banner
* add button(s) that allow users to select a file or a folder
- Option to remove or show the "Cancel" button
- Option to associate a label or a function with a particular event.
Event types:
* OK - User clicked the OK button
* Cancel - User clicked the Cancel button
* Close - User dismissed the InputBox via the Close button
* Timeout - InputBox timed out
- More features are to be added
USAGE: Script designer need only call two methods
- Use #Include InputBoxEx.ahk or #Include <InputBoxEx> (if script is located in your lib folder) or copy code and paste it on your script
- Instantiate the class using the "new" keyword and pass the appropriate parameter(s)
e.g. ibxObj := new InputBoxEx(params*) -for parameters, see documentation for __New() method-
- Then show the InputBox using the Show() method
e.g. ibxObj.Show(param) -for parameters, see documentation for Show() method-
NOTES:
Behavior:
- It behaves pretty much the same as the standard AHK InputBox.
- When the user calls the Show() method, the lines below it will not be executed until the InputBox is dismissed (via OK, Cancel, Close or Timeout). ***similar to MsgBox and AHK InputBox
- If an associated action(label or function) is assigned to an event, that subroutine will be executed first then script will continue at the lines(if any) after the Show() method.
- Text(output) that the user has input can be retieved via the ibxObj.Output[x](which is an object) property, where x is the index of a particualr input field.
e.g. Field1 := ibxObj.Output.1 (or ibxObj.Output[1]) and so on...
- When the user dismisses the InputBox, ErrorLevel is set to either of the following values(depending on the event): "OK", "Cancel", "Close", "Timeout"
*** if an event has an associated action(laber or function), and if within that subroutine ErrorLevel's value is altered by a command(e.g. Run), upon return from that subroutine,
ErrorLevel is reset back to the "event" type. This allows users to retrieve the event type via ErrorLevel on the lines after the Show() method call.
- When associating a function with an event, the function should have atleast 1 parameter & the minimum required parameter(s) should be equal to or less than 1.
*** The class passes an array/object to the function's 1st parameter which contains whatever the user has input.
*** The associated function's return value (if any) is returned by the Show() method. e.g.: IBX_EventRetVal := ibxObj.Show(param)
Others/ToDo's/Issues:
- [issue] Currently, InputBoxEx is not manually resizable (will work on this)
- [issue] Button images might appear different on other systems. Tested only on Win7, Dll files have different icon nos on different systems (will fix this)
- [intentional] InputBoxEx height is not configurable, this is done automatically.
- [intentional] Unlike input field(s) whose font can be set individually, "prompt" font is uniform and follows the whatever is set by the user for the GUI font (might allow customization)
- [todo] Implement option to allow users to add a ComboBox and/or a DateTime control instead of just an Edit control as an input field.
- [todo] Option to add an UpDown control and use the input field as a buddy control.
- [todo?] Might add option to allow users to add Checkboxes and Radio controls
- [todo?] Option to add custom buttons with custom actions
CREDITS:
Credits to tkoi and corrupt for the ILButton() function. (http://www.autohotkey.com/board/topic/37147-ilbutton-image-buttons-with-text-states-alignment/)
Credits to sbc for the GetBorderSize() function. (http://www.autohotkey.com/board/topic/60410-function-getbordersize/)
Credits to art for AddToolTip() function, I altered it a bit though. (http://www.autohotkey.com/board/topic/27670-add-tooltips-to-controls/page-2#entry431059)
*/
class InputBoxEx
{
/*
METHOD: __New()
DESCRIPTION: Constructor for the class
PARAMETER(s):
prompt(required) - Text to display, a message to the user indicating what kind of input is expected
User can pass a string or an object/array. If string, it can be a pipe-delimited list of text items to display, no. of items indicates the no. of input field(s)
Example: (String) - "Prompt One|Prompt Two|Prompt Three" ; (Object/Array) - ["Prompt One", "Prompt Two" , "Prompt Three"]
title(optional) - Title of the InputBox, If blank or omitted, it defaults to the name of the script.
optionsA(optional) - InputBox GUI options. String containing any(or none) of following(space-delimited). If blank, default settings will be used(see description below)
Example: "w450 c0xFFFFFF Font(Segoe UI|s9 cRed)"
Wn - Width of the InputBox, where n is the amount in pixels. If omitted, it defaults to 375(same with AHK standard InputBox)
C - GUI color. Specify the letter "C" followed immediately by a color name(see color chart in AHK help file) or RGB value (the 0x prefix is optional).
Font(FontName|FontOptions) or F(FontName|FontOptions) - GUI font. Specify the word "Font" or the letter "F" (not case-sensitive) followed by immediately by a string enclosed in parentheses(open and close)
String format should be: '(Font Name|Font Options)' (note pipe delimiter and parentheses), where "Font Name" is the name of the font
and "Font Options" is a space-delimited string containing standard font options(see AHK "Gui, Font" command for a list of options. e.g.: "cRed Italic s9")
Example: Font(Segoe UI|cRed s10 Italic Underline) ***note: this is a string not a function or expression, for easy parsing***
-Cancel - Specify the word "-Cancel" to remove the "Cancel" button. InputBox will only have the "OK" button if this option is specified. Default is two buttons: OK and Cancel
action(optional) - Label or Funcion to call for each InputBoxEx event. There are 4 events: OK, Cancel, Close, Timeout. (Self-explanatory)
User can pass a string or an object/array. If string, it should be a pipe-delimited list(max=4 items) of labels or functions names where item1 corresponds to the "OK" event
, item2 to the "Cancel" event, item3 to the "Close" event and item4 to the "Timeout" event. If no label or function is specified for a particular event , no action is taken.
When InputBoxEx is dismissed via any of the events stated above, user can retrieve the event type/name via the obj.Event property. (This is similar to ErrorLevel for the standard AHK InputBox)
Example: "LabelOK|LabelCancel|LabelClose|LabelTimeout" or if Array/object: ["LabelOK", "LabelCancel", "LabelClose", "LabelTimeout"] ***see notes reagrding InputBoxEx behavior***
optionsB(variadic) - InputBox input field option(s). String containing any(or none) of following(space-delimited). If blank, default settings will be used(see description below).
Number of parameters must be equal or less than the number of items in the "prompt" parameter. Each parameter corresponds to each prompt/field.
Example: "R5 Default(This is the default text) Folder(C:\Windows\System32) Font(Consolas|s9 cBlue Italic) File(C:\Users\Username\Pictures|Image Files:*.jpg; *.png; *.bmp)"
R - Number of rows of the input field(Edit control). Specify the letter "R" followed immediately by a number. If omitted it defaults to 1. (e.g.: R5 - five rows)
Password - Hides the user's input. Similar to "Hide" option for the standard AHK InputBox. This option has no effect for multi-line input fields
Number - Prevents the user from typing anything other than digits into the field.
Uppercase or Upper - The characters typed by the user are automatically converted to uppercase.
Lowercase or Lower - The characters typed by the user are automatically converted to lowercase.
-Wrap - Turns off word-wrapping in a multi-line input field.
HScroll - Adds a horizontal scrollbar, useful for input fields with the "-Wrap" option.
Font(FontName|FontOptions) or F(FontName|FontOptions) - Input field font. For usage/format, see same option for "optionsA" parameter above.
Default(DefaultText) - A string that will appear in the InputBox's edit field when the dialog first appears.
Specify the word "Default" followed immediately by a string enclosed in parentheses(open and close)
Example: Default(This is the default text.) ***note: this is a string not a function or expression, for easy parsing***
Cue(CueText) - Sets the textual cue, or tip, that is displayed by the input field to prompt the user for information.
Specify the word "Cue" followed immediately by a string enclosed in parentheses(open and close)
Example: Cue(This is a cue banner) ***note: this is a string not a function or expression, for easy parsing***
This option has no effect for multi-line edit controls.
Tip(TipText) - Sets/creates the tooltip that is displayed every time the mouse hovers over the input field/edit control.
Specify the word "Tip" followed immediately by a string enclosed in parentheses(open and close)
Example: Tip(This is a tooltip) ***note: this is a string not a function or expression, for easy parsing***
File(RootDir|Filter) - Adds a button to the right of the input field that when pressed, displays a standard dialog that allows the user to select a file.
Specify the word "File" followed immediately by a string enclosed in parentheses(open and close).
String format should be: '(RootDir|Filter)' (note pipe delimiter and parentheses), where RootDir is the root(starting) directory and filter indicates which types of files are shown by the dialog.
If 'RootDir' is omitted (e.g. by just specifying 'File(|Filter)', **note a pipe "|" is still specified or else 'Filter' will be assumed as 'RootDir'**) it defaults to the user's "My Documents" folder
The button is a 25x25 px sized button with a "search folder" icon. A tooltip with the text "Browse file" is displayed when the mouse hovers over the button.
***FILTER SYNTAX: [FileType:*FileExt1; *FileExt2] (note colon[:] delimiter between "FileType" and "FileExt" and also the semi-colon[;]+space that separates each file extension). If omitted, the filter defaults to All Files (*.*).
***see AHK "FileSelectFile" command for more info on RootDir and Filter***
Example: File(C:\Windows|Audio:*.wav; *.mp2; *.mp3) ***note: this is a string not a function or expression, for easy parsing***
Folder(RootDir) - Adds a button to the right of the input field that when pressed, displays a standard dialog that allows the user to select a folder. Path is displayed in the input field.
Specify the word "Folder" followed immediately by a string enclosed in parentheses(open and close).
String format should be: '(RootDir)' (note parentheses) , where RootDir is the root(starting) directory. If 'RootDir' is omitted (e.g. by just specifying 'Folder()') it defaults to the user's "My Documents" folder
The button is a 25x25 px sized button with a "search folder" icon. A tooltip with the text "Browse folder" is displayed when the mouse hovers over the button.
Example: Folder(C:\Windows) ***note: this is a string not a function or expression, for easy parsing***
USAGE: Use the "new" keyword to create a class instance
Syntax:
ibxObj := new InputBoxEx(prompt, title, optionsA, action, optionsB*)
Example:
ibxObj := new InputBoxEx("Name:|Address:"
, "Contact Details"
, "w400 cWhite Font(Tahoma|s9 cMaroon)"
, "OK|Cancel|Close|Timeout"
, "Tip(Enter name) Font(Segoe UI|s9)"
, "R3 Cue(Address here) Font(Segoe UI|s9)")
RETURN VALUE: A derived object
*/
__New(prompt, title:="", optionsA:="", action:="", optionsB*) {
static i := 0
__IBExMsg("new", this)
this.Prompt := [] , this.Title := (title ? title : A_ScriptName) , this.Action := [] , this.OptionsA := optionsA , this.OptionsB := optionsB
, this.Button := {OK: [], Cancel: [], File : [], Folder: []} , this.Edit := []
, this.Output := [] , this.Idx := i+=1
for x, y in {prompt: prompt, action: action} {
axn := ["OK", "Cancel", "Close", "Timeout"]
if IsObject(y) {
for k, v in y
this[x, ((x = "action") ? axn[k] : k)] := v
} else {
for a, b in this.Split(y)
this[x, ((x = "action") ? axn[a] : a)] := b
}
}
}
/*
METHOD: Show()
DESCRIPTION: Shows the InputBox
PARAMATER(s):
options(optional) - String containing any(or none) of following(space-delimited)
xn - x-position where n is the amount in pixels
yn - y-position where n is the amount in pixels
tn - timeout , where n is the amount in milliseconds
USAGE: ibxObj.Show("x10 y50 t5000")
RETURN VALUE: If an event has an associated action, particularly a function, this method returns that function's return value(if any) otherwise none
*/
Show(options:="") {
static rxn := {x: "i)\s*\Kx\d+(?=\s*)", y: "i)\s*\Ky\d+(?=\s*)", t: "i)\s*\Kt(\d+)(?=\s*)"}
; MPV - setting default values because of #warn
t := 0
x := 0
y := 0
elrv := ""
if options
for a, b in rxn
RegExMatch(options, b, %a%)
pdhw := A_DetectHiddenWindows
DetectHiddenWindows, On
if WinExist("ahk_id " this.Hwnd)
Gui, % this.Hwnd ":Show" , % (x ? (y ? x " " y : x) : (y ? y : "")) , % this.Title
else {
this.Create()
, this.Show(options)
, this.Message(0x112, true, 0x0111, true) ; WM_COMMAND:=0x0111 | WM_SYSCOMMAND:=0x112
/*
if t {
;~ start := A_TickCount
;~ while ((A_TickCount - start) <> t1) ;~ Sleep, % t1
;~ continue
;~ this.EventHandler(3)
WinWaitClose, % "ahk_id " this.Hwnd,, % t1/1000
if ErrorLevel
this.EventHandler(3)
}
*/
WinWaitClose, % "ahk_id " this.Hwnd,, % (t ? t1/1000 : "")
if ErrorLevel ; in case timeout is specified
this.EventHandler(3)
Errorlevel := (elrv := this.EventHandler(6)).event ; set ErrorLevel to the event type
}
DetectHiddenWindows, % pdhw
if elrv
return elrv.retval
}
;=================
;~ PRIVATE METHODS
;=================
Create() {
; REGEX NEEDLES (for maintainability purposes, easier to add/remove/alter)
static rxn := {color: "\s*(C|c)\K[^\s]*" ; Color
, font: "i)\s*\K(f|font)\(([^\)]*)\)" ; Font
, cancel: "i)\s*\K\-Cancel(?=\s*)" ; -Cancel
, owner: "i)\s*\Kowner[^\s]*" ; Gui Owner
, mws: "\s+" ; Multiple whitespace(s)
, width: "i)\s*\Kw(\d+)(?=\s*)" ; Width
, default: "i)\s*\Kdefault\(([^\)]*)\)" ; Default
, cue: "i)\s*\Kcue\(([^\)]*)\)" ; Cue Banner
, tip: "i)\s*\Ktip\(([^\)]*)\)" ; Tooltip
, file: "i)\s*\Kfile\(([^\)]*)\)" ; File search
, folder: "i)\s*\Kfolder\(([^\)]*)\)"
, mid: "\(\K[^\)]*"}
;~ BUILD GUI
Gui, New ; Create a nameless GUI to avoid conflict with existing GUI numbers | will identify it via Hwnd
Gui , % "+LastFound" (RegExMatch(this.optionsA, rxn.owner, owner) ? " +" owner : "") ; GUI options
this.Hwnd := WinExist() ; Store Hwnd
optionsA := RegExMatch(this.OptionsA, rxn.font, fontA) ? RegExReplace(this.OptionsA, rxn.font, "") : this.OptionsA
optionsA := RegExMatch(optionsA, rxn.cancel, cancel) ? RegExReplace(optionsA, rxn.cancel, "") : optionsA
optionsA := Trim(RegExReplace(optionsA, rxn.mws, A_Space))
;~ SET COLOR
if RegExMatch(optionsA, rxn.color, clr)
Gui, Color , % clr
;~ SET GUI FONT
if fontA {
fA := this.Split(fontA2)
Gui , Font , % Trim(fA.2), % Trim(fA.1)
}
Gui, Margin, 10, 10
;~ GET BORDER SIZE
bdr := this.BorderSize(this.Hwnd)
;~ if !bdr
;~ bdr := this.BorderSize(this.Hwnd)
; MPV - setting default values because of #warn
fontB := ""
cue := ""
default := ""
eO := ""
file := ""
folder := ""
o := ""
r := ""
tip := ""
;~ SET INPUT FIELD(s)
for x, y in this.Prompt {
pRow := this.Split(y, "`n").MaxIndex()
;~ SET PROMPT WIDTH - to automatically wrap long text
wSub := 20+abs(bdr.L)+abs(bdr.R)
pW := RegExMatch(optionsA, rxn.width, ibxW) ? (ibxW1-wSub) : (375-wSub)
Gui , Add, Text , % "w" pW (x == 1 ? " Section" : " xs"), % y ; "r" pRow
if this.OptionsB[x] {
eO := this.OptionsB[x]
for a, b in {fontB: rxn.font, default: rxn.default, cue: rxn.cue, tip: rxn.tip, file: rxn.file, folder: rxn.folder}
eO := RegExMatch(eO, b, %a%) ? RegExReplace(eO, b, "") : eO
}
eO := Trim(RegExReplace(eO, rxn.mws, A_Space))
Loop, Parse, eO, % A_Space
if A_LoopField in % (RegExMatch(eO, "i)r(\d+)", r) ? r "," : "") "Password,Number,Uppercase,Lowercase,Upper,Lower,-Wrap,HScroll"
o .= (A_LoopField = "upper" || A_LoopField = "lower") ? (A_LoopField "case ") : (A_LoopField " ")
eO := Trim(o) , o := ""
if !r
eO .= " r1" , r1 := 1
;~ SET INPUT FIELD WIDTH
wSub := file ? 29+wSub : wSub
wSub := folder ? (file ? (r1 <= 2 ? 27+wSub : wSub) : 29+wSub) : wSub
eW := "w" (RegExMatch(optionsA, rxn.width, w) ? (w1 -= wSub) " " : (w1 := 375-wSub) " ") ; 20+n1+n2:=Border Size+Margin | 375:=Default width, same with AHK std InputBox
Gui, Add, Edit, % eW " " eO " xm y+5", % default ? default1 : ""
this.Edit[x] := (hEdit := this.GetCtrl()) ; Store Edit control(s) Hwnd
;~ SET INPUT FIELD FONT
if fontB {
fB := this.Split(fontB2)
Gui , Font , % Trim(fB.2), % Trim(fB.1)
GuiControl, Font, % this.Edit[x]
Gui , Font ; Restore to default
if fontA ; if GUI font is specified, restore back
Gui , Font , % Trim(fA.2), % Trim(fA.1)
}
;~ SET CUE BANNER
if (cue && r1 == 1)
this.Cue(cue1)
;~ SET TOOLTIP
if tip
this.ToolTip(this.Edit[x], tip1)
;~ SET ADDITIONAL "BROWSE FILE/FOLDER" BUTTON(s) - if specified
if file { ; Browse file button
Gui , Add , Button , % "x+4 " (r1 <= 1 ? "yp-1 " : "yp ") "w25 h25"
this.ILButton(this.Button.File[x, "Hwnd"] := this.GetCtrl(), "Shell32.dll:55", 16, 16, 4) ; need to check icon(s) in other systems (Win7 tested)
this.ToolTip(this.Button.File[x].Hwnd, "Browse file")
__IBExMsg("btn", this.Button.File[x].Hwnd , this.Idx , x)
;~ Set options (RootDir, filter)
this.Button.File[x, "Path"] := this.Split(file1).1 ? this.Split(file1).1 : A_MyDocuments
filter := this.Split(file1).2 ? RegExReplace(this.Split(file1).2, "\:", A_Space "(") : ""
filter .= filter ? ")" : ""
this.Button.File[x, "Filter"] := filter
}
if folder { ; Browse folder button
Gui , Add , Button , % (file ? (r1 <= 2 ? "x+2 yp " : "xp y+1 ") : "x+4 " (r1 <= 1 ? "yp-1 " : "yp ")) "w25 h25"
this.ILButton(this.Button.Folder[x, "Hwnd"] := this.GetCtrl(), "imageres.dll:204", 16, 16, 4)
this.ToolTip(this.Button.Folder[x].Hwnd , "Browse folder")
__IBExMsg("btn", this.Button.Folder[x].Hwnd , this.Idx , x)
this.Button.Folder[x, "Path"] := folder1 ? folder1 : A_MyDocuments
}
}
;~ SET OK/CANCEL BUTTON(s)
if cancel { ; Single-button(OK only)
Gui, Add, Button , % "y+20 w" (w := 90) " x" (((w1+wSub)/2)-(w/2)) " h25", OK
this.Button.OK["Hwnd"] := this.GetCtrl(), this.Button.Cancel["Hwnd"] := false ; Store Hwnd
this.ILButton(this.Button.OK.Hwnd, "comres.dll:8", 16, 16, 0) ; Set button image
__IBExMsg("btn", this.Button.OK.Hwnd , this.Idx , 0)
} else { ; Default: 2 buttons(OK & Cancel)
Gui, Add, Button, % "y+20 w" (w := 90) " x" (((w1+wSub)/2)-(((w*2) +10)/2)) " h25" , OK
hBtn1 := this.GetCtrl()
Gui, Add, Button, x+10 yp wp hp, Cancel
hBtn2 := this.GetCtrl()
for a, b in [8, 10] {
this.Button[a == 1 ? "OK" : "Cancel", "Hwnd"] := hBtn%a%
this.ILButton(hBtn%a%, "comres.dll:" b, 16, 16, 0, "2,2,0,2")
__IBExMsg("btn", hBtn%a% , this.Idx , 0)
}
}
}
Destroy() {
Gui, % this.Hwnd ":Destroy"
this.Message(0x112, false, 0x0111, false) ; WM_COMMAND:=0x0111 | WM_SYSCOMMAND:=0x112
__IBExMsg("del", this.Idx)
}
EventHandler(event:=1, param:="") {
; MPV - setting default values because of #warn
static e := {0: "OK", 1: "Cancel", 2: "Close", 3: "Timeout"} , rv := "" , ev := ""
if (event <= 3) {
for a, b in this.Edit {
GuiControlGet, f, % this.Hwnd ":", % b
this.Output[a] := f
}
ev := e[event]
ErrorLevel := ev
if (axn := this.Action[ev]) {
Gui , % this.Hwnd ":Cancel"
if (IsLabel(axn) && !IsFunc(axn))
gosub % axn
else if (IsFunc(axn) && !IsLabel(axn)) {
if ((fn := Func(axn)).MaxParams >= 2 && fn.MinParams <= 2)
rv := fn.([this.Output]*)
} else if (IsLabel(axn) && IsFunc(axn))
gosub % axn
}
this.Destroy()
}
if (event == 4) { ; Select file button
Gui , % this.Hwnd ":+OwnDialogs"
FileSelectFile, file ,, % this.Button.File[param].Path, % "Select File - " this.Title, % this.Button.File[param].Filter
if !ErrorLevel
GuiControl, % this.Hwnd ":", % this.Edit[param] , % file
}
if (event == 5) { ; Select folder button
Gui , % this.Hwnd ":+OwnDialogs"
FileSelectFolder, folder , % this.Button.Folder[param].Path,, % "Select Folder - " this.Title
if !ErrorLevel
GuiControl, % this.Hwnd ":", % this.Edit[param] , % folder
}
if (event == 6) {
elrv := []
elrv["retval"] := rv , elrv["event"] := ev
rv := "" , ev := ""
return elrv
}
}
Message(params*) {
static fn := [] , fnc := "__IBExMsg"
for a, b in params {
if Mod(a, 2) {
f := OnMessage(b, (m := params[a+1]) ? fnc : (fn.HasKey(b) ? fn[b] : ""))
if (f && f <> fnc && m)
fn[b] := f
}
}
}
Split(str, delim:="|") {
split := []
Loop, Parse, str, % delim, % A_Space A_Tab
split[A_Index] := A_LoopField
return split
}
GetCtrl(idx:=0, option:="Hwnd") { ; idx:=0("last control added") option:=[Hwnd, ClassNN]
cmd := {Hwnd: "ControlListHwnd", ClassNN: "ControlList"}
pdhw := A_DetectHiddenWindows
DetectHiddenWindows, On
WinGet, Ctrls, % cmd[option], % "ahk_id " this.Hwnd
DetectHiddenWindows, % pdhw
c := this.Split(Ctrls, "`n")
return c[!idx ? c.MaxIndex() : idx]
;~ if RegExMatch(Ctrls, "[^\n]*$", ctrl)
;~ return ctrl
}
Cue(lpMultiByteStr, idx:=0) {
if A_IsUnicode
WideCharStr := lpMultiByteStr
else {
nSize := DllCall("MultiByteToWideChar", "UInt", (CP_ACP := 0), "Uint", 0, (PtrType := (A_PtrSize=8) ? "Ptr" : "UInt", lpMultiByteStr, "Int"), -1, "UInt", 0, "Int", 0)
VarSetCapacity(WideCharStr, nSize*2, 0)
DllCall("MultiByteToWideChar", "UInt", CP_ACP, "UInt", 0, PtrType, lpMultiByteStr, "Int", nSize, "Str", WideCharStr, "Int", nSize)
}
SendMessage, 0x1501, true, &WideCharStr,, % "ahk_id " this.Edit[idx ? idx : this.Edit.MaxIndex()] ; EM_SETCUEBANNER
return ErrorLevel
}
ToolTip(hCtrl:="", text:="", modify:=0) {
; MPV - setting default values because of #warn
static hTT :=0 , hGui := 0, Ptr := 0
hParent := DllCall("GetParent", "Ptr", hCtrl)
if (hParent <> hGui) {
;~ If (!hTT) {
;~ hGui := DllCall("GetParent", "Ptr", hCtrl)
hGui := hParent
hTT := DllCall("CreateWindowEx", "Uint", 0, "Str", "TOOLTIPS_CLASS32", "Uint", 0, "Uint", 2147483648 | 3, "Uint", -2147483648
, "Uint", -2147483648, "Uint", -2147483648, "Uint", -2147483648, "Uint", hGui, "Uint", 0, "Uint", 0, "Uint", 0)
Ptr := (A_PtrSize ? "Ptr" : "UInt") ; , DllCall("uxtheme\SetWindowTheme","Uint",hTT,Ptr,0,"UintP",0) ; TTM_SETWINDOWTHEME
}
Varsetcapacity(TInfo, 44, 0), Numput(44, TInfo), Numput(1|16, TInfo, 4), Numput(hGui, TInfo, 8), Numput(hCtrl, TInfo, 12), Numput(&text, TInfo, 36)
!modify ? (DllCall("SendMessage", Ptr, hTT, "Uint", 1028, Ptr, 0, Ptr, &TInfo, Ptr)) ; TTM_ADDTOOL := 1028 (add a tool and assign to control)
. (DllCall("SendMessage", Ptr, hTT, "Uint", 1048, Ptr, 0, Ptr, A_ScreenWidth)) ; TTM_SETMAXTIPWIDTH := 1048 (Multi-line tooltip)
DllCall("SendMessage", Ptr, hTT, "UInt", (A_IsUnicode ? 0x439 : 0x40c), Ptr, 0, Ptr, &TInfo, Ptr) ; TTM_UPDATETIPTEXT (OLD_MSG:=1036) (Modify Tooltip text)
}
BorderSize(hwnd) {
; MPV - setting default values because of #warn
pt := 0
ClientX := 0
ClientY := 0
WinGetPos, WinX, WinY, WinW, WinH, ahk_id %hwnd%
VarSetCapacity(rc, 16) , DllCall("GetClientRect", "uint", hwnd, "uint", &rc) , ClientW := NumGet(rc, 8, "int") , ClientH := NumGet(rc, 12, "int")
NumPut(ClientX, pt, 0) , NumPut(ClientY, pt, 4) , VarSetCapacity(pt, 16) , DllCall("ClientToScreen", "uint", hwnd, "uint", &pt)
ClientX := NumGet(pt, 0, "int") , ClientY := NumGet(pt, 4, "int") , Top := ClientY - WinY , Left := ClientX - WinX
Bottom := WinH - ClientH - Top , Right := WinW - ClientW - Left
return {L: Left, R: Right, T: Top, B: Bottom}
}
/*
Title: ILButton
Version: 1.1
Author: tkoi <http://www.autohotkey.net/~tkoi>
License: GNU GPLv3 <http://www.opensource.org/licenses/gpl-3.0.html>
Function: ILButton()
Creates an imagelist and associates it with a button.
Parameters:
hBtn - handle to a buttton
images - a pipe delimited list of images in form "file:zeroBasedIndex"
- file must be of type exe, dll, ico, cur, ani, or bmp
- there are six states: normal, hot (hover), pressed, disabled, defaulted (focused), and stylushot
- ex. "normal.ico:0|hot.ico:0|pressed.ico:0|disabled.ico:0|defaulted.ico:0|stylushot.ico:0"
- if only one image is specified, it will be used for all the button's states
- if fewer than six images are specified, nothing is drawn for the states without images
- omit "file" to use the last file specified
- ex. "states.dll:0|:1|:2|:3|:4|:5"
- omitting an index is the same as specifying 0
- note: within vista's aero theme, a defaulted (focused) button fades between images 5 and 6
cx - width of the image in pixels
cy - height of the image in pixels
align - an integer between 0 and 4, inclusive. 0: left, 1: right, 2: top, 3: bottom, 4: center
margin - a comma-delimited list of four integers in form "left,top,right,bottom"
Notes:
A 24-byte static variable is created for each IL button
Tested on Vista Ultimate 32-bit SP1 and XP Pro 32-bit SP2.
Changes:
v1.1
Updated the function to use the assume-static feature introduced in AHK version 1.0.48
*/
ILButton(hBtn, images, cx=16, cy=16, align=4, margin="1,1,1,1") { ; http://www.autohotkey.com/board/topic/37147-ilbutton-image-buttons-with-text-states-alignment/page-3
static
static i = 0
local himl, v0, v1, v2, v3, ext, hbmp, hicon := 0
i ++
himl := DllCall("ImageList_Create", "Int",cx, "Int",cy, "UInt",0x20, "Int",1, "Int",5, "UPtr")
Loop, Parse, images, |
{
Pos := InStr(A_LoopField, ":", false, 3)
if(pos)
{
v1 := SubStr(A_LoopField, 1, pos - 1)
v2 := SubStr(A_LoopField, pos + 1)
}
else
v1 := A_LoopField
SplitPath, v1, , , ext
if(ext = "bmp")
{
hbmp := DllCall("LoadImage", "UInt",0, "Str",v1, "UInt",0, "UInt",cx, "UInt",cy, "UInt",0x10, "UPtr")
DllCall("ImageList_Add", "Ptr",himl, "Ptr",hbmp, "Ptr",0)
DllCall("DeleteObject", "Ptr", hbmp)
}
else
{
DllCall("PrivateExtractIcons", "Str",v1, "Int",v2, "Int",cx, "Int",cy, "PtrP",hicon, "UInt",0, "UInt",1, "UInt",0)
DllCall("ImageList_AddIcon", "Ptr",himl, "Ptr",hicon)
DllCall("DestroyIcon", "Ptr", hicon)
}
}
; Create a BUTTON_IMAGELIST structure
VarSetCapacity(struct%i%, A_PtrSize + (5 * 4) + (A_PtrSize - 4), 0)
NumPut(himl, struct%i%, 0, "Ptr")
Loop, Parse, margin, `,
NumPut(A_LoopField, struct%i%, A_PtrSize + ((A_Index - 1) * 4), "Int")
NumPut(align, struct%i%, A_PtrSize + (4 * 4), "UInt")
; BCM_FIRST := 0x1600, BCM_SETIMAGELIST := BCM_FIRST + 0x2
PostMessage, 0x1602, 0, &struct%i%, , ahk_id %hBtn%
Sleep 1 ; workaround for a redrawing problem on WinXP
}
}
__IBExMsg(wParam, lParam, msg:="", hwnd:="") {
static ix := [] , i := 0 , btn := [] , WM_COMMAND := 0x0111 , WM_SYSCOMMAND := 0x112 , BN_CLICKED := 0x0000 , SC_CLOSE := 0xF060
if (wParam == "new" && IsObject(lParam))
ix[i += 1] := lParam
if (wParam == "del") { ; Remove stored info in object upon calling Destroy() method
keys := []
for x, y in btn
if (y.o == lParam)
keys.Insert(x)
for a, b in keys
if btn.HasKey(b)
btn.Remove(b, "")
}
if (wParam == "btn")
btn[lParam] := {o: msg , b: hwnd}
if (msg == WM_COMMAND) {
lo := wParam & 0xFFFF
hi := wParam >> 16
if (hi == BN_CLICKED) {
if btn.HasKey(lParam) {
j := btn[lParam].o , k := btn[lParam].b
if (lParam == ix[j].Button.OK.Hwnd) ; Button OK
ix[j].EventHandler(0)
if (lParam == ix[j].Button.Cancel.Hwnd) ; Button Cancel
ix[j].EventHandler(1)
if (lParam == ix[j].Button.File[k].Hwnd) ; Browse File
ix[j].EventHandler(4, k)
if (lParam == ix[j].Button.Folder[k].Hwnd) ; Browse Folder
ix[j].EventHandler(5, k)
}
}
}
if (msg == WM_SYSCOMMAND && wParam == SC_CLOSE) {
Loop, % ix.MaxIndex()
if (hwnd == ix[A_Index].Hwnd)
ix[A_Index].EventHandler(2)
}
}
Can anyone offer any suggestions on how to fix the "tip"?
Also, I was not able to get the "actions" to work if I specified a function, but I could make them work using labels. Instead, I just opted for parsing the ErrorLevel to sending it to the function directly. I welcome any thoughts on getting that working, too.
Mike V.