Help with InputBoxEx

Get help with using AutoHotkey and its commands and hotkeys
Guest

Help with InputBoxEx

09 Jan 2014, 15:47

I have been posting on the forums on autohotkey.com, but no one seems to be answering anything over there. So, rather than try to duplicate all of my posts here, I am going to put a link to the thread there.

I would REALLY appreciate it is someone could answer me here or there. Thank you.

http://www.autohotkey.com/board/topic/8 ... -inputbox/

Mike V.
Guest

Re: Help with InputBoxEx

09 Jan 2014, 16:34

After giving this a little thought (and some cleanup of my example), I have decided to post the example here. Basically, I am looking to get the issues under the "TODO" header to work.

Code: Select all

#SingleInstance force
#Warn

#f8::
	askDemoOld()
	return

#f9::
	askDemoSingle()
	return

#f10::
	askDemoMulti()
	return

/*
; http://www.autohotkey.com/board/topic/88377-class-inputboxex-customizable-inputbox/
; http://ahkscript.org/boards/viewtopic.php?f=5&t=1352

TODO
-------------------
- Bug (severity 4): Tip() does not work
- Bug (severity 1): If multiple InputBoxes are shown, and the first one is closed/canceled, the result is not processed until the second one is closed
- Bug (severity 1): If multiple InputBoxes are shown, and the second one is closed, the first one "gets lost" and is never processed
- Add (severity 2): Have ESC trigger the 'Cancel' button
- Add (severity 2): Have ENTER trigger the 'OK' button
*/

askDemoOld() {
	value :=	ask("Please enter a phrase or value...", "Simple Input (old)", 500)
	if	(ErrorLevel) {
		message("User canceled the input for askDemoOld()...")
	}
	else {
		message("value = " . toString(value))
	}
}

askDemoSingle() {
	value :=	askMulti("Please enter a phrase or value...", "Simple Input (new)")
	if	(ErrorLevel == "OK") {
		message("value = " . toString(value))
	}
	else {
		message("User canceled the input for askDemoSingle()...")
	}
}

askDemoMulti() {
	inputs :=	["Normal Input:|Default(Just a simple sentence.) Font(Courier New|s14 cRed Italic)", "Multi-line Input:|R3 -Wrap HScroll", "lower only:|Lower Font(Sans Serif|s12 cGreen Bold)", "UPPER ONLY:|Upper", "Numbers Only:|Number", "Password:|Password", "File Only:|File(|Text File:*.txt)", "Folder Only:|Folder(Computer)"]
	title :=	"Multi-input Demo"
	values :=	askMulti(inputs, title)
	if	(IsObject(values)) {
		message("values = " . toString(values))
	}
	else {
		message("User canceled the input for askDemoMulti()...")
	}
}

ask(prompt, title:="Input Request", width:=250, height:=125, defaultValue:="") {
	InputBox, value, %title%, %prompt%, , %width%, %height%, , , , , %defaultValue%
	return	value
}

askMulti(inputs, title:="", options:="") {
	static	inputFont := " Font(Calibri|s10 cBlack Normal)"
	isSingle :=	false
	if	(not IsObject(inputs)) {
		inputs :=	[inputs]
		isSingle :=	true
	}
	if	(title == "") {
		title == "Prompt for Input"
	}
	if	(options == "") {
		options :=	"w500 Font(Segoe UI|s10 c0F0F80)"
	}
	inputItems :=	[]
	inputOpts :=	[]
	for idx, inputDef in inputs {
		labelStr :=	""
		optStr :=	""
		pos :=	InStr(inputDef, "|")
		if	(pos == 0) {
			inputItems[idx] :=	inputDef
			inputOpt :=	""
		}
		else {
			inputItems[idx] :=	SubStr(inputDef, 1, pos - 1)
			inputOpt :=	SubStr(inputDef, pos + 1)
		}
		if	(not InStr(inputOpt, "Font(")) {
			inputOpt .=	" " . inputFont
		}
		inputOpts[idx] :=	inputOpt
	}
	ibx :=	new InputBoxEx(inputItems, title, options, "", inputOpts*)
	ibx.show()
	if	(ErrorLevel == "OK") {
		return	(isSingle ? ibx.Output.1 : ibx.Output)
	}
}

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)
    }
}
Mike V.
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
GitHub: cocobelgica

Re: Help with InputBoxEx

11 Jan 2014, 02:25

Hi, I'll work on a newer version of InputBoxEx which should be easier to use. I'll resolve the #Warn warnings and other bugs.
Guest

Re: Help with InputBoxEx

13 Jan 2014, 18:25

Thank you, Coco. I appreciate the assistance and look forward to trying your new version. I have tried to create an account here, but the confirmation email never arrives. :cry:

I will keep checking every few days for an update from you.

Mike V.
User avatar
mviens
Posts: 43
Joined: 08 Jan 2014, 19:04

Re: Help with InputBoxEx

30 Jan 2014, 14:12

Bump... Now that I have been able to register, hopefully I get email updates to this thread.
Mike V.
Guest10
Posts: 578
Joined: 01 Oct 2013, 02:50

Re: Help with InputBoxEx

30 Jan 2014, 14:35

Coco wrote:Hi, I'll work on a newer version of InputBoxEx which should be easier to use. I'll resolve the #Warn warnings and other bugs.
me too! looking forward to the newer version since recently i've been using InputBox more frequently. :ugeek:
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
GitHub: cocobelgica

Re: Help with InputBoxEx

13 Sep 2014, 09:37

New version, see: EntryForm

Return to “Ask For Help”

Who is online

Users browsing this forum: Albireo, Bing [Bot], Google [Bot], Odlanir, pn4265 and 253 guests