slightly-improved dialogs

Discuss the future of the AutoHotkey language
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

slightly-improved dialogs

06 Mar 2018, 19:00

- The idea here is to create slightly-improved versions of the AutoHotkey dialogs (MsgBox/InputBox/ToolTip) that support a custom font and various simple commonly-asked for features, and to also recreate Progress and SplashImage. The Borders function simply puts coloured borders around a square/rectangle (e.g. a window/control/GUI element).

1 MsgBox [big font]
2 InputBox (+ InputBox multi) [big font/multiple fields]
3 Progress [i.e. SplashText] [no additional features]
4 SplashImage [option to use IE control]
5 ToolTip [big font]
6 Borders [show coloured borders around a rectangle]

see also:
7 Find dialog [custom Find dialog with whole word/RegEx options]
Find dialog with whole word and RegEx support - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=50262

- I'm implementing the functions here in AutoHotkey v2, although I had intended ultimately to create them using my own custom GUI functions, because that way you can specify the class name (needed for all of them), no icon (e.g. MsgBox, Find dialog), and you don't cause #Persistent, and they can be used in AutoHotkey v1/v2.
- I mentioned some of my plans here:
GUI COMMANDS: COMPLETE RETHINK - Page 2 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 91#p190491

- Some points on functionality.
- I have various queries relating to the GUI functionality, in the script's to-do list.
- One idea for #Persistent could be something like object reference counts, the user can choose to increment the 'GUI count' by 1, and decrement it when finished, as a way to ensure the script remains open at least while the GUI is displaying. If the 'GUI count' ever reaches 0, the script can then close at the next appropriate point.
- Some properties that could be useful: HwndParent (or ParentHwnd), .B/.R for bottom/right, being able to set the control colours as built-in (the one 'complex' feature I would recommend adding in), specific menu item IDs.
- Having used AutoHotkey v2 GUIs for the first time in this project (apart from in my control zoo), I repeatedly noticed the benefits of this idea:
For convenience, it should be possible to pass a Gui object to a WinTitle parameter instead of "ahk_id " Gui.Hwnd.
I would suggest that it work in general for all WinXXX functions, if you pass any object which has a key 'hWnd'.

Code: Select all

oWin := {hWnd:hWnd}
vWinTitle := WinGetTitle(oWin)
vWinTitle := WinGetTitle("ahk_id " oWin.hWnd)
vWinTitle := WinGetTitle("ahk_id " hWnd)
- I'm looking for any similar/related scripts for comparison. [EDIT: See the list in a post lower down.] Although the focus here is strictly very minor oft-asked-for features.
Last edited by jeeswg on 08 Jun 2018, 05:46, edited 2 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: slightly-improved dialogs

06 Mar 2018, 19:47

First attempt which currently only covers InputBox.

Code: Select all

;AHK v2
;(the comment above makes my AHK go-between exe run the script with AHK v2)
;written for AutoHotkey v2 alpha (v2.0-a089)

;==================================================

;slightly-improved dialogs by jeeswg

;1 MsgBox [NOT IMPLEMENTED]
;2 InputBox (+ InputBox multi)
;3 Progress (+ SplashText) (no additional features) [NOT IMPLEMENTED]
;4 SplashImage [NOT IMPLEMENTED]
;5 ToolTip [NOT IMPLEMENTED]
;6 Borders [NOT IMPLEMENTED]
;7 Find dialog [NOT IMPLEMENTED]
;(TrayTip) (unchanged)
;(FileSelect) (unchanged)
;(DirSelect) (unchanged)

;==================================================

;NOTES - INPUTBOX

;to do: prevent InputBox getting lost under controls (currently it reactivates window, need logic for a general way to do this)
;to do: enable timed countdown (5, 4, 3, 2, 1, need the user of SetTimer and a custom function that receives a GUI object)
;to do+: do nothing until GUI is dismissed (an alternative to WinWaitClose?)
;to do+: what is 'W375 H189', client/window?
;to do+: how to get default margin values? (note: MarginX and MarginY start off as -1 until set by the user)
;to do+: what is the default font? I tested on Windows 7 and it was 'Segoe UI, size 10'
;to do+: output return value from InputBox [or array if InputBoxMulti] (an alternative to using global variable?)
;to do+: window/control styles (is -0xFFFFFFFF -E0xFFFFFFFF always necessary?)
;to do+: maybe a general solution re. editing dialogs is to allow a mode where you can edit the control before it's visible (could the default MsgBox/InputBox/ToolTip functions have a mode where they are hidden, so that you can edit them before they appear, possibly by temporarily blocking a show window dll function, perhaps this is more difficult with MsgBox which uses the Winapi's MessageBox function)
;to do+: what is the exact size of a button (width/height)? something like text dimensions from DrawTextEx plus some arbitrary margin value perhaps
;to do+: one 'Close' event is closing without an explicit .Destroy

;note: cannot specify no icon
;note: cannot specify class

;style: GUI: +0x94CC0A4C +E0x00010100
;style: Edit1: +0x50010080 +E0x00000204 [password: +0x500100A0 +E0x00000204]
;style: Button1: +0x50010001 +E0x00000004
;style: Button2: +0x50010000 +E0x00000004
;style: Static1: +0x50020000 +E0x00000004

;window client area
;W368 H166

;control client pos relative to window client area
;X5 Y108 W358 H24
;X68 Y137 W54 H24
;X247 Y137 W54 H24
;X5 Y5 W358 H98

;size: margin X: unknown
;size: margin Y: unknown
;size: GUI X/Y: centre of screen
;size: GUI W/H: default W375 H189 (client coordinates?)
;size: buttons both are equal to fit larger string
;resize: Edit stretched to GUI width (minus margins)
;resize: MgnX[gap]OK[double gap]Cancel[gap]MgnX
;resize: squash Static, Buttons/Edit remain in position relative to GUI bottom

;==================================================

#SingleInstance force
#Persistent
global AX_DlgTitle
global AX_ScriptNameNoExt
global AX_InputBoxResult
global AX_InputBoxOpt := {}
AX_InputBoxOpt.TextButton1 := "OK"
AX_InputBoxOpt.TextButton2 := "Cancel"
AX_InputBoxOpt.FontName := "Segoe UI"
AX_InputBoxOpt.FontSize := 18
AX_InputBoxOpt.StylesWin := "+0x94CC0A4C +E0x00010100"
AX_InputBoxOpt.StylesEdit := "+0x50010080 +E0x00000204"
AX_InputBoxOpt.StylesEditPW := "+0x500100A0 +E0x00000204"
AX_InputBoxOpt.StylesButton1 := "+0x50010001 +E0x00000004"
AX_InputBoxOpt.StylesButton2 := "+0x50010000 +E0x00000004"
AX_InputBoxOpt.StylesStatic := "+0x50020000 +E0x00000004"

SplitPath(A_ScriptFullPath,,,, AX_ScriptNameNoExt)
AX_DlgTitle := AX_ScriptNameNoExt

;InputBox("text", "title",, "default")
;InputBox("text", "title", "Password+", "default")
;InputBox("text",,, "default")

;InputBoxNew("text", "title", "Password+", "default")
;InputBoxNew("text", "title",, "default")
;InputBoxNew("text",,, "default")

;InputBoxNew("text", "title", "T1000", "default")
vRet := InputBoxNew("text", "title",, "default")
MsgBox(vRet)
;MsgBox(ErrorLevel "`r`n" AX_InputBoxResult)

;InputBoxMulti(["ITEM 1`naaa","ITEM 2`nbbb","ITEM 3`nccc"], vWinTitle:="", vOpt:="", ["AAA","BBB","CCC"])
oArray := InputBoxMulti(["aaa","bbb","ccc"], vWinTitle:="", vOpt:="", ["AAA","BBB","CCC"])
if IsObject(oArray)
	MsgBox(oArray.1 "`r`n" oArray.2 "`r`n" oArray.3 "`r`n" oArray.4)
else
	MsgBox("")
;MsgBox(ErrorLevel "`r`n" AX_InputBoxResult)
return

;==================================================

InputBoxNew(vText:="", vWinTitle:="", vOpt:="", vDefault:="")
{
	global oInputBoxRet
	if !IsObject(oInputBoxRet)
		oInputBoxRet := []
	hWnd := WinExist("A")

	DetectHiddenWindows(1)
	vPosWin := "", vTimeout := "", vPassChar := ""
	vMgnX := 5, vMgnY := 5
	vWinW := 368, vWinH := 166

	Loop Parse, vOpt, " `t"
	{
		if (SubStr(A_LoopField, 1, 1) = "W")
			vWinW := SubStr(A_LoopField, 2)
		else if (SubStr(A_LoopField, 1, 1) = "H")
			vWinH := SubStr(A_LoopField, 2)

		if (A_LoopField ~= "i)^[XYWH]")
			vPosWin .= A_LoopField " "
		else if (SubStr(A_LoopField, 1, 1) = "T")
			vTimeout := SubStr(A_LoopField, 2)
		else if (A_LoopField = "Password")
			vHasPassChar := 1
		else if (SubStr(A_LoopField, 1, 8) = "Password")
		&& (StrLen(A_LoopField) > 8)
			vPassChar := SubStr(A_LoopField, 9, 1), vHasPassChar := 1
	}
	if !InStr(vPosWin, "W")
		vPosWin .= " W" vWinW
	if !InStr(vPosWin, "H")
		vPosWin .= " H" vWinH

	vStyleReset := "-0xFFFFFFFF -E0xFFFFFFFF "

	vTextBtn1 := AX_InputBoxOpt.TextButton1
	vTextBtn2 := AX_InputBoxOpt.TextButton2
	vFontName := AX_InputBoxOpt.FontName
	vFontSize := AX_InputBoxOpt.FontSize
	vStylesWin := vStyleReset AX_InputBoxOpt.StylesWin
	vStylesEdit := AX_InputBoxOpt.StylesEdit
	vStylesEditPW := AX_InputBoxOpt.StylesEditPW
	vStylesButton1 := AX_InputBoxOpt.StylesButton1
	vStylesButton2 := AX_InputBoxOpt.StylesButton2
	vStylesStatic := AX_InputBoxOpt.StylesStatic

	vFontOpt := "s" vFontSize
	hFont := JEE_FontCreate(vFontName, vFontSize)
	JEE_StrGetDim(vTextBtn1, hFont, vBtn1W, vBtn1H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	JEE_StrGetDim(vTextBtn2, hFont, vBtn2W, vBtn2H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	vBtnW := Max(vBtn1W, vBtn2W) + 16
	vBtnH := Max(vBtn1H, vBtn2H) + 7

	vEditW := vWinW - 2*vMgnX
	vGap := Round((vWinW - 2*vBtnW - 2*vMgnX)/4)
	vBtn1X := vMgnX + vGap
	vBtn2X := vMgnX + vGap*3 + vBtnW
	vBtnY := vWinH - vMgnY - vBtnH
	vEditY := vBtnY - vMgnY - vBtnH
	vStcH := vEditY - 5
	vPosEdit := Format("X{} Y{} W{} H{}", vMgnX, vEditY, vEditW, vBtnH)
	vPosBtn1 := Format("X{} Y{} W{} H{}", vBtn1X, vBtnY, vBtnW, vBtnH)
	vPosBtn2 := Format("X{} Y{} W{} H{}", vBtn2X, vBtnY, vBtnW, vBtnH)
	vPosStc := Format("X{} Y{} W{} H{}", vMgnX, vMgnY, vEditW, vStcH)

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	;WS_POPUPWINDOW := 0x80880000 ;composite style (WS_POPUP | WS_BORDER | WS_SYSMENU)
	;WS_VISIBLE := 0x10000000
	;WS_CLIPSIBLINGS := 0x4000000
	;WS_CAPTION := 0xC00000 ;composite style (WS_BORDER | WS_DLGFRAME)
	;WS_THICKFRAME := 0x40000
	;WS_EX_CONTROLPARENT := 0x10000
	;WS_EX_WINDOWEDGE := 0x100
	oGui := GuiCreate(vStylesWin, vWinTitle)
	oGui.SetFont(vFontOpt, vFontName)
	oGui.OnEvent("Close", Func("InputBoxNew_Close").Bind(oGui, "Close"))
	oGui.OnEvent("Escape", Func("InputBoxNew_Close").Bind(oGui, "Escape"))
	hGui := oGui.hWnd

	oGui.MarginX := vMgnX
	oGui.MarginY := vMgnY

	if !vHasPassChar
		oGui.Add("Edit", vStylesEdit " " vPosEdit, vDefault)
	else
	{
		oGui.Add("Edit", vStylesEditPW " " vPosEdit, vDefault)
		if !(vPassChar = "")
			PostMessage(0xCC, Ord(vPassChar),, "Edit1", "ahk_id " hGui) ;EM_SETPASSWORDCHAR := 0xCC
	}
	ControlFocus("Edit1", "ahk_id " hGui)
	PostMessage(0xB1, 0, -1, "Edit1", "ahk_id " hGui) ;EM_SETSEL := 0xB1 ;select all

	oBtn1 := oGui.Add("Button", vStylesButton1 " " vPosBtn1, vTextBtn1)
	oBtn2 := oGui.Add("Button", vStylesButton2 " " vPosBtn2, vTextBtn2)
	oBtn1.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "OK"))
	oBtn2.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "Cancel"))
	oGui.Add("Text", vStylesStatic " " vPosStc, vText)

	oGui.Show(vPosWin)

	oGui.OnEvent("Size", "InputBoxNew_Size")

	if !(vTimeout = "")
	{
		BoundFuncTemp := Func("InputBoxNew_Close").Bind(oGui, "Timeout")
		SetTimer(BoundFuncTemp, -vTimeout)
	}

	WinWaitClose("ahk_id " hGui)

	if WinExist("ahk_id " hWnd)
		WinActivate("ahk_id " hWnd)

	if oInputBoxRet.Length()
		return oInputBoxRet[hGui].1
	else
		return
}
InputBoxNew_Close(oGui, vBtn)
{
	global oInputBoxRet
	;MsgBox(A_ThisFunc " " oGui.hWnd " " vBtn)
	if (vBtn = "OK")
	{
		oArray := []
		Loop
		{
			hCtl := ControlGetHwnd("Edit" A_Index, "ahk_id " oGui.hWnd)
			if !hCtl
				break
			oArray.Push(ControlGetText("", "ahk_id " hCtl))
		}
		oInputBoxRet[oGui.hWnd] := oArray
		ErrorLevel := 0
		AX_InputBoxResult := 0
	}
	else
	{
		ErrorLevel := 1
		AX_InputBoxResult := 1
	}
	oGui.Destroy
	return
}
InputBoxNew_Size(oGui)
{
	WinGetClientPos(vWinX, vWinY, vWinW, vWinH, "ahk_id " oGui.hWnd)
	vMgnX := oGui.MarginX
	vMgnY := oGui.MarginY
	ControlGetPos(,, vBtnW, vBtnH, "Button1", "ahk_id " oGui.hWnd)

	vEditW := vWinW - 2*vMgnX
	vGap := Round((vWinW - 2*vBtnW - 2*vMgnX)/4)
	vBtn1X := vMgnX + vGap
	vBtn2X := vMgnX + vGap*3 + vBtnW
	vBtnY := vWinH - vMgnY - vBtnH
	vEditY := vBtnY - vMgnY - vBtnH
	vStcH := vEditY - 5
	ControlMove(vMgnX, vEditY, vEditW, vBtnH, "Edit1", "ahk_id " oGui.hWnd)
	ControlMove(vBtn1X, vBtnY, vBtnW, vBtnH, "Button1", "ahk_id " oGui.hWnd)
	ControlMove(vBtn2X, vBtnY, vBtnW, vBtnH, "Button2", "ahk_id " oGui.hWnd)
	ControlMove(vMgnX, vMgnY, vEditW, vStcH, "Static1", "ahk_id " oGui.hWnd)
}

;==================================================

;no handling for passwords
;no handling for resize
InputBoxMulti(oText:="", vWinTitle:="", vOpt:="", oDefault:="")
{
	global oInputBoxRet
	if !IsObject(oInputBoxRet)
		oInputBoxRet := []
	hWnd := WinExist("A")

	if !IsObject(oText)
		oText := [oText]
	if !IsObject(oDefault)
		oDefault := [oDefault]

	DetectHiddenWindows(1)
	vPosWin := "", vTimeout := "", vPassChar := ""
	vMgnX := 5, vMgnY := 5
	vWinW := 368, vWinH := 166

	Loop Parse, vOpt, " `t"
	{
		if (SubStr(A_LoopField, 1, 1) = "W")
			vWinW := SubStr(A_LoopField, 2)
		else if (SubStr(A_LoopField, 1, 1) = "H")
			vWinH := SubStr(A_LoopField, 2)

		if (A_LoopField ~= "i)^[XYWH]")
			vPosWin .= A_LoopField " "
		else if (SubStr(A_LoopField, 1, 1) = "T")
			vTimeout := SubStr(A_LoopField, 2)
	}

	vStyleReset := "-0xFFFFFFFF -E0xFFFFFFFF "

	vTextBtn1 := AX_InputBoxOpt.TextButton1
	vTextBtn2 := AX_InputBoxOpt.TextButton2
	vFontName := AX_InputBoxOpt.FontName
	vFontSize := AX_InputBoxOpt.FontSize
	vStylesWin := vStyleReset AX_InputBoxOpt.StylesWin
	vStylesEdit := AX_InputBoxOpt.StylesEdit
	vStylesEditPW := AX_InputBoxOpt.StylesEditPW
	vStylesButton1 := AX_InputBoxOpt.StylesButton1
	vStylesButton2 := AX_InputBoxOpt.StylesButton2
	vStylesStatic := AX_InputBoxOpt.StylesStatic

	vFontOpt := "s" vFontSize
	hFont := JEE_FontCreate(vFontName, vFontSize)
	JEE_StrGetDim(vTextBtn1, hFont, vBtn1W, vBtn1H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	JEE_StrGetDim(vTextBtn2, hFont, vBtn2W, vBtn2H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	vBtnW := Max(vBtn1W, vBtn2W) + 16
	vBtnH := Max(vBtn1H, vBtn2H) + 7

	vGap := Round((vWinW - 2*vBtnW - 2*vMgnX)/4)
	vBtn1X := vMgnX + vGap
	vBtn2X := vMgnX + vGap*3 + vBtnW

	vEditW := vWinW - 2*vMgnX
	vEditH := vBtnH
	vStcW := vEditW

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	;WS_POPUPWINDOW := 0x80880000 ;composite style (WS_POPUP | WS_BORDER | WS_SYSMENU)
	;WS_VISIBLE := 0x10000000
	;WS_CLIPSIBLINGS := 0x4000000
	;WS_CAPTION := 0xC00000 ;composite style (WS_BORDER | WS_DLGFRAME)
	;WS_THICKFRAME := 0x40000
	;WS_EX_CONTROLPARENT := 0x10000
	;WS_EX_WINDOWEDGE := 0x100
	oGui := GuiCreate(vStylesWin, vWinTitle)
	oGui.SetFont(vFontOpt, vFontName)
	oGui.OnEvent("Close", Func("InputBoxNew_Close").Bind(oGui, "Close"))
	oGui.OnEvent("Escape", Func("InputBoxNew_Close").Bind(oGui, "Escape"))
	hGui := oGui.hWnd

	vPosX := vMgnX, vPosY := vMgnY
	Loop Max(oText.Length(), oDefault.Length())
	{
		if (A_Index <= oDefault.Length())
		{
			JEE_StrGetDim(oText[A_Index], hFont, vStcW2, vStcH, vDTFormat:=0x400, vLimW:=vWinW, vLimH:="")
			vPosStc := Format("X{} Y{} W{} H{}", vPosX, vPosY, vStcW, vStcH)
			oGui.Add("Text", vStylesStatic " " vPosStc, oText[A_Index])
			vPosY += vStcH + vMgnY
		}
		if (A_Index <= oDefault.Length())
		{
			vPosEdit := Format("X{} Y{} W{} H{}", vPosX, vPosY, vEditW, vEditH)
			oGui.Add("Edit", vStylesEdit " " vPosEdit, oDefault[A_Index])
			vPosY += vEditH + vMgnY
		}
	}

	vBtnY := vPosY
	vPosBtn1 := Format("X{} Y{} W{} H{}", vBtn1X, vBtnY, vBtnW, vBtnH)
	vPosBtn2 := Format("X{} Y{} W{} H{}", vBtn2X, vBtnY, vBtnW, vBtnH)
	vPosY += vBtnH + vMgnY

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	oGui.MarginX := vMgnX
	oGui.MarginY := vMgnY

	ControlFocus("Edit1", "ahk_id " hGui)
	PostMessage(0xB1, 0, -1, "Edit1", "ahk_id " hGui) ;EM_SETSEL := 0xB1 ;select all

	oBtn1 := oGui.Add("Button", vStylesButton1 " " vPosBtn1, vTextBtn1)
	oBtn2 := oGui.Add("Button", vStylesButton2 " " vPosBtn2, vTextBtn2)
	oBtn1.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "OK"))
	oBtn2.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "Cancel"))

	if !InStr(vPosWin, "W")
		vPosWin .= " W" vWinW
	if !InStr(vPosWin, "H")
		vPosWin .= " H" vPosY
	oGui.Show(vPosWin)

	;oGui.OnEvent("Size", "InputBoxNew_Size")

	if !(vTimeout = "")
	{
		BoundFuncTemp := Func("InputBoxNew_Close").Bind(oGui, "Timeout")
		SetTimer(BoundFuncTemp, -vTimeout)
	}

	WinWaitClose("ahk_id " hGui)

	if WinExist("ahk_id " hWnd)
		WinActivate("ahk_id " hWnd)

	if oInputBoxRet.Length()
		return oInputBoxRet[hGui]
	else
		return
}

;==================================================

;e.g. hFont := JEE_FontCreate("Arial", 12, "bius")

;JEE_CreateFont
JEE_FontCreate(vName, vSize, vFontStyle:="", vWeight:="")
{
	vHeight := -DllCall("kernel32\MulDiv", Int,vSize, Int,A_ScreenDPI, Int,72)
	vWidth := 0
	vEscapement := 0
	vOrientation := 0
	vWeight := (InStr(vFontStyle, "b") && (vWeight="")) ? 700 : 400
	vItalic := InStr(vFontStyle, "i") ? 1 : 0
	vUnderline := InStr(vFontStyle, "u") ? 1 : 0
	vStrikeOut := InStr(vFontStyle, "s") ? 1 : 0
	vCharSet := 0
	vOutPrecision := 0
	vClipPrecision := 0
	vQuality := 0
	vPitchAndFamily := 0
	vFaceName := vName
	vOutPrecision := 3
	vClipPrecision := 2
	vQuality := 1
	vPitchAndFamily := 34
	return DllCall("gdi32\CreateFont", Int,vHeight, Int,vWidth, Int,vEscapement, Int,vOrientation, Int,vWeight, UInt,vItalic, UInt,vUnderline, UInt,vStrikeOut, UInt,vCharSet, UInt,vOutPrecision, UInt,vClipPrecision, UInt,vQuality, UInt,vPitchAndFamily, Str,vFaceName, Ptr)
}

;==================================================

;vLimW and vLimH if present are used as limits
;JEE_DrawText
JEE_StrGetDim(vText, hFont, ByRef vTextW, ByRef vTextH, vDTFormat:=0x400, vLimW:="", vLimH:="")
{
	;DT_EDITCONTROL := 0x2000 ;DT_NOPREFIX := 0x800
	;DT_CALCRECT := 0x400 ;DT_NOCLIP := 0x100
	;DT_EXPANDTABS := 0x40 ;DT_SINGLELINE := 0x20
	;DT_WORDBREAK := 0x10

	;HWND_DESKTOP := 0
	hDC := DllCall("user32\GetDC", Ptr,0, Ptr)
	hFontOld := DllCall("gdi32\SelectObject", Ptr,hDC, Ptr,hFont, Ptr)
	VarSetCapacity(SIZE, 8, 0)
	vTabLengthText := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	DllCall("gdi32\GetTextExtentPoint32", Ptr,hDC, Str,vTabLengthText, Int,52, Ptr,&SIZE)
	vTabLength := NumGet(&SIZE, 0, "Int") ;cx ;logical units
	vTabLength := Floor((vTabLength/52)+0.5)
	vTabLength := Round(vTabLength*(72/A_ScreenDPI))
	vLen := StrLen(vText)

	VarSetCapacity(DRAWTEXTPARAMS, 20, 0)
	NumPut(20, &DRAWTEXTPARAMS, 0, "UInt") ;cbSize
	NumPut(vTabLength, &DRAWTEXTPARAMS, 4, "Int") ;iTabLength
	;NumPut(0, &DRAWTEXTPARAMS, 8, "Int") ;iLeftMargin
	;NumPut(0, &DRAWTEXTPARAMS, 12, "Int") ;iRightMargin
	NumPut(vLen, &DRAWTEXTPARAMS, 16, "UInt") ;uiLengthDrawn

	VarSetCapacity(RECT, 16, 0)
	if !(vLimW = "")
		NumPut(vLimW, &RECT, 8, "Int")
	if !(vLimH = "")
		NumPut(vLimH, &RECT, 12, "Int")
	DllCall("user32\DrawTextEx", Ptr,hDC, Str,vText, Int,vLen, Ptr,&RECT, UInt,vDTFormat, Ptr,&DRAWTEXTPARAMS)
	DllCall("gdi32\SelectObject", Ptr,hDC, Ptr,hFontOld, Ptr)
	DllCall("user32\ReleaseDC", Ptr,0, Ptr,hDC)

	vTextW := NumGet(&RECT, 8, "Int")
	vTextH := NumGet(&RECT, 12, "Int")
}

;==================================================
[EDIT:] Added to 'NOTES' re. 'Close' event.
Last edited by jeeswg on 07 Mar 2018, 05:56, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: slightly-improved dialogs

07 Mar 2018, 02:42

I think this would be better off in the Scripts forum.
Recommends AHK Studio
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: slightly-improved dialogs

27 Mar 2018, 23:49

- @nnnik: Part of the focus of this thread, is re. recreating Progress and SplashImage in AHK v2. It had been stated that they should be reborn as custom functions instead of built-in functions.
- (I don't mind if 0% of my code is used, I just hope to try and identify some of the problems, and some of the solutions, and hopefully speed up the eventual release of the finalised code.)
- The aim is more to discuss some of the programming issues through examples, rather than to make pretty GUIs. Maybe at some future point, the thread can be moved, but, as I see it, right now, it wouldn't be appropriate.
- The project is almost complete, just that I want to double-check the Progress/SplashImage functions, and convert the Find dialog to AHK v2.

- I found some key GUI links, that I thought I might share:

[MSDN: MsgBox]
[details for AHK's MsgBox command]
MessageBox function (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
[MsgBox plus icon, mentioned in: 'MsgBox with custom icon (from resource)']
MessageBoxIndirect function (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
[MsgBox sounds][this overlaps with AHK's SoundPlay function]
MessageBeep function (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx

[MSDN: AdjustWindowRectEx]
[from the source code for Progress/SplashImage]
[calculate window size based on client size and window style/ex. style (and vice versa)]
AdjustWindowRectEx function (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
[apparently AdjustWindowRectEx can do both client size to/from window size based on window style/ex. style]
What is the inverse of AdjustWindowRect and AdjustWindowRectEx? – The Old New Thing
https://blogs.msdn.microsoft.com/oldnew ... 00/?p=2903
[AdjustWindowRectEx example]
measure Menu/ToolTip/GUI dimensions before showing - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=28291

[MSDN: work area]
[from the source code for Progress/SplashImage]
[SPI_GETWORKAREA: get screen size minus (horizontal/vertical) taskbar size]
SystemParametersInfo function (Windows)
https://msdn.microsoft.com/en-us/librar ... s.85).aspx
This functionality is built-in in AutoHotkey as SysGet-MonitorWorkArea, see here:
AutoHotkey via DllCall: AutoHotkey functions as custom functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=37871

[Alguimist]
MagicBox - Message Box Generator - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=20983

[berban]
InputBox() - easy & powerful InputBox implementation - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/7040 ... mentation/

[Coco]
Class: InputBoxEx - Customizable InputBox - Scripts and Functions - AutoHotkey Community
https://autohotkey.com/board/topic/8837 ... -inputbox/
Help with InputBoxEx - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=1352
EntryForm() - easy, custom InputBox, data entry forms - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=4559

[Flipeador]
AHKv2: Diálogos para seleccionar Icono, Fuente, Color y Más! [23/07/17] - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 42&t=10802

[jeeswg]
ToolTips: set border colour (custom font/colour ToolTips) (borders around windows/controls) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=36959
no-frills SplashImage - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=45295
no-frills Progress/SplashText/ToolTip alternative - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=45341
slightly-improved dialogs - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 37&t=45220
Find dialog with whole word and RegEx support - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=50262

[just me]
ToolTipEx - custom fonts and colors in ToolTips - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=4350

[lexikos]
ToolTipFont / ToolTipColor - options for the ToolTip command - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=4777
MsgBox with custom icon (from resource) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=29374

- I'd be interested as to whether the system-wide text for the MsgBox buttons could be changed/retrieved. And also text like 'New Folder' and 'Copy of'. I had done some searching in the past but had not found anything.
- In the AutoHotkey source code, Progress and SplashImage are both defined within one function called Splash() within script2.cpp.
Last edited by jeeswg on 08 Jun 2018, 05:49, edited 5 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: slightly-improved dialogs

26 May 2018, 22:57

- What would be better re. recreating SplashImage: using a Static control (closer to what a user would do) or drawing the image each time the window is activated (closer to the AHK v1 source code)?
- There are examples of both here:
no-frills SplashImage - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=45295
- [EDIT:] For the custom SplashImage function below I've used a Static control.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: slightly-improved dialogs

08 Jun 2018, 06:44

- I now have a full set of examples for slightly-improved dialogs.

1 MsgBox [big font]
2 InputBox (+ InputBox multi) [big font/multiple fields]
- The custom functions demonstrate the functionality that I wanted.
- (In theory I would have recreated MsgBox's Windows 7 appearance exactly, down to the pixels, but the numbers appeared wildly unpredictable.)

3 Progress [i.e. SplashText] [no additional features]
4 SplashImage [option to use IE control]
- The custom functions are virtually identical appearance-wise to the originals, although I would be happy for them to be even closer. The same applies to InputBox.

5 ToolTip [big font]
6 Borders [show coloured borders around a rectangle]
- I'm quite happy with these functions.

- Some general points:
- One issue that emerged was the need for consistency re. '0xABCDEF' v. 'ABCDEF'. The former allows hex or dec to be used, so no conversion is needed, and it allows the use of double quotes to be avoided.
- I experienced a major problem relating to Critical and Thread-Priority. If I set Critical or Thread-Priority, and then called the InputBox function, I was unable to close the InputBox. I don't know if there is a solution to this problem. There is an example of it amongst the examples below.

- Re. Progress/SplashImage:
- I judge Progress(/SplashText) and SplashImage to be sufficiently useful, non-trivial to implement, and long (thus inconvenient for inclusion at the bottom of a script or in the documentation), to be worth keeping as built-in functions. Having them as somehow 'semi-official' but not built-in, suggests that they are official, but that a random barrier has been put in place to burden newbies and spawn support threads in the forums. If they were to be 'kept' in some form, ideally they'd be as easy to use as MsgBox or InputBox.
- I find them to be versatile general purpose workhorse functions, useful for writing and sharing quick scripts, displaying large/custom fonts, and notifying me of events. If they were semi-deprecated, I'd end up with multiple incompatible snippets of code, and spending a disproportionate amount of time writing small GUI scripts for personal use and for sharing. If I felt that I wanted to keep using the P and S functions in some form, but that they weren't standard for sharing on the forum, I could also waste time converting code every time I wanted to share something. If anyone has any instincts for keeping them, they could consider any changes/additions to further increase their attractiveness.

Examples.

Code: Select all

;AHK v2
;slightly-improved dialogs by jeeswg
;written for AutoHotkey v2 alpha (v2.0-a096)

;==================================================

;INTRODUCTION

;slightly-improved dialogs by jeeswg:
;1 MsgBox [big font]
;2 InputBox (+ InputBox multi) [big font/multiple fields]
;3 Progress [i.e. SplashText] [no additional features]
;4 SplashImage [option to use IE control]
;5 ToolTip [big font]
;6 Borders [show coloured borders around a rectangle]

;see also:
;7 Find dialog [custom Find dialog with whole word/RegEx options]
;Find dialog with whole word and RegEx support - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=50262

;other:
;(TrayTip) (unchanged)
;(FileSelect) (unchanged)
;(DirSelect) (unchanged)

;==================================================

;NOTES

;NOTES - GENERAL
;there are examples for:
;MsgBox/InputBox, Progress/SplashImage, ToolTip, Borders
;and finally an InputBox example demonstrating problems relating to
;Critical and Thread-Priority
;note: some MsgBox/ToolTip settings can be set in Control Panel e.g. Control Panel\Appearance and Personalization\Personalization

;NOTES - MSGBOX
;considerations: font name/size
;to do+: doesn't handle modal
;note: specify AX_MsgBoxOpt.CustomButtonCount, as 1/2/3 to use custom button names
;note: MsgBox in 'O' mode cannot distinguish between OK and X (close button)
;queries: exact specifications of system MsgBox (the XYWH window/control values appear to be very very unpredictable)

;NOTES - INPUTBOX
;considerations: font name/size, multiple input fields
;to do+: prevent InputBox getting lost under controls (currently it reactivates window, need logic for a general way to do this)
;to do+: enable timed countdown (5, 4, 3, 2, 1, need the user of SetTimer and a custom function that receives a GUI object)
;queries: do nothing until the GUI is dismissed (an alternative to WinWaitClose?)
;queries: how to get default margin values? (note: MarginX and MarginY start off as -1 until set by the user)
;queries: what is the default font? I tested on Windows 7 and it was 'Segoe UI, size 10'
;queries: window/control styles (are -0xFFFFFFFF -E0xFFFFFFFF always necessary?)
;queries: maybe a general solution re. editing dialogs is to allow a mode where you can edit the control before it's visible (could the default MsgBox/InputBox/ToolTip functions have a mode where they are hidden, so that you can edit them before they appear, possibly by temporarily blocking a show window dll function, perhaps this is more difficult with MsgBox which uses the Winapi's MessageBox function)
;queries: what is the exact size of a button (width/height)? something like text dimensions from DrawTextEx plus some arbitrary margin value perhaps
;queries: perhaps an InputBox minus an Edit control (plus extra button options) could provide an alternative to MsgBox
;note: InputBox button arrangement appears to be: MgnX[gap]OK[double gap]Cancel[gap]MgnX

;NOTES - PROGRESS/SPLASHIMAGE
;considerations: handle more image types (anigif, jxr/wdp, svg)
;to do+: font appearances don't quite match
;to do+: 'T': window is owned
;note: progress bars: if a colour is specified for the progress bar, the theme becomes old style
;note: progress bars: PBM_SETSTATE can change some colours
;note: SplashImage: this custom function uses a Static control, the built-in command uses BitBlt to draw an image
;note: source code location (Progress/SplashImage): 'Splash(' in script2.cpp
;note: additional source code location (SplashImage): 'case WM_ERASEBKGND' in script2.cpp
;note: 'FM'/'FS' refer to font size main/sub text
;note: the use of WS_THICKFRAME forces window of a minimum width

;built-in command functionality:
;options (stand-alone): A, B/B1/B2/M/M1/M2, T, Hide
;options (+ numbers): C, CB/CT/CW, FM/FS/WM/WS, P, X/Y/W/H, ZX/ZY/ZW/ZH
;options (+ range): R

;the use of Off/Show/Hide:
;Off/Show: Arg1 (Progress/SplashImage)
;Hide: Arg1 (Progress), Arg2 (SplashImage)

;possibilities for Arg1:
;Arg1: Off
;Arg1: Show
;Arg1: 3:Off [refer to nth window e.g. 3rd window][10 windows in total]
;[SplashImage (2 params): ImageFile, Options]
;Arg1: C:\MyDir\MyFile.png [bmp/(static) gif/jpg(/png/tif/ico)]
;Arg1: MyFile.png [in A_WorkingDir]
;Arg1: (blank) [image unchanged, change text]
;Arg1: HBITMAP:
;Arg1: HICON:
;[Progress (1 param): ProgressParam1 combines Arg1/Options]
;Arg1: bar position

;custom changes:
;supports 'A0' for always-on-top off
;supports 'B0' for borderless
;supports 'HFONT:' for FontName parameter
;supports 'Hide' for SplashImage's first parameter
;supports 'IE' for Internet Explorer_Server control (for more image types)
;supports 'IE###' for Internet Explorer_Server control (for more image types) (and zoom %)
;note: for 'IE': image dimensions and zoom must be specified explicitly, and the image will maintain its proportions

;NOTES - TOOLTIP
;considerations: font name/size, colours
;not using SetWindowTheme, gives an appearance like the ToolTip command: faded colours and rounded corners
;to do+: AHK has special handling when the cursor is near the edge of the screen
;queries: the built-in ToolTip at (X,Y) doesn't always work as expected (at least in Window/Client modes) (the 'OLD' ToolTips should overlap the 'XXX' ToolTips)
;queries: make it exactly central (e.g. by using precise title bar/border dimensions, versus something like creating the window hidden initially and retrieving the size)
;queries: what size margins does the ToolTip command use (otherwise what are the defaults)
;note: source code location (ToolTip): 'ToolTip(' in script2.cpp

;NOTES - BORDERS
;(none)

;NOTES - GENERAL LIMITATIONS IN BUILT-IN FUNCTIONALITY
;- cannot specify no icon
;- cannot specify class
;- problems re. Critical and Thread-Priority in custom GUI functions that built-in functions don't have
;- prefer '0xABCDEF' (or dec) to 'ABCDEF' (consistency, and the avoidance of double quotes)

;==================================================

;SETTINGS

;for Progress/SplashImage:
;include a valid path to an AHK v1 exe
;otherwise certain AHK v1 examples are skipped
vGblPathAhk1 := A_Desktop "\AutoHotkey_1.1.28.02\AutoHotkeyU32.exe"

;for SplashImage:
;include a valid path to an animated gif
;otherwise certain anigif examples are skipped
;e.g. animated gif
;https://autohotkey.com/boards/download/file.php?avatar=198_1381214069.gif
vPathAniGif := A_ScriptDir "\198 [hoppfrosch].gif"

;==================================================

#SingleInstance force
#Persistent
vListSub := "MIPSTBX" ;GUI subs to execute
;vListSub := "X"

vDoInputBoxCriticalTest := 1
vDoInputBoxThreadPriorityTest := 1

if InStr(vListSub, "M")
	Gosub SubMsgBox
if InStr(vListSub, "I")
	Gosub SubInputBox
if InStr(vListSub, "P")
	Gosub SubProgress
if InStr(vListSub, "S")
	Gosub SubSplashImage
if InStr(vListSub, "T")
	Gosub SubToolTip
if InStr(vListSub, "B")
	Gosub SubBorders
if InStr(vListSub, "X")
	Gosub SubCriticalThreadPriority
return

;==================================================

SubMsgBox:
global AX_DlgTitle
global AX_ScriptNameNoExt
global AX_MsgBoxResult
global AX_MsgBoxOpt := {}
AX_MsgBoxOpt.Prompt0 := "Press OK to continue."
AX_MsgBoxOpt.Prompt1 := ""
AX_MsgBoxOpt.TextAbort := "Abort"
AX_MsgBoxOpt.TextCancel := "Cancel"
AX_MsgBoxOpt.TextContinue := "Continue"
AX_MsgBoxOpt.TextIgnore := "Ignore"
AX_MsgBoxOpt.TextNo := "No"
AX_MsgBoxOpt.TextOK := "OK"
AX_MsgBoxOpt.TextRetry := "Retry"
AX_MsgBoxOpt.TextTryAgain := "Try Again"
AX_MsgBoxOpt.TextYes := "Yes"
AX_MsgBoxOpt.TextButton1 := "Btn1"
AX_MsgBoxOpt.TextButton2 := "Btn2"
AX_MsgBoxOpt.TextButton3 := "Btn3"
AX_MsgBoxOpt.CustomButtonCount := ""
AX_MsgBoxOpt.FontName := "Segoe UI"
AX_MsgBoxOpt.FontSize := 18
AX_MsgBoxOpt.FontWeight := 400
;AX_MsgBoxOpt.HFont := hFont
AX_MsgBoxOpt.StylesWin := "+0x94C803C5 +E0x00010101"
AX_MsgBoxOpt.StylesButton1 := "+0x50030001 +E0x00000004"
AX_MsgBoxOpt.StylesButton2 := "+0x50010000 +E0x00000004"
AX_MsgBoxOpt.StylesButton3 := "+0x50010000 +E0x00000004"
AX_MsgBoxOpt.StylesStatic := "+0x50022080 +E0x00000004"
AX_MsgBoxOpt.StylesIcon := "+0x50020003 +E0x00000004"
AX_MsgBoxOpt.WinMaxWidth := 800
AX_MsgBoxOpt.HIconDefault := ""
AX_MsgBoxOpt.HIconAsterisk := ""
AX_MsgBoxOpt.HIconExclamation := ""
AX_MsgBoxOpt.HIconHand := ""
AX_MsgBoxOpt.HIconQuestion := ""
AX_MsgBoxOpt.SoundDefault := "-"
AX_MsgBoxOpt.SoundAsterisk := ""
AX_MsgBoxOpt.SoundExclamation := ""
AX_MsgBoxOpt.SoundHand := ""
AX_MsgBoxOpt.SoundQuestion := ""

SplitPath(A_ScriptFullPath,,,, AX_ScriptNameNoExt)
AX_DlgTitle := AX_ScriptNameNoExt

vDoButtons := 1
vDoIcons := 1

;test buttons: 7 types: 0 to 6
vList := "O,OC,ARI,YNC,YN,RC,CTC"
if vDoButtons
	Loop Parse, vList, ","
	{
		MsgBoxNew(A_LoopField,, A_LoopField)
		MsgBox(A_LoopField,, A_LoopField)
	}
;note: ARI/YN - Close blocked
;note: result if press Esc (or alt+space, c): OK/Cancel/-/Cancel/-/Cancel/Cancel

;test icons: 4 types
vList := "x?!i"
if vDoIcons
	Loop Parse, vList
	{
		MsgBoxNew("text", "title", "Icon" A_LoopField)
		MsgBox("text", "title", "Icon" A_LoopField)
	}

;test timeout
vRet := MsgBoxNew("test timeout", "title", "T2000")
MsgBoxNew("result: " vRet)

;test return value
vRet := MsgBoxNew("the next MsgBox will state:`r`n" "return value/ErrorLevel/return value", "title", "YNC")
MsgBoxNew(vRet "`r`n" ErrorLevel "`r`n" AX_MsgBoxResult)

;text short/long strings
;test blanks
MsgBoxNew("the next MsgBoxes will display short/long strings")
MsgBoxNew()
MsgBox()
MsgBoxNew(,, "")
MsgBox(,, "")

vText := "abcdefghijklmnopqrstuvwxyz"
vText2 := ""
Loop 100
	vText2 .= "a`n"
oArray := []
oArray.Push("")
oArray.Push("text")
oArray.Push("text`ntext`ntext")
oArray.Push("text`ntext`ntext`ntext`ntext")
oArray.Push(vText vText vText vText vText)
oArray.Push(vText vText vText vText vText vText vText vText vText vText)
oArray.Push(vText)
oArray.Push(vText "`n" vText "`n" vText)
oArray.Push(vText "`n" vText "`n" vText "`n" vText "`n" vText)
oArray.Push(vText2)
for vKey, vValue in oArray
{
	MsgBoxNew(vValue, "title")
	MsgBox(vValue, "title")
}

MsgBoxNew("the next MsgBoxes will display text with/without icons")
MsgBox(vText vText vText vText vText)
MsgBox(vText vText vText vText vText,, "Icon?")
MsgBoxNew(vText vText vText vText vText)
MsgBoxNew(vText vText vText vText vText,, "Icon?")
MsgBox(vText "`n" vText "`n" vText "`n" vText "`n" vText)
MsgBox(vText "`n" vText "`n" vText "`n" vText "`n" vText,, "Icon?")
MsgBoxNew(vText "`n" vText "`n" vText "`n" vText "`n" vText)
MsgBoxNew(vText "`n" vText "`n" vText "`n" vText "`n" vText,, "Icon?")

;test custom buttons
MsgBoxNew("the next MsgBoxes will display custom buttons")
AX_MsgBoxOpt.CustomButtonCount := 1
MsgBoxNew("custom buttons: 1")
AX_MsgBoxOpt.CustomButtonCount := 2
MsgBoxNew("custom buttons: 2")
AX_MsgBoxOpt.CustomButtonCount := 3
vRet := MsgBoxNew("custom buttons: 3")
MsgBoxNew("return value: " vRet)
AX_MsgBoxOpt.CustomButtonCount := ""
MsgBoxNew("custom buttons: off")

;test custom icon and sound
AX_MsgBoxOpt.HIconDefault := LoadPicture(A_AhkPath, "w32 h32", vType)
AX_MsgBoxOpt.SoundDefault := "C:\Windows\Media\tada.wav"
MsgBoxNew("custom icon and sound")
AX_MsgBoxOpt.HIconDefault := ""
AX_MsgBoxOpt.SoundDefault := "-"
return

;==================================================

SubInputBox:
global AX_DlgTitle
global AX_ScriptNameNoExt
global AX_InputBoxResult
global AX_InputBoxOpt := {}
AX_InputBoxOpt.Prompt0 := ""
AX_InputBoxOpt.Prompt1 := ""
AX_InputBoxOpt.TextButton1 := "OK"
AX_InputBoxOpt.TextButton2 := "Cancel"
AX_InputBoxOpt.FontName := "Segoe UI"
AX_InputBoxOpt.FontSize := 18
AX_InputBoxOpt.FontWeight := 400
;AX_InputBoxOpt.HFont := hFont
AX_InputBoxOpt.StylesWin := "+0x94CC0A4C +E0x00010100"
AX_InputBoxOpt.StylesEdit := "+0x50010080 +E0x00000204"
AX_InputBoxOpt.StylesEditPW := "+0x500100A0 +E0x00000204"
AX_InputBoxOpt.StylesButton1 := "+0x50010001 +E0x00000004"
AX_InputBoxOpt.StylesButton2 := "+0x50010000 +E0x00000004"
AX_InputBoxOpt.StylesStatic := "+0x50020000 +E0x00000004"

SplitPath(A_ScriptFullPath,,,, AX_ScriptNameNoExt)
AX_DlgTitle := AX_ScriptNameNoExt

;InputBoxNew("text", "title", "T1000", "default")
vRet := InputBoxNew("text", "title",, "default")
MsgBox(vRet)
;MsgBox(ErrorLevel "`r`n" AX_InputBoxResult)

;InputBoxMulti(["ITEM 1`naaa","ITEM 2`nbbb","ITEM 3`nccc"], vWinTitle:="", vOpt:="", ["AAA","BBB","CCC"])
oArray := InputBoxMulti(["aaa","bbb","ccc"], vWinTitle:="", vOpt:="", ["AAA","BBB","CCC"])
if IsObject(oArray)
	MsgBox(oArray.1 "`r`n" oArray.2 "`r`n" oArray.3 "`r`n" oArray.4)
else
	MsgBox("")
;MsgBox(ErrorLevel "`r`n" AX_InputBoxResult)

InputBox("text", "title",, "default")
InputBoxNew("text", "title",, "default")
InputBox("text", "title", "Password+", "default")
InputBoxNew("text", "title", "Password+", "default")
InputBox("text",,, "default")
InputBoxNew("text",,, "default")
return

SubCriticalThreadPriority:
;copy of InputBox settings code from earlier
;===============
global AX_DlgTitle
global AX_ScriptNameNoExt
global AX_InputBoxResult
global AX_InputBoxOpt := {}
AX_InputBoxOpt.Prompt0 := ""
AX_InputBoxOpt.Prompt1 := ""
AX_InputBoxOpt.TextButton1 := "OK"
AX_InputBoxOpt.TextButton2 := "Cancel"
AX_InputBoxOpt.FontName := "Segoe UI"
AX_InputBoxOpt.FontSize := 18
AX_InputBoxOpt.FontWeight := 400
;AX_InputBoxOpt.HFont := hFont
AX_InputBoxOpt.StylesWin := "+0x94CC0A4C +E0x00010100"
AX_InputBoxOpt.StylesEdit := "+0x50010080 +E0x00000204"
AX_InputBoxOpt.StylesEditPW := "+0x500100A0 +E0x00000204"
AX_InputBoxOpt.StylesButton1 := "+0x50010001 +E0x00000004"
AX_InputBoxOpt.StylesButton2 := "+0x50010000 +E0x00000004"
AX_InputBoxOpt.StylesStatic := "+0x50020000 +E0x00000004"

SplitPath(A_ScriptFullPath,,,, AX_ScriptNameNoExt)
AX_DlgTitle := AX_ScriptNameNoExt
;===============

;can't be used with Critical or Thread-Priority,
;which is an argument for good built-in functions
MsgBox("the next and final custom GUI will demonstrate an issue with custom GUIs and Critical/Thread-Priority, i.e. it can't be closed")
if 1
{
	if vDoInputBoxCriticalTest
		Critical()
	if vDoInputBoxThreadPriorityTest
		Thread("Priority", 1)
	vRet := InputBoxNew("text", "title",, "default")
	MsgBox(vRet)
}
return

;==================================================

SubProgress:
;vGblPathAhk1 := A_Desktop "\AutoHotkey_1.1.28.02\AutoHotkeyU32.exe"
vSleep := 3000
vGblSleep := vSleep
;note: the 'vGbl' variables are for use with the diagnostic ProgressAhk1 function

global AX_DlgTitle
global AX_ScriptNameNoExt
SplitPath(A_ScriptFullPath,,,, AX_ScriptNameNoExt)
AX_DlgTitle := AX_ScriptNameNoExt

;vTemp := "hello"
vTemp := "abcdefghijklmnopqrstuvwxyz"
vText := vTemp "`n" vTemp "`n" vTemp "`n" vTemp "`n" vTemp

vFormat := "zh0 b c0 fs18" ;no border + left
ProgressAhk1(vFormat, vText)
ProgressNew(vFormat, vText), Sleep(vSleep), ProgressNew("Off")
vFormat := "zh0 b1 fs18" ;border + centre
ProgressAhk1(vFormat, vText)
ProgressNew(vFormat, vText), Sleep(vSleep), ProgressNew("Off")
vFormat := "b2"
ProgressAhk1(vFormat, vText)
ProgressNew(vFormat, vText), Sleep(vSleep), ProgressNew("Off")
return

if 1
{
	ProgressAhk1("", vText)
	ProgressNew("", vText), Sleep(vSleep), ProgressNew("Off")
	ProgressAhk1("fs4", vText)
	ProgressNew("fs4", vText), Sleep(vSleep), ProgressNew("Off")
	ProgressAhk1("fs24", vText)
	ProgressNew("fs24", vText), Sleep(vSleep), ProgressNew("Off")
}

ProgressAhk1("P30", "SubText", "MainText", "WinTitle")
ProgressNew("P30", "SubText", "MainText", "WinTitle"), Sleep(vSleep), ProgressNew("Off")

ProgressAhk1("P30 CWFF0000 CBFFFF00 CT0000FF", "SubText", "MainText", "WinTitle")
ProgressNew("P30 CW0xFF0000 CB0xFFFF00 CT0x0000FF", "SubText", "MainText", "WinTitle"), Sleep(vSleep), ProgressNew("Off")

ProgressAhk1("P7500 R0-10000 CWFF0000 CBFFFF00 CT0000FF", "SubText", "MainText", "WinTitle")
ProgressNew("P7500 R0-10000 CW0xFF0000 CB0xFFFF00 CT0x0000FF", "SubText", "MainText", "WinTitle"), Sleep(vSleep)
ProgressNew(10000), Sleep(vSleep), ProgressNew("Off")
return

;==================================================

SubSplashImage:
;vGblPathAhk1 := A_Desktop "\AutoHotkey_1.1.28.02\AutoHotkeyU32.exe"
vSleep := 1500
vGblSleep := vSleep
;e.g. animated gif
;https://autohotkey.com/boards/download/file.php?avatar=198_1381214069.gif
;vPathAniGif := A_ScriptDir "\198 [hoppfrosch].gif"
;note: the 'vGbl' variables are for use with the diagnostic SplashImageAhk1 function

global AX_DlgTitle
global AX_ScriptNameNoExt
SplitPath(A_ScriptFullPath,,,, AX_ScriptNameNoExt)
AX_DlgTitle := AX_ScriptNameNoExt
vPath := A_AhkPath

if 1
{
	SplashImageAhk1(vPath)
	SplashImageNew(vPath), Sleep(vSleep), SplashImageNew("Off")

	SplashImageAhk1(vPath, "M2 X0 Y0", "SubText", "MainText", "WinTitle")
	SplashImageNew(vPath, "M2 X0 Y0", "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageAhk1(vPath, "M1 X200 Y200", "SubText", "MainText", "WinTitle")
	SplashImageNew(vPath, "M1 X200 Y200", "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageAhk1(vPath, "M X400 Y400", "SubText", "MainText", "WinTitle")
	SplashImageNew(vPath, "M X400 Y400", "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageAhk1(vPath, "B X600 Y600", "SubText", "MainText", "WinTitle")
	SplashImageNew(vPath, "B X600 Y600", "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")

	SplashImageNew("1:" vPath, "M2 X0 Y0", "SubText", "MainText", "WinTitle")
	SplashImageNew("2:" vPath, "M1 X200 Y200", "SubText", "MainText", "WinTitle")
	SplashImageNew("3:" vPath, "M X400 Y400", "SubText", "MainText", "WinTitle")
	SplashImageNew("4:" vPath, "B X600 Y600", "SubText", "MainText", "WinTitle")
	Sleep(vSleep)
	SplashImageNew("1:Off")
	SplashImageNew("3:Off")
	Sleep(vSleep)
	SplashImageNew("2:Off")
	SplashImageNew("4:Off")
}

if 1
{
	hIcon := LoadPicture(A_AhkPath,, vImgType)
	;compare load hIcon v. load path
	;SplashImageAhk1("HICON:" hIcon,, "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageNew("HICON:" hIcon,, "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageAhk1(A_AhkPath,, "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageNew(A_AhkPath,, "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")

	hIcon := LoadPicture("shell32.dll", "icon35", vImgType)
	;SplashImageAhk1("HICON:" hIcon,, "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageNew("HICON:" hIcon,, "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
}

vPath := vPathAniGif
if FileExist(vPathAniGif)
if 1
{
	vImgW := 80, vImgH := 63
	vImgW += 20, vImgH += 30 ;add margins
	vImgPos := Format("ZW{} ZH{}", vImgW, vImgH)
	SplashImageNew(vPath, "IE " vImgPos, "SubText", "MainText", "WinTitle"), Sleep(vSleep*1.6), SplashImageNew("Off")
	vImgW *= 3, vImgH *= 3 ;zoom
	vImgPos := Format("ZW{} ZH{}", vImgW, vImgH)
	SplashImageNew(vPath, "IE300 " vImgPos, "SubText", "MainText", "WinTitle"), Sleep(vSleep*1.6), SplashImageNew("Off")

	SplashImageNew(vPath,, "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageNew(vPath, "ZW300", "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageNew(vPath, "ZH300", "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageNew(vPath, "ZW600 ZH300", "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageNew(vPath, "ZW300 ZH300", "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	SplashImageNew(vPath, "ZW300 ZH-1", "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
}

if 1
{
	vPath := A_AhkPath
	SplashImageNew(vPath,, "SubText", "MainText", "WinTitle")
	Sleep(vSleep)
	SplashImageNew(, "Hide")
	Sleep(vSleep)
	SplashImageNew("Show")
	Sleep(vSleep)
	SplashImageNew("Off")
	Sleep(vSleep)

	hBitmap := LoadPicture(A_AhkPath, "W256 H256")
	SplashImageNew("HBITMAP:" hBitmap,, "", "", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	hIcon := LoadPicture(A_AhkPath, "W256 H256", vImgType)
	SplashImageNew("HICON:" hIcon,, "", "", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")

	hBitmap := LoadPicture(A_AhkPath, "W256 H256")
	SplashImageNew("HBITMAP:" hBitmap,, "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
	hIcon := LoadPicture(A_AhkPath, "W256 H256", vImgType)
	SplashImageNew("HICON:" hIcon,, "SubText", "MainText", "WinTitle"), Sleep(vSleep), SplashImageNew("Off")
}
return

;==================================================

SubToolTip:
;test standard ToolTips
if 0
{
	CoordMode("ToolTip", "Screen")
	ToolTip("Client ___ Window ___ SCREEN",,, 1)
	CoordMode("ToolTip", "Window")
	ToolTip("Client ___ WINDOW",,, 2)
	CoordMode("ToolTip", "Client")
	ToolTip("CLIENT",,, 3)
	Sleep(3000)

	CoordMode("ToolTip", "Screen")
	ToolTip("SCREEN", 300, 300, 1)
	CoordMode("ToolTip", "Window")
	ToolTip("WINDOW", 300, 300, 2)
	CoordMode("ToolTip", "Client")
	ToolTip("CLIENT", 300, 300, 3)
	Sleep(3000)

	ToolTip(,,, 1)
	ToolTip(,,, 2)
	ToolTip(,,, 3)
}

CoordMode("ToolTip", "Screen")
CoordMode("ToolTip", "Window")
CoordMode("ToolTip", "Client")
ToolTip("XXX 300 300", 300, 300, 4)
ToolTip("XXX",,, 5)

global AX_ToolTipOpt := []
AX_ToolTipOpt.FontName := "Segoe UI"
AX_ToolTipOpt.FontSize := 9
AX_ToolTipOpt.FontWeight := 400
;AX_ToolTipOpt.ColTxRGB := 0x404040
;AX_ToolTipOpt.ColBkRGB := 0xF0F0F0
AX_ToolTipOpt.ColTxRGB := 0x000000
AX_ToolTipOpt.ColBkRGB := 0xFFFFFF
AX_ToolTipOpt.SetWindowTheme := "" ;like AutoHotkey ToolTip command (faded colours and rounded corners)
AX_ToolTipOpt.SetWindowTheme := 0

AX_ToolTipOpt.FontSize := 18

;vText := "   hello world   `r`nhello world`r`nhello world"
vText := "hello world`r`nhello world`r`nhello world"

vDelay := 1500

ToolTipNew(vText)
Sleep(vDelay)
ToolTipNew()

ToolTipNew(vText, "c", "c")
Sleep(vDelay)
ToolTipNew()

ToolTipNew(vText, "c", "c",, 0x0000FF, 0xFFFF00)
Sleep(vDelay)
ToolTipNew()

ToolTipNew(vText, "c", "c",, 0xFFFF00, 0x0000FF)
Sleep(vDelay)
ToolTipNew()

ToolTipNew(vText, 100, 100, 1)
ToolTipNew(vText, 200, 200, 2)
ToolTipNew(vText, 300, 300, 3)
Sleep(vDelay)
;Sleep(vDelay * 3)
ToolTipNew(,,, 1)
ToolTipNew(,,, 2)
ToolTipNew(,,, 3)

AX_ToolTipOpt.FontSize := 9
ToolTipNew(vText, "c", "c")
ToolTip(vText)
Sleep(vDelay)
ToolTipNew()
ToolTip()

;confirm they overlap at (X,Y)
ToolTipNew("NEW" A_Space A_Space A_CoordModeToolTip)
ToolTip("OLD")
Sleep(vDelay)
ToolTipNew()
ToolTip()

;confirm they overlap when (X,Y) not specified
ToolTipNew("NEW" A_Space A_Space A_CoordModeToolTip, 300, 300)
ToolTip("OLD", 300, 300)
Sleep(vDelay)
ToolTipNew()
ToolTip()

ToolTip(,,, 4)
ToolTip(,,, 5)
return

;w:: ;ToolTip - get margins
;ToolTip via AutoHotkey ToolTip command reports 0,0,0,0
VarSetCapacity(RECT, 16, 0)
SendMessage(0x41B,, &RECT,, "ahk_class tooltips_class32") ;TTM_GETMARGIN := 0x41B
vOutput := NumGet(&RECT, 0, "UInt")
vOutput .= "," NumGet(&RECT, 4, "UInt")
vOutput .= "," NumGet(&RECT, 8, "UInt")
vOutput .= "," NumGet(&RECT, 12, "UInt")
MsgBox(vOutput)
return

;==================================================

SubBorders:
;draw borders around the active window/control
hWnd := WinGetID("A")
vCtlClassNN := ControlGetFocus("ahk_id " hWnd)
hWnd := ControlGetHwnd(vCtlClassNN, "ahk_id " hWnd)
WinGetPos(vCtlX, vCtlY, vCtlW, vCtlH, "ahk_id " hWnd)
Borders(vCtlX, vCtlY, vCtlW, vCtlH)
return

;==================================================
Functions.

Code: Select all

;==================================================

;;FUNCTIONS - MSGBOX
;MsgBoxNew(oParams*) ;vText, vWinTitle, vOpt
;MsgBoxNew_Close(oGui, vBtn)
;;FUNCTIONS - INPUTBOX
;InputBoxNew(oParams*) ;vText, vWinTitle, vOpt, vDefault
;InputBoxNew_Close(oGui, vBtn)
;InputBoxNew_Size(oGui)
;;FUNCTIONS - INPUTBOX MULTI
;InputBoxMulti(oParams*) ;oText, vWinTitle, vOpt, oDefault
;;FUNCTIONS - PROGRESS/SPLASHIMAGE (AHK V1 DIAGNOSTIC)
;Progress(ProgressParam1, SubText:="", MainText:="", WinTitle:="", FontName:="")
;SplashImage(ImageFile:="", Options:="", SubText:="", MainText:="", WinTitle:="", FontName:="")
;ProgressAhk1(oParams*)
;SplashImageAhk1(oParams*)
;SplashAhk1(vFunc, oParams*)
;;FUNCTIONS - PROGRESS/SPLASHIMAGE (AHK V2)
;ProgressNew(vParam1, vSubText:="", vMainText:="", vWinTitle:=" ", vFontName:="")
;SplashImageNew(vImageFile:="", vOpt:="", vSubText:="", vMainText:="", vWinTitle:=" ", vFontName:="")
;;FUNCTIONS - TOOLTIP
;ToolTipNew(vText:="", vPosX:="", vPosY:="", vWhichToolTip:=1, vColTxRGB:="", vColBkRGB:="")
;;FUNCTIONS - BORDERS
;Borders(vPosX, vPosY, vPosW:=0, vPosH:=0, vTime:=1000, vColRGB:=0xFFFF00, vBdrW:=5, vBdrH:=5)
;Borders_WndProc(hWnd, uMsg, wParam, lParam)

;;FUNCTIONS - AUXILIARY
;JEE_HIconGetDims(hIcon, ByRef vImgW, ByRef vImgH)
;JEE_HBitmapGetDims(hBitmap, ByRef vImgW, ByRef vImgH)
;JEE_FontCreate(vName, vSize, vFontStyle:="", vWeight:="")
;JEE_StrGetDim(vText, hFont, ByRef vTextW, ByRef vTextH, vDTFormat:=0x400, vLimW:="", vLimH:="")

;==================================================

;FUNCTIONS - MSGBOX

MsgBoxNew(oParams*) ;vText, vWinTitle, vOpt
{
	static oType := {"OK":0, "O":0, "OKCancel":1, "O/C":1, "OC":1, "AbortRetryIgnore":2, "A/R/I":2, "ARI":2
	, "YesNoCancel":3, "Y/N/C":3, "YNC":3, "YesNo":4, "Y/N":4, "YN":4, "RetryCancel":5, "R/C":5, "RC":5
	, "CancelTryAgainContinue":6, "C/T/C":6, "CTC":6, "Iconx":16, "Icon?":32, "Icon!":48, "Iconi":64
	, "Default2":256, "Default3":512, "Default4":768}
	static hIconHand := DllCall("user32\LoadIcon", Ptr,0, Ptr,32513, Ptr) ;IDI_HAND := 32513
	static hIconQuestion := DllCall("user32\LoadIcon", Ptr,0, Ptr,32514, Ptr) ;IDI_QUESTION := 32514
	static hIconExclamation := DllCall("user32\LoadIcon", Ptr,0, Ptr,32515, Ptr) ;IDI_EXCLAMATION := 32515
	static hIconAsterisk := DllCall("user32\LoadIcon", Ptr,0, Ptr,32516, Ptr) ;IDI_ASTERISK := 32516
	global oMsgBoxRet

	vText := !oParams.Length() ? AX_MsgBoxOpt.Prompt0 : oParams.HasKey(1) ? oParams.1 : AX_MsgBoxOpt.Prompt1
	vWinTitle := oParams.HasKey(2) ? oParams.2 : AX_DlgTitle
	vOpt := oParams.3

	if !IsObject(oMsgBoxRet)
		oMsgBoxRet := []
	hWnd := WinExist("A")

	vDHW := A_DetectHiddenWindows
	DetectHiddenWindows(1)
	vPosWin := "", vTimeout := "", vType := 0, vHasIcon := 0, vHasIconCustom := 0
	vMgnX := 5, vMgnY := 5
	vWinW := 368, vWinH := 166

	Loop Parse, vOpt, " `t"
	{
		if (SubStr(A_LoopField, 1, 1) = "W")
			vWinW := SubStr(A_LoopField, 2)
		else if (SubStr(A_LoopField, 1, 1) = "H")
			vWinH := SubStr(A_LoopField, 2)

		if (A_LoopField ~= "i)^[XYWH]\d")
			vPosWin .= A_LoopField " "
		else if (SubStr(A_LoopField, 1, 1) = "T")
			vTimeout := SubStr(A_LoopField, 2)
		else if ((A_LoopField is "number") && (vTemp := Abs(A_LoopField))
		|| (vTemp := oType[A_LoopField]))
			vType |= vTemp
	}

	vStyleReset := "-0xFFFFFFFF -E0xFFFFFFFF "

	vBtnClose := 0
	if (AX_MsgBoxOpt.CustomButtonCount = 1)
		vBtnList := "Button1"
	else if (AX_MsgBoxOpt.CustomButtonCount = 2)
		vBtnList := "Button1,Button2"
	else if (AX_MsgBoxOpt.CustomButtonCount = 3)
		vBtnList := "Button1,Button2,Button3"
	else if (vType & 0xF = 0) ;O
		vBtnList := "OK", vBtnClose := 1
	else if (vType & 0xF = 1) ;OC
		vBtnList := "OK,Cancel", vBtnClose := 2
	else if (vType & 0xF = 2) ;ARI
		vBtnList := "Abort,Retry,Ignore", vDisableClose := 1
	else if (vType & 0xF = 3) ;YNC
		vBtnList := "Yes,No,Cancel", vBtnClose := 3
	else if (vType & 0xF = 4) ;YN
		vBtnList := "Yes,No", vDisableClose := 1
	else if (vType & 0xF = 5) ;RC
		vBtnList := "Retry,Cancel", vBtnClose := 2
	else if (vType & 0xF = 6) ;CTC
		vBtnList := "Cancel,TryAgain,Continue", vBtnClose := 1
	StrReplace(vBtnList, ",",, vBtnCount), vBtnCount += 1
	oTemp := StrSplit(vBtnList, ",")
	vTextBtn0 := "Cancel"
	Loop vBtnCount
		vTextBtn%A_Index% := AX_MsgBoxOpt["Text" oTemp[A_Index]]

	if (vType & 0xF0 = 0x10) ;Iconx
		vTypeIcon := "Hand", vHasIcon := 1
	else if (vType & 0xF0 = 0x20) ;Icon?
		vTypeIcon := "Question", vHasIcon := 1
	else if (vType & 0xF0 = 0x30) ;Icon!
		vTypeIcon := "Exclamation", vHasIcon := 1
	if (vType & 0xF0 = 0x40) ;Iconi
		vTypeIcon := "Asterisk", vHasIcon := 1

	if vHasIcon
	&& !(hIcon := AX_MsgBoxOpt["HIcon" vTypeIcon])
		hIcon := hIcon%vTypeIcon%
	if !vHasIcon
	&& (hIcon := AX_MsgBoxOpt.HIconDefault)
		vHasIconCustom := 1

	vFontName := AX_MsgBoxOpt.FontName
	vFontSize := AX_MsgBoxOpt.FontSize
	vFontWeight := AX_MsgBoxOpt.FontWeight
	vStylesWin := vStyleReset AX_MsgBoxOpt.StylesWin
	vStylesButton1 := AX_MsgBoxOpt.StylesButton1
	vStylesButton2 := AX_MsgBoxOpt.StylesButton2
	vStylesButton3 := AX_MsgBoxOpt.StylesButton3
	vStylesStatic := AX_MsgBoxOpt.StylesStatic
	vStylesIcon := AX_MsgBoxOpt.StylesIcon

	vFontOpt := "s" vFontSize
	if AX_MsgBoxOpt.HFont
		hFont := AX_MsgBoxOpt.HFont
	else
		hFont := JEE_FontCreate(vFontName, vFontSize, "", vFontWeight)
	JEE_StrGetDim("Try Again", hFont, vBtn0W, vBtn0H, vDTFormat:=0x400, vLimW:="", vLimH:="") ;minimum width
	JEE_StrGetDim(vTextBtn1, hFont, vBtn1W, vBtn1H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	JEE_StrGetDim(vTextBtn2, hFont, vBtn2W, vBtn2H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	JEE_StrGetDim(vTextBtn3, hFont, vBtn3W, vBtn3H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	vBtnW := Max(vBtn0W, vBtn1W, vBtn2W, vBtn3W) + 16
	vBtnH := Max(vBtn0H, vBtn1H, vBtn2H, vBtn3H) + 7

	if !InStr(vPosWin, "W") || !InStr(vPosWin, "H")
	{
		if AX_MsgBoxOpt.WinMaxWidth
			vLimW := AX_MsgBoxOpt.WinMaxWidth - 2*vMgnX
		else
			vLimW := A_ScreenWidth - 2*vMgnX
		JEE_StrGetDim(vText, hFont, vTextW, vTextH, vDTFormat:=0x2410, vLimW, vLimH:="")
		vWinW := vTextW + 2*vMgnX
		vWinH := vTextH + vBtnH + 3*vMgnY
		vWinW := Max(vWinW, 368)
		vWinH := Max(vWinH, 166)
	}

	if vHasIcon || vHasIconCustom
	{
		vWinW += vMgnX + 32
		vOffsetX := vMgnX + 32
	}
	else
		vOffsetX := 0
	if !InStr(vPosWin, "W")
		vPosWin .= " W" vWinW
	if !InStr(vPosWin, "H")
		vPosWin .= " H" vWinH

	vEditW := vWinW - 2*vMgnX - vOffsetX
	vGap := Round((vWinW - vBtnCount*vBtnW - 2*vMgnX)/(vBtnCount*2))
	;[][BTN1][]
	;[][BTN1][][][BTN2][]
	;[][BTN1][][][BTN2][][][BTN3][]
	;centred buttons:
	;vBtn1X := vMgnX + vGap
	;vBtn2X := vMgnX + 3*vGap + vBtnW
	;vBtn3X := vMgnX + 5*vGap + 2*vBtnW
	;offset from right
	vBtn1X := vWinW - vBtnCount*vMgnX - vBtnCount*vBtnW
	vBtn2X := vWinW - (vBtnCount-1)*vMgnX - (vBtnCount-1)*vBtnW
	vBtn3X := vWinW - (vBtnCount-2)*vMgnX - (vBtnCount-2)*vBtnW
	vBtnY := vWinH - vMgnY - vBtnH
	;vEditY := vBtnY - vMgnY - vBtnH
	;vStcH := vEditY - vMgnY
	vStcH := vBtnY - vMgnY
	;vPosEdit := Format("X{} Y{} W{} H{}", vMgnX, vEditY, vEditW, vBtnH)
	vPosBtn1 := Format("X{} Y{} W{} H{}", vBtn1X, vBtnY, vBtnW, vBtnH)
	vPosBtn2 := Format("X{} Y{} W{} H{}", vBtn2X, vBtnY, vBtnW, vBtnH)
	vPosBtn3 := Format("X{} Y{} W{} H{}", vBtn3X, vBtnY, vBtnW, vBtnH)
	vPosStc := Format("X{} Y{} W{} H{}", vMgnX+vOffsetX, vMgnY, vEditW, vStcH)
	vPosStcIcon := Format("X{} Y{} W{} H{}", vMgnX, vMgnY, 32, 32)

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	;WS_POPUPWINDOW := 0x80880000 ;composite style (WS_POPUP | WS_BORDER | WS_SYSMENU)
	;WS_VISIBLE := 0x10000000
	;WS_CLIPSIBLINGS := 0x4000000
	;WS_CAPTION := 0xC00000 ;composite style (WS_BORDER | WS_DLGFRAME)
	;WS_THICKFRAME := 0x40000
	;WS_EX_CONTROLPARENT := 0x10000
	;WS_EX_WINDOWEDGE := 0x100
	oGui := GuiCreate(vStylesWin, vWinTitle)
	oGui.SetFont(vFontOpt, vFontName)
	;types: O,OC,ARI,YNC,YN,RC,CTC
	;press Esc or alt+space, c: OK/Cancel/-/Cancel/-/Cancel/Cancel
	oGui.OnEvent("Close", Func("MsgBoxNew_Close").Bind(oGui, vTextBtn%vBtnClose%))
	if !vDisableClose
		oGui.OnEvent("Escape", Func("MsgBoxNew_Close").Bind(oGui, vTextBtn%vBtnClose%))
	hGui := oGui.hWnd

	;SC_RESTORE := 0xF120 ;SC_MOVE := 0xF010
	;SC_SIZE := 0xF000 ;SC_MINIMIZE := 0xF020
	;SC_MAXIMIZE := 0xF030 ;SC_CLOSE := 0xF060
	hSysMenu := DllCall("user32\GetSystemMenu", Ptr,hGui, Int,0, Ptr)
	vList := "0xF120,0xF000,0xF020,0xF030,0,"
	Loop Parse, vList, ","
		DllCall("user32\DeleteMenu", Ptr,hSysMenu, UInt,A_LoopField, UInt,0x0) ;MF_BYCOMMAND := 0x0
	if vDisableClose
		DllCall("user32\DeleteMenu", Ptr,hSysMenu, UInt,0xF060, UInt,0x0) ;MF_BYCOMMAND := 0x0
	;DllCall("user32\EnableMenuItem", Ptr,hSysMenu, UInt,0xF060, UInt,0x3) ;MF_DISABLED := 0x2, MF_GRAYED := 0x1

	oGui.MarginX := vMgnX
	oGui.MarginY := vMgnY

	;oGui.Add("Edit", vStylesEdit " " vPosEdit, vDefault)

	Loop vBtnCount
	{
		oBtn%A_Index% := oGui.Add("Button", vStylesButton%A_Index% " " vPosBtn%A_Index%, vTextBtn%A_Index%)
		oBtn%A_Index%.OnEvent("Click", Func("MsgBoxNew_Close").Bind(oGui, "*" vTextBtn%A_Index%))
	}
	if vHasIcon | vHasIconCustom
	{
		oGui.Add("Picture", vStylesIcon " " vPosStcIcon)
		SendMessage(0x172, 1, hIcon, "Static1", "ahk_id " hGui) ;STM_SETIMAGE := 0x172 ;IMAGE_ICON := 1 ;IMAGE_BITMAP := 0
	}
	oGui.Add("Text", vStylesStatic " " vPosStc, vText)

	if (vType & 0x200)
		ControlFocus("Button3", "ahk_id " hGui)
	else if (vType & 0x100)
		ControlFocus("Button2", "ahk_id " hGui)
	else
		ControlFocus("Button1", "ahk_id " hGui)

	;oGui.OnEvent("Size", "MsgBoxNew_Size")

	if !(vTimeout = "")
	{
		BoundFuncTemp := Func("MsgBoxNew_Close").Bind(oGui, "Timeout")
		SetTimer(BoundFuncTemp, -vTimeout)
	}

	oGui.Show(vPosWin)

	DetectHiddenWindows(vDHW)

	if vHasIcon
	{
		vPathSound := AX_MsgBoxOpt["Sound" vTypeIcon]
		if (vPathSound = "")
			;vPathSound := RegRead("HKEY_CURRENT_USER\AppEvents\Schemes\Apps\.Default\System" vTypeIcon "\.Current")
			vPathSound := "*" ({Hand:16, Question:32, Exclamation:48, Asterisk:64}[vTypeIcon])
	}
	else
	{
		vPathSound := AX_MsgBoxOpt.SoundDefault
		if (vPathSound = "")
			;vPathSound := RegRead("HKEY_CURRENT_USER\AppEvents\Schemes\Apps\.Default\.Default\.Current")
			vPathSound := "*-1"
	}
	if (SubStr(vPathSound, 1, 1) = "*")
	|| (InStr(vPathSound, "\") && FileExist(vPathSound))
		SoundPlay(vPathSound)

	WinWaitClose("ahk_id " hGui)

	;if WinExist("ahk_id " hWnd)
	;	WinActivate("ahk_id " hWnd)

	if oMsgBoxRet.Length()
		return oMsgBoxRet[hGui]
	else
		return
}
MsgBoxNew_Close(oGui, vBtn)
{
	global oMsgBoxRet
	try hWnd := oGui.hWnd
	catch
		vBtn := ""
	;MsgBox(A_ThisFunc " " hWnd " " vBtn)
	;Abort,Cancel,Continue,Ignore,No,OK,Retry,TryAgain,Yes
	if (SubStr(vBtn, 1, 1) = "*")
	{
		oMsgBoxRet[hWnd] := SubStr(vBtn, 2)
		ErrorLevel := 0
		AX_MsgBoxResult := SubStr(vBtn, 2)
	}
	;Timeout
	else
	{
		oMsgBoxRet[hWnd] := vBtn
		ErrorLevel := 1
		AX_MsgBoxResult := vBtn
	}
	try oGui.Destroy
}

;==================================================

;FUNCTIONS - INPUTBOX

InputBoxNew(oParams*) ;vText, vWinTitle, vOpt, vDefault
{
	global oInputBoxRet

	vText := !oParams.Length() ? AX_InputBoxOpt.Prompt0 : oParams.HasKey(1) ? oParams.1 : AX_InputBoxOpt.Prompt1
	vWinTitle := oParams.HasKey(2) ? oParams.2 : AX_DlgTitle
	vOpt := oParams.3
	vDefault := oParams.4

	if !IsObject(oInputBoxRet)
		oInputBoxRet := []
	hWnd := WinExist("A")

	DetectHiddenWindows(1)
	vPosWin := "", vTimeout := "", vPassChar := ""
	vMgnX := 5, vMgnY := 5
	vWinW := 368, vWinH := 166

	Loop Parse, vOpt, " `t"
	{
		if (SubStr(A_LoopField, 1, 1) = "W")
			vWinW := SubStr(A_LoopField, 2)
		else if (SubStr(A_LoopField, 1, 1) = "H")
			vWinH := SubStr(A_LoopField, 2)

		if (A_LoopField ~= "i)^[XYWH]")
			vPosWin .= A_LoopField " "
		else if (SubStr(A_LoopField, 1, 1) = "T")
			vTimeout := SubStr(A_LoopField, 2)
		else if (A_LoopField = "Password")
			vHasPassChar := 1
		else if (SubStr(A_LoopField, 1, 8) = "Password")
		&& (StrLen(A_LoopField) > 8)
			vPassChar := SubStr(A_LoopField, 9, 1), vHasPassChar := 1
	}
	if !InStr(vPosWin, "W")
		vPosWin .= " W" vWinW
	if !InStr(vPosWin, "H")
		vPosWin .= " H" vWinH

	vStyleReset := "-0xFFFFFFFF -E0xFFFFFFFF "

	vTextBtn1 := AX_InputBoxOpt.TextButton1
	vTextBtn2 := AX_InputBoxOpt.TextButton2
	vFontName := AX_InputBoxOpt.FontName
	vFontSize := AX_InputBoxOpt.FontSize
	vFontWeight := AX_InputBoxOpt.FontWeight
	vStylesWin := vStyleReset AX_InputBoxOpt.StylesWin
	vStylesEdit := AX_InputBoxOpt.StylesEdit
	vStylesEditPW := AX_InputBoxOpt.StylesEditPW
	vStylesButton1 := AX_InputBoxOpt.StylesButton1
	vStylesButton2 := AX_InputBoxOpt.StylesButton2
	vStylesStatic := AX_InputBoxOpt.StylesStatic

	vFontOpt := "s" vFontSize
	if AX_InputBoxOpt.HFont
		hFont := AX_InputBoxOpt.HFont
	else
		hFont := JEE_FontCreate(vFontName, vFontSize, "", vFontWeight)
	JEE_StrGetDim(vTextBtn1, hFont, vBtn1W, vBtn1H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	JEE_StrGetDim(vTextBtn2, hFont, vBtn2W, vBtn2H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	vBtnW := Max(vBtn1W, vBtn2W) + 16
	vBtnH := Max(vBtn1H, vBtn2H) + 7

	vEditW := vWinW - 2*vMgnX
	vGap := Round((vWinW - 2*vBtnW - 2*vMgnX)/4)
	vBtn1X := vMgnX + vGap
	vBtn2X := vMgnX + vGap*3 + vBtnW
	vBtnY := vWinH - vMgnY - vBtnH
	vEditY := vBtnY - vMgnY - vBtnH
	vStcH := vEditY - vMgnY
	vPosEdit := Format("X{} Y{} W{} H{}", vMgnX, vEditY, vEditW, vBtnH)
	vPosBtn1 := Format("X{} Y{} W{} H{}", vBtn1X, vBtnY, vBtnW, vBtnH)
	vPosBtn2 := Format("X{} Y{} W{} H{}", vBtn2X, vBtnY, vBtnW, vBtnH)
	vPosStc := Format("X{} Y{} W{} H{}", vMgnX, vMgnY, vEditW, vStcH)

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	;WS_POPUPWINDOW := 0x80880000 ;composite style (WS_POPUP | WS_BORDER | WS_SYSMENU)
	;WS_VISIBLE := 0x10000000
	;WS_CLIPSIBLINGS := 0x4000000
	;WS_CAPTION := 0xC00000 ;composite style (WS_BORDER | WS_DLGFRAME)
	;WS_THICKFRAME := 0x40000
	;WS_EX_CONTROLPARENT := 0x10000
	;WS_EX_WINDOWEDGE := 0x100
	oGui := GuiCreate(vStylesWin, vWinTitle)
	oGui.SetFont(vFontOpt, vFontName)
	oGui.OnEvent("Close", Func("InputBoxNew_Close").Bind(oGui, "Close"))
	oGui.OnEvent("Escape", Func("InputBoxNew_Close").Bind(oGui, "Escape"))
	hGui := oGui.hWnd

	oGui.MarginX := vMgnX
	oGui.MarginY := vMgnY

	if !vHasPassChar
		oGui.Add("Edit", vStylesEdit " " vPosEdit, vDefault)
	else
	{
		oGui.Add("Edit", vStylesEditPW " " vPosEdit, vDefault)
		if !(vPassChar = "")
			PostMessage(0xCC, Ord(vPassChar),, "Edit1", "ahk_id " hGui) ;EM_SETPASSWORDCHAR := 0xCC
	}
	ControlFocus("Edit1", "ahk_id " hGui)
	PostMessage(0xB1, 0, -1, "Edit1", "ahk_id " hGui) ;EM_SETSEL := 0xB1 ;select all

	oBtn1 := oGui.Add("Button", vStylesButton1 " " vPosBtn1, vTextBtn1)
	oBtn2 := oGui.Add("Button", vStylesButton2 " " vPosBtn2, vTextBtn2)
	oBtn1.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "OK"))
	oBtn2.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "Cancel"))
	oGui.Add("Text", vStylesStatic " " vPosStc, vText)

	oGui.Show(vPosWin)

	oGui.OnEvent("Size", "InputBoxNew_Size")

	if !(vTimeout = "")
	{
		BoundFuncTemp := Func("InputBoxNew_Close").Bind(oGui, "Timeout")
		SetTimer(BoundFuncTemp, -vTimeout)
	}

	WinWaitClose("ahk_id " hGui)

	;if WinExist("ahk_id " hWnd)
	;	WinActivate("ahk_id " hWnd)

	if oInputBoxRet.Length()
		return oInputBoxRet[hGui].1
	else
		return
}
InputBoxNew_Close(oGui, vBtn)
{
	global oInputBoxRet
	try hWnd := oGui.hWnd
	catch
		vBtn := ""
	;MsgBox(A_ThisFunc " " hWnd " " vBtn)
	if (vBtn = "OK")
	{
		oArray := []
		Loop
		{
			hCtl := ControlGetHwnd("Edit" A_Index, "ahk_id " hWnd)
			if !hCtl
				break
			oArray.Push(ControlGetText("", "ahk_id " hCtl))
		}
		oInputBoxRet[hWnd] := oArray
		ErrorLevel := 0
		AX_InputBoxResult := 0
	}
	else
	{
		ErrorLevel := 1
		AX_InputBoxResult := 1
	}
	try oGui.Destroy
}
InputBoxNew_Size(oGui)
{
	try hWnd := oGui.hWnd
	catch
		return
	WinGetClientPos(vWinX, vWinY, vWinW, vWinH, "ahk_id " hWnd)
	vMgnX := oGui.MarginX
	vMgnY := oGui.MarginY
	ControlGetPos(,, vBtnW, vBtnH, "Button1", "ahk_id " hWnd)

	vEditW := vWinW - 2*vMgnX
	vGap := Round((vWinW - 2*vBtnW - 2*vMgnX)/4)
	vBtn1X := vMgnX + vGap
	vBtn2X := vMgnX + vGap*3 + vBtnW
	vBtnY := vWinH - vMgnY - vBtnH
	vEditY := vBtnY - vMgnY - vBtnH
	vStcH := vEditY - 5
	ControlMove(vMgnX, vEditY, vEditW, vBtnH, "Edit1", "ahk_id " hWnd)
	ControlMove(vBtn1X, vBtnY, vBtnW, vBtnH, "Button1", "ahk_id " hWnd)
	ControlMove(vBtn2X, vBtnY, vBtnW, vBtnH, "Button2", "ahk_id " hWnd)
	ControlMove(vMgnX, vMgnY, vEditW, vStcH, "Static1", "ahk_id " hWnd)
}

;==================================================

;FUNCTIONS - INPUTBOX MULTI

;note: no handling for passwords
;note: no handling for resize
InputBoxMulti(oParams*) ;oText, vWinTitle, vOpt, oDefault
{
	global oInputBoxRet

	oText := oParams.1
	vWinTitle := oParams.HasKey(2) ? oParams.2 : AX_DlgTitle
	vOpt := oParams.3
	oDefault := oParams.4

	if !IsObject(oInputBoxRet)
		oInputBoxRet := []
	hWnd := WinExist("A")

	if !IsObject(oText)
		oText := [oText]
	if !IsObject(oDefault)
		oDefault := [oDefault]

	DetectHiddenWindows(1)
	vPosWin := "", vTimeout := "", vPassChar := ""
	vMgnX := 5, vMgnY := 5
	vWinW := 368, vWinH := 166

	Loop Parse, vOpt, " `t"
	{
		if (SubStr(A_LoopField, 1, 1) = "W")
			vWinW := SubStr(A_LoopField, 2)
		else if (SubStr(A_LoopField, 1, 1) = "H")
			vWinH := SubStr(A_LoopField, 2)

		if (A_LoopField ~= "i)^[XYWH]")
			vPosWin .= A_LoopField " "
		else if (SubStr(A_LoopField, 1, 1) = "T")
			vTimeout := SubStr(A_LoopField, 2)
	}

	vStyleReset := "-0xFFFFFFFF -E0xFFFFFFFF "

	vTextBtn1 := AX_InputBoxOpt.TextButton1
	vTextBtn2 := AX_InputBoxOpt.TextButton2
	vFontName := AX_InputBoxOpt.FontName
	vFontSize := AX_InputBoxOpt.FontSize
	vFontWeight := AX_InputBoxOpt.FontWeight
	vStylesWin := vStyleReset AX_InputBoxOpt.StylesWin
	vStylesEdit := AX_InputBoxOpt.StylesEdit
	vStylesEditPW := AX_InputBoxOpt.StylesEditPW
	vStylesButton1 := AX_InputBoxOpt.StylesButton1
	vStylesButton2 := AX_InputBoxOpt.StylesButton2
	vStylesStatic := AX_InputBoxOpt.StylesStatic

	vFontOpt := "s" vFontSize
	hFont := JEE_FontCreate(vFontName, vFontSize, "", vFontWeight)
	JEE_StrGetDim(vTextBtn1, hFont, vBtn1W, vBtn1H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	JEE_StrGetDim(vTextBtn2, hFont, vBtn2W, vBtn2H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	vBtnW := Max(vBtn1W, vBtn2W) + 16
	vBtnH := Max(vBtn1H, vBtn2H) + 7

	vGap := Round((vWinW - 2*vBtnW - 2*vMgnX)/4)
	vBtn1X := vMgnX + vGap
	vBtn2X := vMgnX + vGap*3 + vBtnW

	vEditW := vWinW - 2*vMgnX
	vEditH := vBtnH
	vStcW := vEditW

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	;WS_POPUPWINDOW := 0x80880000 ;composite style (WS_POPUP | WS_BORDER | WS_SYSMENU)
	;WS_VISIBLE := 0x10000000
	;WS_CLIPSIBLINGS := 0x4000000
	;WS_CAPTION := 0xC00000 ;composite style (WS_BORDER | WS_DLGFRAME)
	;WS_THICKFRAME := 0x40000
	;WS_EX_CONTROLPARENT := 0x10000
	;WS_EX_WINDOWEDGE := 0x100
	oGui := GuiCreate(vStylesWin, vWinTitle)
	oGui.SetFont(vFontOpt, vFontName)
	oGui.OnEvent("Close", Func("InputBoxNew_Close").Bind(oGui, "Close"))
	oGui.OnEvent("Escape", Func("InputBoxNew_Close").Bind(oGui, "Escape"))
	hGui := oGui.hWnd

	vPosX := vMgnX, vPosY := vMgnY
	Loop Max(oText.Length(), oDefault.Length())
	{
		if (A_Index <= oDefault.Length())
		{
			JEE_StrGetDim(oText[A_Index], hFont, vStcW2, vStcH, vDTFormat:=0x400, vLimW:=vWinW, vLimH:="")
			vPosStc := Format("X{} Y{} W{} H{}", vPosX, vPosY, vStcW, vStcH)
			oGui.Add("Text", vStylesStatic " " vPosStc, oText[A_Index])
			vPosY += vStcH + vMgnY
		}
		if (A_Index <= oDefault.Length())
		{
			vPosEdit := Format("X{} Y{} W{} H{}", vPosX, vPosY, vEditW, vEditH)
			oGui.Add("Edit", vStylesEdit " " vPosEdit, oDefault[A_Index])
			vPosY += vEditH + vMgnY
		}
	}

	vBtnY := vPosY
	vPosBtn1 := Format("X{} Y{} W{} H{}", vBtn1X, vBtnY, vBtnW, vBtnH)
	vPosBtn2 := Format("X{} Y{} W{} H{}", vBtn2X, vBtnY, vBtnW, vBtnH)
	vPosY += vBtnH + vMgnY

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	oGui.MarginX := vMgnX
	oGui.MarginY := vMgnY

	ControlFocus("Edit1", "ahk_id " hGui)
	PostMessage(0xB1, 0, -1, "Edit1", "ahk_id " hGui) ;EM_SETSEL := 0xB1 ;select all

	oBtn1 := oGui.Add("Button", vStylesButton1 " " vPosBtn1, vTextBtn1)
	oBtn2 := oGui.Add("Button", vStylesButton2 " " vPosBtn2, vTextBtn2)
	oBtn1.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "OK"))
	oBtn2.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "Cancel"))

	if !InStr(vPosWin, "W")
		vPosWin .= " W" vWinW
	if !InStr(vPosWin, "H")
		vPosWin .= " H" vPosY
	oGui.Show(vPosWin)

	;oGui.OnEvent("Size", "InputBoxNew_Size")

	if !(vTimeout = "")
	{
		BoundFuncTemp := Func("InputBoxNew_Close").Bind(oGui, "Timeout")
		SetTimer(BoundFuncTemp, -vTimeout)
	}

	WinWaitClose("ahk_id " hGui)

	;if WinExist("ahk_id " hWnd)
	;	WinActivate("ahk_id " hWnd)

	if oInputBoxRet.Length()
		return oInputBoxRet[hGui]
	else
		return
}

;==================================================

;FUNCTIONS - PROGRESS/SPLASHIMAGE (AHK V1 DIAGNOSTIC)

;function wrappers for AHK v1
;to compare with these clone functions
/*
Progress(ProgressParam1, SubText:="", MainText:="", WinTitle:="", FontName:="")
{
	Progress, % ProgressParam1, % SubText, % MainText, % WinTitle, % FontName
}
SplashImage(ImageFile:="", Options:="", SubText:="", MainText:="", WinTitle:="", FontName:="")
{
	SplashImage, % ImageFile, % Options, % SubText, % MainText, % WinTitle, % FontName
}
*/

;diagnostic functions to test Progress/SplashImage commands using an AHK v1 exe
ProgressAhk1(oParams*)
{
	SplashAhk1("Progress", oParams*)
}
SplashImageAhk1(oParams*)
{
	SplashAhk1("SplashImage", oParams*)
}
SplashAhk1(vFunc, oParams*)
{
	global vGblPathAhk1
	global vGblSleep
	vPathAhk1 := vGblPathAhk1
	vSleep := vGblSleep
	vScript := vFunc
	if !FileExist(vPathAhk1)
		return
	Loop oParams.Length()
	{
		vValue := oParams[A_Index]
		if (vValue = "")
			vScript .= ", % " Chr(34) Chr(34)
		else
		{
			vValue := StrReplace(vValue, "`r", "``r")
			vValue := StrReplace(vValue, "`n", "``n")
			vScript .= ", % " Chr(34) vValue Chr(34)
		}
	}
	vScript .= "`r`nSleep, " vSleep
	;MsgBox(vScript)

	vDoWait := 1
	;based on ExecScript
	oShell := ComObjCreate("WScript.Shell")
	oExec := oShell.Exec(vPathAhk1 " /ErrorStdOut *")
	oExec.StdIn.Write(vScript)
	oExec.StdIn.Close()
	if vDoWait
		return oExec.StdOut.ReadAll()
}

;==================================================

;FUNCTIONS - PROGRESS/SPLASHIMAGE (AHK V2)

ProgressNew(vParam1, vSubText:="", vMainText:="", vWinTitle:=" ", vFontName:="")
{
	SplashImageNew("*PROGRESS*", vParam1, vSubText, vMainText, vWinTitle, vFontName)
}
SplashImageNew(vImageFile:="", vOpt:="", vSubText:="", vMainText:="", vWinTitle:=" ", vFontName:="")
{
	static vIsInit, oColRGB, vFontNameDefault, oGuiStore
	local vX, vY
	local vZX, vZY, vFM, vFS, vWM, vWS
	local vZW, vZH
	local vCB, vCT, vCW
	if !vIsInit
	{
		vListColRGB := " ;continuation section
		(LTrim
		Black=000000
		Silver=C0C0C0
		Gray=808080
		White=FFFFFF
		Maroon=800000
		Red=FF0000
		Purple=800080
		Fuchsia=FF00FF
		Green=008000
		Lime=00FF00
		Olive=808000
		Yellow=FFFF00
		Navy=000080
		Blue=0000FF
		Teal=008080
		Aqua=00FFFF
		)"
		oColRGB := Object(StrSplit(vListColRGB, ["=", "`n"])*)

		;DEFAULT_GUI_FONT := 17
		hFontDefault := DllCall("gdi32\GetStockObject", Int,17, Ptr)
		vSize := DllCall("gdi32\GetObject", Ptr,hFontDefault, Int,vSize, Ptr,0)
		VarSetCapacity(LOGFONT, vSize, 0)
		DllCall("gdi32\GetObject", Ptr,hFontDefault, Int,vSize, Ptr,&LOGFONT)
		vFontNameDefault := StrGet(&LOGFONT + 28, 32)
		DllCall("gdi32\DeleteObject", Ptr,hFontDefault)

		oGuiStore := {}
		vIsInit := 1
	}

	vDHW := A_DetectHiddenWindows
	DetectHiddenWindows("On")

	vNum := 1
	if (vImageFile = "*PROGRESS*") ;Progress
	{
		vIsProgress := 1
		vP := 0 ;progress bar (filled) position [Progress windows only]
		vZH := 20 ;progress bar thickness
		if RegExMatch(vOpt, "^(\d+):(.*)$", oMatch) ;window number (which window to change)
			vNum := oMatch.1, vOpt := oMatch.2
		if (vOpt = "Off")
		|| (vOpt = "Show")
			vImageFile := vOpt, vOpt := ""
		;else if !(vOpt+0 = "")
		else if vOpt is "number"
			vP := vOpt
	}

	if (vWinTitle = " ")
		vWinTitle := AX_DlgTitle

	if (SubStr(vFontName, 1, 6) = "HFONT:")
		hFont := SubStr(vFontName, 7)+0
	if !hFont && (vFontName = "")
		vFontName := vFontNameDefault

	if RegExMatch(vImageFile, "^(\d+):(.*)$", oMatch) ;window number (which window to change)
		vNum := oMatch.1, vImageFile := oMatch.2

	vID := (vIsProgress ? "P" : "S") vNum
	if (vImageFile = "Off")
	{
		if !oGuiStore.HasKey(vID)
			return
		oGui := GuiFromHwnd(oGuiStore[vID])
		oGuiStore.Delete(vID)
		oGui.Destroy()
		DetectHiddenWindows(vDHW)
		return
	}
	else if (vImageFile = "Show")
	{
		WinShow("ahk_id " oGuiStore[vID])
		DetectHiddenWindows(vDHW)
		return
	}
	else if (vImageFile = "Hide")
	{
		WinHide("ahk_id " oGuiStore[vID])
		DetectHiddenWindows(vDHW)
		return
	}
	else if !vIsProgress
	&& !(vImageFile = "")
	&& !InStr(vImageFile, "\")
	&& !(SubStr(vImageFile, 1, 8) = "HBITMAP:")
	&& !(SubStr(vImageFile, 1, 6) = "HICON:")
		vImageFile := A_WorkingDir "\" vImageFile

	;get image dimensions
	if !vIsProgress
		if (SubStr(vImageFile, 1, 8) = "HBITMAP:")
		{
			hImg := SubStr(vImageFile, 9)+0
			if !(hImg ~= "^\d+$")
				return
			JEE_HBitmapGetDims(hImg, vImgW, vImgH)
		}
		else if (SubStr(vImageFile, 1, 6) = "HICON:")
		{
			hImg := SubStr(vImageFile, 7)+0
			if !(hImg ~= "^\d+$")
				return
			JEE_HIconGetDims(hImg, vImgW, vImgH)
		}
		else if InStr(vImageFile, "\")
		{
			hImg := LoadPicture(vImageFile,, vImgType) ;IMAGE_ICON := 1 ;IMAGE_BITMAP := 0
			if (vImgType = 0)
				JEE_HBitmapGetDims(hImg, vImgW, vImgH)
			else if (vImgType = 1)
				JEE_HIconGetDims(hImg, vImgW, vImgH)
		}

	;WS_POPUP := 0x80000000	;WS_DISABLED := 0x8000000
	;WS_CLIPSIBLINGS := 0x4000000
	;WS_CAPTION := 0xC00000 := WS_BORDER|WS_DLGFRAME := 0x800000|0x400000
	vWinStyle := 0x8CC00000
	;WS_EX_WINDOWEDGE := 0x100 ;WS_EX_TOPMOST := 0x8
	vWinExStyle := 0x108
	vWinX := vWinY := vWinW := vWinX := ""
	vCM := 1, vCS := 1 ;main/sub text centred by default
	vFM := 0, vFS := 0 ;main/sub font size
	;FW_BOLD := 700 ;FW_SEMIBOLD := 600
	;FW_NORMAL := 400 ;FW_DONTCARE := 0
	vWM := 600, vWS := 400 ;main/sub font weight
	if vIsProgress || !(vMainText = "") || !(vSubText = "")
		vZX := 10, vZY := 5 ;margins
	else
		vZX := 0, vZY := 0 ;margins
	vR1 := "" ;progress bar range ;[Progress windows only]
	vR2 := "" ;progress bar range ;[Progress windows only]

	Loop Parse, vOpt, " `t"
	{
		vTemp := A_LoopField
		if (vTemp = "A") ;always-on-top *off*
		|| (vTemp = "A0") ;always-on-top *off*
			vWinExStyle &= 0x8 ^ -1 ;WS_EX_TOPMOST := 0x8
		else if (vTemp = "B") ;*no* border, no title bar
		|| (vTemp = "B0") ;*no* border, no title bar
			vWinStyle &= 0xC00000 ^ -1 ;WS_CAPTION := 0xC00000
		else if (vTemp = "B1") ;thin border, no title bar
		{
			vWinStyle &= 0xC00000 ^ -1 ;WS_CAPTION := 0xC00000
			vWinStyle |= 0x800000 ;WS_BORDER := 0x800000
		}
		else if (vTemp = "B2") ;dialog-style border, no title bar
		{
			vWinStyle &= 0xC00000 ^ -1 ;WS_CAPTION := 0xC00000
			vWinStyle |= 0x400000 ;WS_DLGFRAME := 0x400000
		}
		else if (vTemp = "M") ;movable
			vWinStyle &= 0x8000000 ^ -1 ;WS_DISABLED := 0x8000000
		else if (vTemp = "M1") ;resizeable
		{
			vWinStyle |= 0x40000 ;WS_THICKFRAME := 0x40000
			vWinStyle &= 0x8000000 ^ -1 ;WS_DISABLED := 0x8000000
		}
		else if (vTemp = "M2") ;resizeable + sysmenu + title bar buttons
		;WS_MINIMIZEBOX := 0x20000 ;WS_MAXIMIZEBOX := 0x10000
		;WS_SYSMENU := 0x80000 ;WS_THICKFRAME := 0x40000
		{
			vWinStyle |= 0xF0000
			vWinStyle &= 0x8000000 ^ -1 ;WS_DISABLED := 0x8000000
		}
		;else if (vTemp = "T") ;taskbar button
		;	vIsOwned := 0
		else if (vTemp = "Hide")
		{
			WinHide("ahk_id " oGuiStore[vID])
			DetectHiddenWindows(vDHW)
			return
		}
		else if (vTemp ~= "i)^[XY]-?\d") ;X/Y coordinates
		|| (vTemp ~= "i)^[WH]\d")
		{
			vTemp1 := SubStr(vTemp, 1, 1)
			vWin%vTemp1% := SubStr(vTemp, 2)+0
		}
		else if (vTemp ~= "i)^C\d\d$") ;centred
		{
			vCM := SubStr(vTemp, 2, 1)
			vCS := SubStr(vTemp, 3, 1)
		}
		else if (vTemp ~= "i)^(ZX|ZY|FM|FS|WM|WS)\d+$") ;X/Y margins, main/sub font size, main/sub font weight
		|| (vTemp ~= "i)^(ZW|ZH)-?\d+$") ;image width/height, progress bar thickness
		{
			vTemp1 := SubStr(vTemp, 1, 2)
			v%vTemp1% := SubStr(vTemp, 3)
		}
		;CB## [Progress windows only]
		else if (vTemp ~= "i)^(CB|CT|CW)[a-z0-9]+") ;progress bar/text/window colours
		{
			vTemp1 := SubStr(vTemp, 1, 2)
			vTemp2 := SubStr(vTemp, 3)
			if oColRGB.HasKey(vTemp2)
				v%vTemp1% := oColRGB[vTemp2] ;hex number without 0x
				;v%vTemp1% := "0x" oColRGB[vTemp2] ;hex number with 0x
			else
				v%vTemp1% := Format("{:06X}", vTemp2) ;hex number without 0x
				;v%vTemp1% := vTemp ;dec number/hex number with 0x
		}
		;P## [Progress windows only] progress bar (filled) position
		else if RegExMatch(vTemp, "i)^P-?\d+")
			vP := SubStr(vTemp, 2)
		;R##-## [Progress windows only] progress bar range
		else if RegExMatch(vTemp, "i)^R(-?\d+)-(-?\d+)$", oMatch)
			vR1 := oMatch.1+0, vR2 := oMatch.2+0
		else if (SubStr(vTemp, 1, 2) = "IE") ;use Internet Explorer_Server control
			vIsIE := 1, vZoomIE := SubStr(vTemp, 3)
		else if (SubStr(vTemp, 1, 6) = "HICON:")
			hIcon := SubStr(vTemp, 7), hIcon += 0
	}
	vStylesWin := "-0xFFFFFFFF " vWinStyle " E" vWinExStyle
	if !vZH ;if no progress bar, place MainText slightly higher
		vZY := 0

	if oGuiStore.HasKey(vID)
	{
		if vIsProgress && !(vP = "")
			SendMessage(0x402, vP,, "msctls_progress321", "ahk_id " oGuiStore[vID]) ;PBM_SETPOS := 0x402
		vText1 := ControlGetText("Static1", "ahk_id " oGuiStore[vID])
		vText2 := ControlGetText("Static2", "ahk_id " oGuiStore[vID])
		if !(vText1 = vSubText)
			ControlSetText(vText1, "Static1", "ahk_id " oGuiStore[vID])
		if !(vText1 = vMainText)
			ControlSetText(vText2, "Static2", "ahk_id " oGuiStore[vID])
		if !(vCB = "")
		{
			vCB := "0x" vCB
			vCBBGR := ((0xFF & vCB) << 16) + (0xFF00 & vCB) + ((0xFF0000 & vCB) >> 16)
			SendMessage(0x409, 0, vCBBGR, "msctls_progress321", "ahk_id " oGuiStore[vID]) ;PBM_SETBARCOLOR := 0x409 ;doesn't work if set theme to blank
			;SendMessage(0x2001, 0, vCBBGR, "msctls_progress321", "ahk_id " oGuiStore[vID]) ;PBM_SETBKCOLOR := 0x2001 ;doesn't work if set theme to blank
		}
		if !(vCT = "")
		{
			hCtl1 := ControlGetHwnd("Static1", "ahk_id " oGuiStore[vID])
			hCtl2 := ControlGetHwnd("Static2", "ahk_id " oGuiStore[vID])
			GuiCtrlFromHwnd(hCtl1).Opt("C" vCW)
			GuiCtrlFromHwnd(hCtl2).Opt("C" vCW)
		}
		if !(vCW = "")
		{
			oGui := GuiFromHwnd(oGuiStore[vID])
			oGui.BackColor := vCW
		}
		DetectHiddenWindows(vDHW)
		return
	}

	oGui := GuiCreate(vStylesWin, vWinTitle)
	;oGui.SetFont(vFontOpt, vFontName)
	;oGui.OnEvent("Close", Func("MsgBoxNew_Close").Bind(oGui, vTextBtn%vBtnClose%))
	;oGui.OnEvent("Escape", Func("MsgBoxNew_Close").Bind(oGui, vTextBtn%vBtnClose%))
	hGui := oGui.hWnd
	oGuiStore[vID] := hGui

	if (vWinStyle & 0x80000) ;WS_SYSMENU := 0x80000
		SendMessage(0x80, 0, hIcon,, "ahk_id " hGui) ;WM_SETICON := 0x80 ;ICON_SMALL := 0 ;sets title bar icon + taskbar icon
	if !vIsOwned
		SendMessage(0x80, 1, hIcon,, "ahk_id " hGui) ;WM_SETICON := 0x80 ;ICON_BIG := 1 ;sets alt+tab icon

	if !(vCW = "")
		oGui.BackColor := vCW

	if hFont
		hFontM := hFontS := hFont
	else
	{
		if !(vMainText = "")
			hFontM := JEE_FontCreate(vFontName, vFM, "", vWM)
		if !(vSubText = "")
			hFontS := JEE_FontCreate(vFontName, vFS, "", vWS)
	}

	;WS_CHILD := 0x40000000 ;WS_VISIBLE := 0x10000000
	;PBS_SMOOTH := 0x1
	;WS_EX_CLIENTEDGE := 0x200
	;vStylesPgs := 0x50000001
	vStylesPgs := 0x50000000

	vTextMW := vTextMH := 0
	vTextSW := vTextSH := 0
	if !(vMainText = "")
		JEE_StrGetDim(vMainText, hFontM, vTextMW, vTextMH, vDTFormat:=0x400, vLimW:="", vLimH:="")
	if !(vSubText = "")
		JEE_StrGetDim(vSubText, hFontS, vTextSW, vTextSH, vDTFormat:=0x400, vLimW:="", vLimH:="")

	if (vZW = -1) && (vZH = -1)
		vZW := vZH := ""
	if (vZW = -1)
		vZW := Round(vImgW * (vZH/vImgH))
	if (vZH = -1)
		vZH := Round(vImgH * (vZW/vImgW))
	if !(vZW = "")
		vImgW := vZW
	if !(vZH = "")
		vImgH := vZH
	if !vImgW
		vImgW := 0
	if !vImgH && !vIsProgress
		vImgH := 20
	if !vWinW
		if vIsProgress
			vWinW := Max(280, vTextMW, vTextSW) + 2*vZX
		else
			vWinW := vImgW + 2*vZX

	vStcMY := vZY
	vImgY := vStcMY + (vTextMH = 0 ? 0 : 21)
	vStcSY := vImgY + vImgH + vZY
	if !vWinH
		vWinH := vStcSY + vTextSH

	;WS_THICKFRAME require a minimum window width
	if (vWinStyle & 0x40000) ;WS_THICKFRAME := 0x40000
	{
		vMinW := SysGet(34) ;SM_CXMINTRACK := 34
		vMinH := SysGet(35) ;SM_CYMINTRACK := 35
		;specify negative values to convert window to client
		;What is the inverse of AdjustWindowRect and AdjustWindowRectEx? – The Old New Thing
		;https://blogs.msdn.microsoft.com/oldnewthing/20131017-00/?p=2903
		VarSetCapacity(RECT, 16, 0)
		NumPut(-vMinW, RECT, 8, "Int")
		NumPut(-vMinH, RECT, 12, "Int")
		DllCall("user32\AdjustWindowRectEx", Ptr,&RECT, UInt,vWinStyle, Int,0, UInt,vWinExStyle)
		vWinW2 := Abs(NumGet(&RECT, 8, "Int"))
		vWinH2 := Abs(NumGet(&RECT, 12, "Int"))
		vWinW := Max(vWinW, vWinW2)
		;vWinH := Max(vWinH, vWinH2)
	}

	MonitorGetWorkArea(1, vLimX, vLimY, vLimR, vLimB)
	vLimW := vLimR-vLimX, vLimH := vLimB-vLimY
	if (vWinW > vLimW)
		vWinW := vLimW
	if (vWinH > vLimH)
		vWinH := vLimH

	if vIsProgress && !vImgW
		vImgW := vWinW - 2*vZX
	vTextMW := vWinW - 2*vZX
	vTextSW := vWinW - 2*vZX

	vMainTextPos := Format("X{} Y{} W{} H{}", vZX, vStcMY, vTextMW, vTextMH)
	vSubTextPos := Format("X{} Y{} W{} H{}", vZX, vStcSY, vTextSW, vTextSH)
	vImgPos := Format("X{} Y{} {} {}", vZX, vImgY, (vImgW = "") ? "" : "W" vImgW, (vImgH = "") ? "" : "H" vImgH)

	;WS_CHILD := 0x40000000 ;WS_VISIBLE := 0x10000000
	;SS_NOPREFIX := 0x80
	;SS_CENTER := 0x1 ;SS_LEFT := 0x0
	vStylesMainText := (0x50000080|vCM) " E0x00000000"
	vStylesSubText := (0x50000080|vCS) " E0x00000000"
	if !(vCT = "")
		vCT := "C" vCT
	oCtl1 := oGui.Add("Text", vStylesMainText " " vMainTextPos " " vCT, vMainText)
	if vIsProgress && vImgH
	{
		vTemp := vStylesPgs " " vImgPos
		if !(vCB = "")
			vTemp .= " C" vCB
		if !(vR1 = "") && !(vR2 = "")
			vTemp .= " Range" vR1 "-" vR2
		oCtl3 := oGui.Add("Progress", vTemp, vP)
		if (vCB = "")
			DllCall("uxtheme\SetWindowTheme", Ptr,oCtl3.hWnd, Str,"", Ptr,0)
		;SendMessage(0x401, 0, (vR2 << 16) | (vR1 & 0xFFFF),, "ahk_id " oCtl3.hWnd) ;PBM_SETRANGE := 0x401
		;SendMessage(0x406, vR1, vR2,, "ahk_id " oCtl3.hWnd) ;PBM_SETRANGE32 := 0x406
	}
	if !(vCT = "")
		vCT := "C" vCT
	oCtl2 := oGui.Add("Text", vStylesSubText " " vSubTextPos " " vCT, vSubText)
	if !vIsProgress && !vIsIE
		oCtl3 := oGui.Add("Picture", vStylesImg " " vImgPos, vImageFile)
	if !vIsProgress && vIsIE
	{
		oWB := oGui.Add("ActiveX", vImgPos, "Shell.Explorer").Value
		oWB.Navigate(vImageFile)
		;OLECMDID_OPTICAL_ZOOM := 63 ;OLECMDEXECOPT_DONTPROMPTUSER := 2
		while oWB.Busy || !(oWB.ReadyState = 4)
			Sleep(100)
		;oWB.ExecWB(63, 2, 30, 0) ;zoom 30%
		if !(vZoomIE = "")
			oWB.ExecWB(63, 2, vZoomIE+0, 0)
		;oWB.document.parentWindow.scrollBy(120, 35)
		oWB.document.body.scroll := "no"
	}

	SendMessage(0x30, hFontM, 0,, "ahk_id " oCtl1.hWnd) ;WM_SETFONT := 0x30
	SendMessage(0x30, hFontS, 0,, "ahk_id " oCtl2.hWnd) ;WM_SETFONT := 0x30

	;vWinPos := Format("X{} Y{} W{} H{}", vWinX, vWinY, vWinW, vWinH)
	if !(vWinX = "")
		vWinPos .= "X" vWinX " "
	if !(vWinY = "")
		vWinPos .= "Y" vWinY " "
	if !(vWinW = "")
		vWinPos .= "W" vWinW " "
	if !(vWinH = "")
		vWinPos .= "H" vWinH " "

	oGui.Show(vWinPos " NoActivate")
	DetectHiddenWindows(vDHW)
	;WinWaitClose("ahk_id " hGui)
	return hGui
}

;==================================================

;FUNCTIONS - TOOLTIP

;note: specify c for vPosX or vPosY, to place the ToolTip in the centre of the screen
ToolTipNew(vText:="", vPosX:="", vPosY:="", vWhichToolTip:=1, vColTxRGB:="", vColBkRGB:="")
{
	static oArray := []
	if (vText = "")
	{
		if oArray[vWhichToolTip]
			DllCall("user32\DestroyWindow", Ptr,oArray[vWhichToolTip])
		oArray[vWhichToolTip] := 0
		return
	}
	else if oArray[vWhichToolTip]
		DllCall("user32\DestroyWindow", Ptr,oArray[vWhichToolTip])

	vFontName := AX_ToolTipOpt.FontName
	vFontSize := AX_ToolTipOpt.FontSize
	vFontWeight := AX_ToolTipOpt.FontWeight
	if (vColTxRGB = "")
		vColTxRGB := AX_ToolTipOpt.ColTxRGB
	if (vColBkRGB = "")
		vColBkRGB := AX_ToolTipOpt.ColBkRGB
	if (vFontName = "")
		vFontName := "Segoe UI"
	if (vFontSize = "")
		vFontSize := 9
	if (vFontWeight = "")
		vFontWeight := 400
	if (vColTxRGB = "")
		vColTxRGB := 0x000000
	if (vColBkRGB = "")
		vColBkRGB := 0xFFFFFF

	;if either X or Y is unspecified,
	;get cursor position
	if (vPosX = "") || (vPosY = "")
	{
		vCMM := A_CoordModeMouse
		CoordMode("Mouse", "Screen")
		MouseGetPos(vCurX, vCurY)
		CoordMode("Mouse", vCMM)
	}

	;if either X or Y is numeric,
	;get required offset
	if !((vPosX = "c") || (vPosX = ""))
	|| !((vPosY = "c") || (vPosY = ""))
	{
		if (A_CoordModeToolTip = "Window")
			WinGetPos(vOffsetX, vOffsetY,,, "A")
		else if (A_CoordModeToolTip = "Client")
			WinGetClientPos(vOffsetX, vOffsetY,,, "A")
		else
			vOffsetX := vOffsetY := 0
	}

	if (vPosX = "")
		vPosX := vCurX+16
	else if !(vPosX = "c")
		vPosX += vOffsetX
	if (vPosY = "")
		vPosY := vCurY+16
	else if !(vPosY = "c")
		vPosY += vOffsetY

	vColTxBGR := ((0xFF & vColTxRGB) << 16) + (0xFF00 & vColTxRGB) + ((0xFF0000 & vColTxRGB) >> 16)
	vColBkBGR := ((0xFF & vColBkRGB) << 16) + (0xFF00 & vColBkRGB) + ((0xFF0000 & vColBkRGB) >> 16)

	vDHW := A_DetectHiddenWindows
	DetectHiddenWindows("On")
	vSizeTI := A_PtrSize=8?72:48
	VarSetCapacity(TOOLINFO, vSizeTI, 0)
	NumPut(vSizeTI, &TOOLINFO, 0, "UInt") ;cbSize
	NumPut(0x20, &TOOLINFO, 4, "UInt") ;uFlags ;TTF_TRACK := 0x20
	NumPut(&vText, &TOOLINFO, A_PtrSize=8?48:36, "Ptr") ;lpszText

	;create window
	;WS_VISIBLE := 0x10000000
	;vWinStyle := 0x8
	;vWinExStyle := 0x3
	vWinStyle := 0x94000003
	;vWinStyle := 0x84000003
	vWinExStyle := 0x00080088

	;TTS_USEVISUALSTYLE := 0x100
	;TTS_CLOSE := 0x80 ;TTS_BALLOON := 0x40
	;TTS_NOFADE := 0x20 ;TTS_NOANIMATE := 0x10
	;TTS_NOPREFIX := 0x2 ;TTS_ALWAYSTIP := 0x1
	;WS_EX_TOPMOST := 0x8
	hTT := DllCall("user32\CreateWindowEx", UInt,vWinExStyle, Str,"tooltips_class32", Ptr,0, UInt,vWinStyle, Int,0, Int,0, Int,0, Int,0, Ptr,A_ScriptHwnd, Ptr,0, Ptr,0, Ptr,0, Ptr)
	;WinSet, Style, 0x94000003, % "ahk_id " hTT
	;WinSet, ExStyle, 0x00080088, % "ahk_id " hTT

	;set background colours/margins
	;the text/font/margins will determine the window size
	if (AX_ToolTipOpt.SetWindowTheme = 0)
		DllCall("uxtheme\SetWindowTheme", Ptr,hTT, Ptr,0, Str,"")
	VarSetCapacity(RECT, 16, 0)
	;vMgnL := vMgnR := 8
	;vMgnT := vMgnB := 4
	vMgnL := vMgnR := 4
	vMgnT := vMgnB := 1
	vRect := vMgnL "," vMgnT "," vMgnR "," vMgnB
	Loop Parse, vRect, ","
		NumPut(A_LoopField, &RECT, A_Index*4-4, "Int")
	SendMessage(0x41A, 0, &RECT,, "ahk_id " hTT) ;TTM_SETMARGIN := 0x41A
	SendMessage(0x413, vColBkBGR, 0,, "ahk_id " hTT) ;TTM_SETTIPBKCOLOR := 0x413
	SendMessage(0x414, vColTxBGR, 0,, "ahk_id " hTT) ;TTM_SETTIPTEXTCOLOR := 0x414

	;to allow multiline ToolTips
	SendMessage(0x418, 0, 200,, "ahk_id " hTT) ;TTM_SETMAXTIPWIDTH := 0x418

	;set font
	vFontHeight := -Round(vFontSize*A_ScreenDPI/72)
	hFont := DllCall("gdi32\CreateFont", Int,vFontHeight, Int,0, Int,0, Int,0, Int,vFontWeight, UInt,0, UInt,0, UInt,0, UInt,0, UInt,0, UInt,0, UInt,0, UInt,0, Str,vFontName, Ptr)
	SendMessage(0x30, hFont, 0,, "ahk_id " hTT) ;WM_SETFONT := 0x30

	if (vPosX = "c") || (vPosY = "c")
		JEE_StrGetDim(vText, hFont, vTextW, vTextH, vDTFormat:=0x400, vLimW:="", vLimH:="")
	if (vPosX = "c")
	{
		vWinW := vTextW + vMgnL + vMgnR + 5 ;5 is of unknown origin
		vPosX := Round((A_ScreenWidth - vWinW) / 2)
	}
	if (vPosY = "c")
	{
		vWinH := vTextH + vMgnT + vMgnB + 3 ;3 is of unknown origin
		vPosY := Round((A_ScreenHeight - vWinH) / 2)
	}

	;TTM_TRACKPOSITION will determine the window position
	SendMessage(A_IsUnicode?0x432:0x404, 0, &TOOLINFO,, "ahk_id " hTT) ;TTM_ADDTOOLW := 0x432
	SendMessage(0x412, 0, (vPosX&0xFFFF)|(vPosY<<16),, "ahk_id " hTT) ;TTM_TRACKPOSITION := 0x412
	SendMessage(0x411, 1, &TOOLINFO,, "ahk_id " hTT) ;TTM_TRACKACTIVATE := 0x411

	;vWinPos1 := Format("x{} y{} w{} h{}", vPosX, vPosY, vWinW, vWinH)
	;WinGetPos, vWinX, vWinY, vWinW, vWinH, % "ahk_id " hTT
	;vWinPos2 := Format("x{} y{} w{} h{}", vWinX, vWinY, vWinW, vWinH)
	;MsgBox, % vWinPos1 "`r`n" vWinPos2

	oArray[vWhichToolTip] := hTT

	DetectHiddenWindows(vDHW)
}

;==================================================

;FUNCTIONS - BORDERS

Borders(vPosX, vPosY, vPosW:=0, vPosH:=0, vTime:=1000, vColRGB:=0xFFFF00, vBdrW:=5, vBdrH:=5)
{
	;based on example at the very bottom of:
	;WinSet
	;https://autohotkey.com/docs/commands/WinSet.htm
	static vWinClass := "AHKBordersClass"
	static vFunc := "Borders_WndProc"
	static pWndProc := CallbackCreate(vFunc, "F")
	static vPIs64 := (A_PtrSize=8)
	static vSize := vPIs64?80:48
	VarSetCapacity(WNDCLASSEX, vSize, 0)
	NumPut(vSize, &WNDCLASSEX, 0, "UInt") ;cbSize
	NumPut(pWndProc, &WNDCLASSEX, 8, "Ptr") ;lpfnWndProc
	vColBGR := ((0xFF & vColRGB) << 16) + (0xFF00 & vColRGB) + ((0xFF0000 & vColRGB) >> 16)
	hBrush := DllCall("gdi32\CreateSolidBrush", UInt,vColBGR, Ptr)
	NumPut(hBrush, &WNDCLASSEX, vPIs64?48:32, "Ptr") ;hbrBackground
	NumPut(&vWinClass, &WNDCLASSEX, vPIs64?64:40, "Ptr") ;lpszClassName
	DllCall("user32\RegisterClassEx", Ptr,&WNDCLASSEX, UShort)

	vPosX -= vBdrW, vPosY -= vBdrH
	vPosW += vBdrW*2, vPosH += vBdrH*2

	DetectHiddenWindows("On")
	;WS_POPUP := 0x80000000
	;WS_EX_TOOLWINDOW := 0x80 ;WS_EX_TOPMOST := 0x8
	vWinText := "", vWinStyle := 0x80000000, vWinExStyle := 0x88
	hWnd := DllCall("user32\CreateWindowEx", UInt,vWinExStyle, Str,vWinClass, Str,vWinText, UInt,vWinStyle, Int,vPosX, Int,vPosY, Int,vPosW, Int,vPosH, Ptr,0, Ptr,0, Ptr,0, Ptr,0, Ptr)
	vBdrL := vBdrR := vBdrW
	vBdrT := vBdrB := vBdrH
	oArray := [vPosW, vPosH, vBdrL, vPosW-vBdrR, vBdrT, vPosH-vBdrB]
	vRegion := Format("0-0 {1:}-0 {1:}-{2:} 0-{2:} 0-0" " {3:}-{5:} {4:}-{5:} {4:}-{6:} {3:}-{6:} {3:}-{5:}", oArray*)
	WinSetRegion(vRegion, "ahk_id " hWnd)
	WinShow("ahk_id " hWnd)

	Sleep(vTime)
	DllCall("user32\DestroyWindow", Ptr,hWnd)
}
Borders_WndProc(hWnd, uMsg, wParam, lParam)
{
	return DllCall("user32\DefWindowProc", Ptr,hWnd, UInt,uMsg, UPtr,wParam, Ptr,lParam, Ptr)
}

;==================================================

;;FUNCTIONS - AUXILIARY

;==================================================

JEE_HIconGetDims(hIcon, ByRef vImgW, ByRef vImgH)
{
	vIsMask := 0
	VarSetCapacity(ICONINFO, A_PtrSize=8?32:20, 0)
	DllCall("user32\GetIconInfo", Ptr,hIcon, Ptr,&ICONINFO)
	hBitmapCol := NumGet(&ICONINFO, A_PtrSize=8?24:16, "Ptr") ;hbmColor
	hBitmapMask := NumGet(&ICONINFO, A_PtrSize=8?16:12, "Ptr") ;hbmMask
	if hBitmap := hBitmapCol
	{
		if hBitmapMask
			DllCall("gdi32\DeleteObject", Ptr,hBitmapMask)
	}
	else if hBitmap := hBitmapMask
		vIsMask := 1
	else
		return
	VarSetCapacity(BITMAP, A_PtrSize=8?32:24, 0)
	DllCall("gdi32\GetObject", Ptr,hBitmap, Int,A_PtrSize=8?32:24, Ptr,&BITMAP)
	vImgW := NumGet(&BITMAP, 4, "Int") ;bmWidth
	vImgH := NumGet(&BITMAP, 8, "Int") ;bmHeight
	if vIsMask
		vImgH //= 2
	DllCall("gdi32\DeleteObject", Ptr,hBitmap)
}

;==================================================

JEE_HBitmapGetDims(hBitmap, ByRef vImgW, ByRef vImgH)
{
	VarSetCapacity(BITMAP, A_PtrSize=8?32:24, 0)
	DllCall("gdi32\GetObject", Ptr,hBitmap, Int,A_PtrSize=8?32:24, Ptr,&BITMAP)
	vImgW := NumGet(&BITMAP, 4, "Int") ;bmWidth
	vImgH := NumGet(&BITMAP, 8, "Int") ;bmHeight
}

;==================================================

;e.g. hFont := JEE_FontCreate("Arial", 12, "bius")

;JEE_CreateFont
JEE_FontCreate(vName, vSize, vFontStyle:="", vWeight:="")
{
	vHeight := -DllCall("kernel32\MulDiv", Int,vSize, Int,A_ScreenDPI, Int,72)
	vWidth := 0
	vEscapement := 0
	vOrientation := 0
	vWeight := (vWeight != "") ? vWeight : InStr(vFontStyle, "b") ? 700 : 400
	vItalic := InStr(vFontStyle, "i") ? 1 : 0
	vUnderline := InStr(vFontStyle, "u") ? 1 : 0
	vStrikeOut := InStr(vFontStyle, "s") ? 1 : 0
	vCharSet := 0
	vOutPrecision := 0
	vClipPrecision := 0
	vQuality := 0
	vPitchAndFamily := 0
	vFaceName := vName
	vOutPrecision := 3
	vClipPrecision := 2
	vQuality := 1
	vPitchAndFamily := 34
	return DllCall("gdi32\CreateFont", Int,vHeight, Int,vWidth, Int,vEscapement, Int,vOrientation, Int,vWeight, UInt,vItalic, UInt,vUnderline, UInt,vStrikeOut, UInt,vCharSet, UInt,vOutPrecision, UInt,vClipPrecision, UInt,vQuality, UInt,vPitchAndFamily, Str,vFaceName, Ptr)
}

;==================================================

;vLimW and vLimH if present are used as limits
;JEE_DrawText
JEE_StrGetDim(vText, hFont, ByRef vTextW, ByRef vTextH, vDTFormat:=0x400, vLimW:="", vLimH:="")
{
	;DT_EDITCONTROL := 0x2000 ;DT_NOPREFIX := 0x800
	;DT_CALCRECT := 0x400 ;DT_NOCLIP := 0x100
	;DT_EXPANDTABS := 0x40 ;DT_SINGLELINE := 0x20
	;DT_WORDBREAK := 0x10

	;HWND_DESKTOP := 0
	hDC := DllCall("user32\GetDC", Ptr,0, Ptr)
	hFontOld := DllCall("gdi32\SelectObject", Ptr,hDC, Ptr,hFont, Ptr)
	VarSetCapacity(SIZE, 8, 0)
	vTabLengthText := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	DllCall("gdi32\GetTextExtentPoint32", Ptr,hDC, Str,vTabLengthText, Int,52, Ptr,&SIZE)
	vTabLength := NumGet(&SIZE, 0, "Int") ;cx ;logical units
	vTabLength := Floor((vTabLength/52)+0.5)
	vTabLength := Round(vTabLength*(72/A_ScreenDPI))
	vLen := StrLen(vText)

	VarSetCapacity(DRAWTEXTPARAMS, 20, 0)
	NumPut(20, &DRAWTEXTPARAMS, 0, "UInt") ;cbSize
	NumPut(vTabLength, &DRAWTEXTPARAMS, 4, "Int") ;iTabLength
	;NumPut(0, &DRAWTEXTPARAMS, 8, "Int") ;iLeftMargin
	;NumPut(0, &DRAWTEXTPARAMS, 12, "Int") ;iRightMargin
	NumPut(vLen, &DRAWTEXTPARAMS, 16, "UInt") ;uiLengthDrawn

	VarSetCapacity(RECT, 16, 0)
	if !(vLimW = "")
		NumPut(vLimW, &RECT, 8, "Int")
	if !(vLimH = "")
		NumPut(vLimH, &RECT, 12, "Int")
	DllCall("user32\DrawTextEx", Ptr,hDC, Str,vText, Int,vLen, Ptr,&RECT, UInt,vDTFormat, Ptr,&DRAWTEXTPARAMS)
	DllCall("gdi32\SelectObject", Ptr,hDC, Ptr,hFontOld, Ptr)
	DllCall("user32\ReleaseDC", Ptr,0, Ptr,hDC)

	vTextW := NumGet(&RECT, 8, "Int")
	vTextH := NumGet(&RECT, 12, "Int")
}

;==================================================
Last edited by jeeswg on 30 Oct 2019, 12:14, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: slightly-improved dialogs

08 Jun 2018, 14:01

Big work :o :thumbup: .
If I set Critical or Thread-Priority, and then called the InputBox function, I was unable to close the InputBox.
Critical wrote:Prevents the current thread from being interrupted by other threads, or enables it to be interrupted.
Gui events spawns new thread.
I don't know if there is a solution to this problem.
Turn off critical. For thread priority it seems you cannot restore it afterwards, but it should be rare. Alternatively you can create your dialogs in new threads, then you do not need to consider the settings in the calling thread. Or just do nothing, no one wants a msgbox/inputbox/... in a critical thread anyways. And it is not a problem, it is a feature of threads ;).

Your functions depend on a number of global variables not supplied with the functions, that is annoying.

Quick tips, gui and gui control event callbacks gets passed the object for which the event occured, eg, Gui_Close(Gui). And gui control objects can get their parent gui via the gui property, eg, myGui := myCtrl.gui. In gui event threads, the last found window is set to the gui for which the event occured.

Cheers, and thanks for sharing.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: slightly-improved dialogs

09 Jun 2018, 07:45

- Some other general points:
- If in AHK v1/v2, you could overwrite a built-in function, but have the ability to still refer to that built-in function, that would be incredibly useful.
- Sometimes MsgBox/InputBox get hidden under other windows. I believe that this happens more in Windows 7 than it did in Windows XP.
- All of these dialog functions would benefit from a timeout option e.g. Progress/SplashImage/ToolTip. E.g. display a Progress ('SplashText') window for 3 seconds. I.e. sometimes a function gets interrupted, and the dialog remains on the screen permanently. And some options prevent a SetTimer event, to hide the dialog, from taking place.
- (Creating custom GUI functions in both AHK v1 and v2, that you can use in your libraries without automatically causing persistence. The built-in dialog functions don't cause persistence.)
Big work :o :thumbup: .
- Cool compliment, cheers.
no one wants a msgbox/inputbox/... in a critical thread anyways.
- The main script that I use more than any other, every day, which displays an InputBox with multiple choices, requires Critical/Thread-Priority be set. I may have done this to protect variables being overwritten by other subroutines.
- Anyhow, I might rewrite it to avoid the use of Critical/Thread-Priority, however, I think it's important to point out that built-in GUI functions appear to have an advantage here over custom functions.
Your functions depend on a number of global variables not supplied with the functions, that is annoying.
- Yeah, I've fixed this in the new version below. I did think of it at one point but forgot, I'd been really focused on the Progress/SplashImage functions which took 3 months to finally finish, but which didn't need special variables.
- I've also added in support for & (accelerator keys) in MsgBoxNew. Something that I noticed was missing just now when running the test script.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: slightly-improved dialogs

09 Jun 2018, 08:06

Update.

Examples.

Code: Select all

;AHK v2
;slightly-improved dialogs by jeeswg: sample scripts
;written for AutoHotkey v2 alpha (v2.0-a096)
;[first released: 2018-06-08]
;[updated: 2018-06-09]

;==================================================

;INTRODUCTION

;slightly-improved dialogs by jeeswg:
;1 MsgBox [big font]
;2 InputBox (+ InputBox multi) [big font/multiple fields]
;3 Progress [i.e. SplashText] [no additional features]
;4 SplashImage [option to use IE control]
;5 ToolTip [big font]
;6 Borders [show coloured borders around a rectangle]

;see also:
;7 Find dialog [custom Find dialog with whole word/RegEx options]
;Find dialog with whole word and RegEx support - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=50262

;other:
;(TrayTip) (unchanged)
;(FileSelect) (unchanged)
;(DirSelect) (unchanged)

;==================================================

;NOTES

;NOTES - GENERAL
;there are examples for:
;MsgBox/InputBox, Progress/SplashImage, ToolTip, Borders
;and finally an InputBox example demonstrating problems relating to
;Critical and Thread-Priority
;note: some MsgBox/ToolTip settings can be set in Control Panel e.g. Control Panel\Appearance and Personalization\Personalization

;NOTES - MSGBOX
;considerations: font name/size
;to do+: doesn't handle modal
;note: specify AX_MsgBoxOpt.CustomButtonCount, as 1/2/3 to use custom button names
;note: MsgBox in 'O' mode cannot distinguish between OK and X (close button)
;queries: exact specifications of system MsgBox (the XYWH window/control values appear to be very very unpredictable)

;NOTES - INPUTBOX
;considerations: font name/size, multiple input fields
;to do+: prevent InputBox getting lost under controls (currently it reactivates window, need logic for a general way to do this)
;to do+: enable timed countdown (5, 4, 3, 2, 1, need the user of SetTimer and a custom function that receives a GUI object)
;queries: do nothing until the GUI is dismissed (an alternative to WinWaitClose?)
;queries: how to get default margin values? (note: MarginX and MarginY start off as -1 until set by the user)
;queries: what is the default font? I tested on Windows 7 and it was 'Segoe UI, size 10'
;queries: window/control styles (are -0xFFFFFFFF -E0xFFFFFFFF always necessary?)
;queries: maybe a general solution re. editing dialogs is to allow a mode where you can edit the control before it's visible (could the default MsgBox/InputBox/ToolTip functions have a mode where they are hidden, so that you can edit them before they appear, possibly by temporarily blocking a show window dll function, perhaps this is more difficult with MsgBox which uses the Winapi's MessageBox function)
;queries: what is the exact size of a button (width/height)? something like text dimensions from DrawTextEx plus some arbitrary margin value perhaps
;queries: perhaps an InputBox minus an Edit control (plus extra button options) could provide an alternative to MsgBox
;note: InputBox button arrangement appears to be: MgnX[gap]OK[double gap]Cancel[gap]MgnX

;NOTES - PROGRESS/SPLASHIMAGE
;considerations: handle more image types (anigif, jxr/wdp, svg)
;to do+: font appearances don't quite match
;to do+: 'T': window is owned
;note: progress bars: if a colour is specified for the progress bar, the theme becomes old style
;note: progress bars: PBM_SETSTATE can change some colours
;note: SplashImage: this custom function uses a Static control, the built-in command uses BitBlt to draw an image
;note: source code location (Progress/SplashImage): 'Splash(' in script2.cpp
;note: additional source code location (SplashImage): 'case WM_ERASEBKGND' in script2.cpp
;note: 'FM'/'FS' refer to font size main/sub text
;note: the use of WS_THICKFRAME forces window of a minimum width

;built-in command functionality:
;options (stand-alone): A, B/B1/B2/M/M1/M2, T, Hide
;options (+ numbers): C, CB/CT/CW, FM/FS/WM/WS, P, X/Y/W/H, ZX/ZY/ZW/ZH
;options (+ range): R

;the use of Off/Show/Hide:
;Off/Show: Arg1 (Progress/SplashImage)
;Hide: Arg1 (Progress), Arg2 (SplashImage)

;possibilities for Arg1:
;Arg1: Off
;Arg1: Show
;Arg1: 3:Off [refer to nth window e.g. 3rd window][10 windows in total]
;[SplashImage (2 params): ImageFile, Options]
;Arg1: C:\MyDir\MyFile.png [bmp/(static) gif/jpg(/png/tif/ico)]
;Arg1: MyFile.png [in A_WorkingDir]
;Arg1: (blank) [image unchanged, change text]
;Arg1: HBITMAP:
;Arg1: HICON:
;[Progress (1 param): ProgressParam1 combines Arg1/Options]
;Arg1: bar position

;custom changes:
;supports 'A0' for always-on-top off
;supports 'B0' for borderless
;supports 'HFONT:' for FontName parameter
;supports 'Hide' for SplashImage's first parameter
;supports 'IE' for Internet Explorer_Server control (for more image types)
;supports 'IE###' for Internet Explorer_Server control (for more image types) (and zoom %)
;note: for 'IE': image dimensions and zoom must be specified explicitly, and the image will maintain its proportions

;NOTES - TOOLTIP
;considerations: font name/size, colours
;not using SetWindowTheme, gives an appearance like the ToolTip command: faded colours and rounded corners
;to do+: AHK has special handling when the cursor is near the edge of the screen
;queries: the built-in ToolTip at (X,Y) doesn't always work as expected (at least in Window/Client modes) (the 'OLD' ToolTips should overlap the 'XXX' ToolTips)
;queries: make it exactly central (e.g. by using precise title bar/border dimensions, versus something like creating the window hidden initially and retrieving the size)
;queries: what size margins does the ToolTip command use (otherwise what are the defaults)
;note: source code location (ToolTip): 'ToolTip(' in script2.cpp

;NOTES - BORDERS
;(none)

;NOTES - GENERAL LIMITATIONS IN BUILT-IN FUNCTIONALITY
;- cannot specify no icon
;- cannot specify class
;- problems re. Critical and Thread-Priority in custom GUI functions that built-in functions don't have
;- prefer '0xABCDEF' (or dec) to 'ABCDEF' (consistency, and the avoidance of double quotes)

;==================================================

;SETTINGS

;for Progress/SplashImage:
;include a valid path to an AHK v1 exe
;otherwise certain AHK v1 examples are skipped
vGblPathAhk1 := A_Desktop "\AutoHotkey_1.1.28.02\AutoHotkeyU32.exe"

;for SplashImage:
;include a valid path to an animated gif
;otherwise certain anigif examples are skipped
;e.g. animated gif
;https://autohotkey.com/boards/download/file.php?avatar=198_1381214069.gif
vPathAniGif := A_ScriptDir "\198 [hoppfrosch].gif"

;==================================================

#SingleInstance force
#Persistent
vListSub := "QMIPSTBX" ;GUI subs to execute
;vListSub := "X"

vDoInputBoxCriticalTest := 1
vDoInputBoxThreadPriorityTest := 1

if InStr(vListSub, "Q")
	Gosub SubQuickDemos
if InStr(vListSub, "M")
	Gosub SubMsgBox
if InStr(vListSub, "I")
	Gosub SubInputBox
if InStr(vListSub, "P")
	Gosub SubProgress
if InStr(vListSub, "S")
	Gosub SubSplashImage
if InStr(vListSub, "T")
	Gosub SubToolTip
if InStr(vListSub, "B")
	Gosub SubBorders
if InStr(vListSub, "X")
	Gosub SubCriticalThreadPriority
return

;==================================================

SubQuickDemos:
MsgBoxNew()
AX_MsgBoxOpt.FontSize := 18
MsgBoxNew()
InputBoxNew(,,, "default")
AX_InputBoxOpt.FontSize := 18
InputBoxNew(,,, "default")
ProgressNew("", "hello", "hello"), Sleep(1500), ProgressNew("Off")
SplashImageNew(A_AhkPath), Sleep(1500), SplashImageNew("Off")
ToolTipNew("hello"), Sleep(3000), ToolTipNew()
AX_ToolTipOpt.FontSize := 18
ToolTipNew("hello"), Sleep(3000), ToolTipNew()
Borders(100, 100, 200, 200)
return

;==================================================

SubMsgBox:
AX_MsgBoxOpt.FontSize := 18
vDoButtons := 1
vDoIcons := 1

;test buttons: 7 types: 0 to 6
vList := "O,OC,ARI,YNC,YN,RC,CTC"
if vDoButtons
	Loop Parse, vList, ","
	{
		MsgBoxNew(A_LoopField,, A_LoopField)
		MsgBox(A_LoopField,, A_LoopField)
	}
;note: ARI/YN - Close blocked
;note: result if press Esc (or alt+space, c): OK/Cancel/-/Cancel/-/Cancel/Cancel

;test icons: 4 types
vList := "x?!i"
if vDoIcons
	Loop Parse, vList
	{
		MsgBoxNew("text", "title", "Icon" A_LoopField)
		MsgBox("text", "title", "Icon" A_LoopField)
	}

;test timeout
vRet := MsgBoxNew("test timeout", "title", "T2000")
MsgBoxNew("result: " vRet)

;test return value
vRet := MsgBoxNew("the next MsgBox will state:`r`n" "return value/ErrorLevel/AX_MsgBoxResult", "title", "YNC")
MsgBoxNew("return value: " vRet "`r`n" "ErrorLevel: " ErrorLevel "`r`n" "AX_MsgBoxResult: " AX_MsgBoxResult)

;text short/long strings
;test blanks
MsgBoxNew("the next MsgBoxes will display short/long strings")
MsgBoxNew()
MsgBox()
MsgBoxNew(,, "")
MsgBox(,, "")

vText := "abcdefghijklmnopqrstuvwxyz"
vText2 := ""
Loop 100
	vText2 .= "a`n"
oArray := []
oArray.Push("")
oArray.Push("text")
oArray.Push("text`ntext`ntext")
oArray.Push("text`ntext`ntext`ntext`ntext")
oArray.Push(vText vText vText vText vText)
oArray.Push(vText vText vText vText vText vText vText vText vText vText)
oArray.Push(vText)
oArray.Push(vText "`n" vText "`n" vText)
oArray.Push(vText "`n" vText "`n" vText "`n" vText "`n" vText)
oArray.Push(vText2)
for vKey, vValue in oArray
{
	MsgBoxNew(vValue, "title")
	MsgBox(vValue, "title")
}

MsgBoxNew("the next MsgBoxes will display text with/without icons")
MsgBox(vText vText vText vText vText)
MsgBox(vText vText vText vText vText,, "Icon?")
MsgBoxNew(vText vText vText vText vText)
MsgBoxNew(vText vText vText vText vText,, "Icon?")
MsgBox(vText "`n" vText "`n" vText "`n" vText "`n" vText)
MsgBox(vText "`n" vText "`n" vText "`n" vText "`n" vText,, "Icon?")
MsgBoxNew(vText "`n" vText "`n" vText "`n" vText "`n" vText)
MsgBoxNew(vText "`n" vText "`n" vText "`n" vText "`n" vText,, "Icon?")

;test custom buttons
MsgBoxNew("the next MsgBoxes will display custom buttons")
AX_MsgBoxOpt.CustomButtonCount := 1
MsgBoxNew("custom buttons: 1")
AX_MsgBoxOpt.CustomButtonCount := 2
MsgBoxNew("custom buttons: 2")
AX_MsgBoxOpt.CustomButtonCount := 3
vRet := MsgBoxNew("custom buttons: 3")
MsgBoxNew("return value: " vRet)
AX_MsgBoxOpt.CustomButtonCount := ""
MsgBoxNew("custom buttons: off")

;test custom icon and sound
AX_MsgBoxOpt.HIconDefault := LoadPicture(A_AhkPath, "w32 h32", vType)
AX_MsgBoxOpt.SoundDefault := "C:\Windows\Media\tada.wav"
MsgBoxNew("custom icon and sound")
AX_MsgBoxOpt.HIconDefault := ""
AX_MsgBoxOpt.SoundDefault := "-"
return

;==================================================

SubInputBox:
AX_InputBoxOpt.FontSize := 18
;InputBoxNew("text", "title", "T1000", "default")
vRet := InputBoxNew("text", "title",, "default")
MsgBox(vRet)
;MsgBox(ErrorLevel "`r`n" AX_InputBoxResult)

;InputBoxMulti(["ITEM 1`naaa","ITEM 2`nbbb","ITEM 3`nccc"], vWinTitle:="", vOpt:="", ["AAA","BBB","CCC"])
oArray := InputBoxMulti(["aaa","bbb","ccc"], vWinTitle:="", vOpt:="", ["AAA","BBB","CCC"])
if IsObject(oArray)
	MsgBox(oArray.1 "`r`n" oArray.2 "`r`n" oArray.3 "`r`n" oArray.4)
else
	MsgBox("")
;MsgBox(ErrorLevel "`r`n" AX_InputBoxResult)

InputBox("text", "title",, "default")
InputBoxNew("text", "title",, "default")
InputBox("text", "title", "Password+", "default")
InputBoxNew("text", "title", "Password+", "default")
InputBox("text",,, "default")
InputBoxNew("text",,, "default")
return

SubCriticalThreadPriority:
;can't be used with Critical or Thread-Priority,
;which is an argument for good built-in functions
MsgBox("the next and final custom GUI will demonstrate an issue with custom GUIs and Critical/Thread-Priority, i.e. it can't be closed")
if 1
{
	if vDoInputBoxCriticalTest
		Critical()
	if vDoInputBoxThreadPriorityTest
		Thread("Priority", 1)
	vRet := InputBoxNew("text", "title",, "default")
	MsgBox(vRet)
}
return

;==================================================

SubProgress:
;vGblPathAhk1 := A_Desktop "\AutoHotkey_1.1.28.02\AutoHotkeyU32.exe"
vDelay := 3000
vGblDelayAhk1 := vDelay
;note: the 'vGbl' variables are for use with the diagnostic ProgressAhk1 function

;vTemp := "hello"
vTemp := "abcdefghijklmnopqrstuvwxyz"
vText := vTemp "`n" vTemp "`n" vTemp "`n" vTemp "`n" vTemp

vFormat := "zh0 b c0 fs18" ;no border + left
ProgressAhk1(vFormat, vText)
ProgressNew(vFormat, vText), Sleep(vDelay), ProgressNew("Off")
vFormat := "zh0 b1 fs18" ;border + centre
ProgressAhk1(vFormat, vText)
ProgressNew(vFormat, vText), Sleep(vDelay), ProgressNew("Off")
vFormat := "b2"
ProgressAhk1(vFormat, vText)
ProgressNew(vFormat, vText), Sleep(vDelay), ProgressNew("Off")
return

if 1
{
	ProgressAhk1("", vText)
	ProgressNew("", vText), Sleep(vDelay), ProgressNew("Off")
	ProgressAhk1("fs4", vText)
	ProgressNew("fs4", vText), Sleep(vDelay), ProgressNew("Off")
	ProgressAhk1("fs24", vText)
	ProgressNew("fs24", vText), Sleep(vDelay), ProgressNew("Off")
}

ProgressAhk1("P30", "SubText", "MainText", "WinTitle")
ProgressNew("P30", "SubText", "MainText", "WinTitle"), Sleep(vDelay), ProgressNew("Off")

ProgressAhk1("P30 CWFF0000 CBFFFF00 CT0000FF", "SubText", "MainText", "WinTitle")
ProgressNew("P30 CW0xFF0000 CB0xFFFF00 CT0x0000FF", "SubText", "MainText", "WinTitle"), Sleep(vDelay), ProgressNew("Off")

ProgressAhk1("P7500 R0-10000 CWFF0000 CBFFFF00 CT0000FF", "SubText", "MainText", "WinTitle")
ProgressNew("P7500 R0-10000 CW0xFF0000 CB0xFFFF00 CT0x0000FF", "SubText", "MainText", "WinTitle"), Sleep(vDelay)
ProgressNew(10000), Sleep(vDelay), ProgressNew("Off")
return

;==================================================

SubSplashImage:
;vGblPathAhk1 := A_Desktop "\AutoHotkey_1.1.28.02\AutoHotkeyU32.exe"
vDelay := 1500
vGblDelayAhk1 := vDelay
;e.g. animated gif
;https://autohotkey.com/boards/download/file.php?avatar=198_1381214069.gif
;vPathAniGif := A_ScriptDir "\198 [hoppfrosch].gif"
;note: the 'vGbl' variables are for use with the diagnostic SplashImageAhk1 function
vPath := A_AhkPath

if 1
{
	SplashImageAhk1(vPath)
	SplashImageNew(vPath), Sleep(vDelay), SplashImageNew("Off")

	SplashImageAhk1(vPath, "M2 X0 Y0", "SubText", "MainText", "WinTitle")
	SplashImageNew(vPath, "M2 X0 Y0", "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageAhk1(vPath, "M1 X200 Y200", "SubText", "MainText", "WinTitle")
	SplashImageNew(vPath, "M1 X200 Y200", "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageAhk1(vPath, "M X400 Y400", "SubText", "MainText", "WinTitle")
	SplashImageNew(vPath, "M X400 Y400", "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageAhk1(vPath, "B X600 Y600", "SubText", "MainText", "WinTitle")
	SplashImageNew(vPath, "B X600 Y600", "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")

	SplashImageNew("1:" vPath, "M2 X0 Y0", "SubText", "MainText", "WinTitle")
	SplashImageNew("2:" vPath, "M1 X200 Y200", "SubText", "MainText", "WinTitle")
	SplashImageNew("3:" vPath, "M X400 Y400", "SubText", "MainText", "WinTitle")
	SplashImageNew("4:" vPath, "B X600 Y600", "SubText", "MainText", "WinTitle")
	Sleep(vDelay)
	SplashImageNew("1:Off")
	SplashImageNew("3:Off")
	Sleep(vDelay)
	SplashImageNew("2:Off")
	SplashImageNew("4:Off")
}

if 1
{
	hIcon := LoadPicture(vPath,, vImgType)
	;compare load hIcon v. load path
	;SplashImageAhk1("HICON:" hIcon,, "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageNew("HICON:" hIcon,, "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageAhk1(vPath,, "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageNew(vPath,, "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")

	hIcon := LoadPicture("shell32.dll", "icon35", vImgType)
	;SplashImageAhk1("HICON:" hIcon,, "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageNew("HICON:" hIcon,, "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
}

if FileExist(vPathAniGif)
if 1
{
	vImgW := 80, vImgH := 63
	vImgW += 20, vImgH += 30 ;add margins
	vImgPos := Format("ZW{} ZH{}", vImgW, vImgH)
	SplashImageNew(vPathAniGif, "IE " vImgPos, "SubText", "MainText", "WinTitle"), Sleep(vDelay*1.6), SplashImageNew("Off")
	vImgW *= 3, vImgH *= 3 ;zoom
	vImgPos := Format("ZW{} ZH{}", vImgW, vImgH)
	SplashImageNew(vPathAniGif, "IE300 " vImgPos, "SubText", "MainText", "WinTitle"), Sleep(vDelay*1.6), SplashImageNew("Off")

	SplashImageNew(vPathAniGif,, "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageNew(vPathAniGif, "ZW300", "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageNew(vPathAniGif, "ZH300", "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageNew(vPathAniGif, "ZW600 ZH300", "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageNew(vPathAniGif, "ZW300 ZH300", "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	SplashImageNew(vPathAniGif, "ZW300 ZH-1", "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
}

if 1
{
	SplashImageNew(vPath,, "SubText", "MainText", "WinTitle")
	Sleep(vDelay)
	SplashImageNew(, "Hide")
	Sleep(vDelay)
	SplashImageNew("Show")
	Sleep(vDelay)
	SplashImageNew("Off")
	Sleep(vDelay)

	hBitmap := LoadPicture(vPath, "W256 H256")
	SplashImageNew("HBITMAP:" hBitmap,, "", "", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	hIcon := LoadPicture(vPath, "W256 H256", vImgType)
	SplashImageNew("HICON:" hIcon,, "", "", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")

	hBitmap := LoadPicture(vPath, "W256 H256")
	SplashImageNew("HBITMAP:" hBitmap,, "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
	hIcon := LoadPicture(vPath, "W256 H256", vImgType)
	SplashImageNew("HICON:" hIcon,, "SubText", "MainText", "WinTitle"), Sleep(vDelay), SplashImageNew("Off")
}
return

;==================================================

SubToolTip:
;test standard ToolTips
if 0
{
	CoordMode("ToolTip", "Screen")
	ToolTip("Client ___ Window ___ SCREEN",,, 1)
	CoordMode("ToolTip", "Window")
	ToolTip("Client ___ WINDOW",,, 2)
	CoordMode("ToolTip", "Client")
	ToolTip("CLIENT",,, 3)
	Sleep(3000)

	CoordMode("ToolTip", "Screen")
	ToolTip("SCREEN", 300, 300, 1)
	CoordMode("ToolTip", "Window")
	ToolTip("WINDOW", 300, 300, 2)
	CoordMode("ToolTip", "Client")
	ToolTip("CLIENT", 300, 300, 3)
	Sleep(3000)

	ToolTip(,,, 1)
	ToolTip(,,, 2)
	ToolTip(,,, 3)
}

CoordMode("ToolTip", "Screen")
CoordMode("ToolTip", "Window")
CoordMode("ToolTip", "Client")
ToolTip("XXX 300 300", 300, 300, 4)
ToolTip("XXX",,, 5)

AX_ToolTipOpt.FontSize := 18
;vText := "   hello world   `r`nhello world`r`nhello world"
vText := "hello world`r`nhello world`r`nhello world"
vDelay := 1500

ToolTipNew(vText)
Sleep(vDelay)
ToolTipNew()

ToolTipNew(vText, "c", "c")
Sleep(vDelay)
ToolTipNew()

ToolTipNew(vText, "c", "c",, 0x0000FF, 0xFFFF00)
Sleep(vDelay)
ToolTipNew()

ToolTipNew(vText, "c", "c",, 0xFFFF00, 0x0000FF)
Sleep(vDelay)
ToolTipNew()

ToolTipNew(vText, 100, 100, 1)
ToolTipNew(vText, 200, 200, 2)
ToolTipNew(vText, 300, 300, 3)
Sleep(vDelay)
;Sleep(vDelay * 3)
ToolTipNew(,,, 1)
ToolTipNew(,,, 2)
ToolTipNew(,,, 3)

AX_ToolTipOpt.FontSize := 9
ToolTipNew(vText, "c", "c")
ToolTip(vText)
Sleep(vDelay)
ToolTipNew()
ToolTip()

;confirm they overlap at (X,Y)
ToolTipNew("NEW" A_Space A_Space A_CoordModeToolTip)
ToolTip("OLD")
Sleep(vDelay)
ToolTipNew()
ToolTip()

;confirm they overlap when (X,Y) not specified
ToolTipNew("NEW" A_Space A_Space A_CoordModeToolTip, 300, 300)
ToolTip("OLD", 300, 300)
Sleep(vDelay)
ToolTipNew()
ToolTip()

ToolTip(,,, 4)
ToolTip(,,, 5)
return

;w:: ;ToolTip - get margins
;ToolTip via AutoHotkey ToolTip command reports 0,0,0,0
VarSetCapacity(RECT, 16, 0)
SendMessage(0x41B,, &RECT,, "ahk_class tooltips_class32") ;TTM_GETMARGIN := 0x41B
vOutput := NumGet(&RECT, 0, "UInt")
vOutput .= "," NumGet(&RECT, 4, "UInt")
vOutput .= "," NumGet(&RECT, 8, "UInt")
vOutput .= "," NumGet(&RECT, 12, "UInt")
MsgBox(vOutput)
return

;==================================================

SubBorders:
;draw borders around the active window/control
hWnd := WinGetID("A")
vCtlClassNN := ControlGetFocus("ahk_id " hWnd)
hWnd := ControlGetHwnd(vCtlClassNN, "ahk_id " hWnd)
WinGetPos(vCtlX, vCtlY, vCtlW, vCtlH, "ahk_id " hWnd)
Borders(vCtlX, vCtlY, vCtlW, vCtlH)
return

;==================================================
Functions.

Code: Select all

;AHK v2
;slightly-improved dialogs by jeeswg: functions
;written for AutoHotkey v2 alpha (v2.0-a096)
;[first released: 2018-06-08]
;[updated: 2018-06-09]

;==================================================

;;FUNCTIONS - INIT
;MsgBoxNew_Init()
;InputBoxNew_Init()
;ToolTipNew_Init()
;;FUNCTIONS - MSGBOX
;MsgBoxNew(oParams*) ;vText, vWinTitle, vOpt
;MsgBoxNew_Close(oGui, vBtn)
;;FUNCTIONS - INPUTBOX
;InputBoxNew(oParams*) ;vText, vWinTitle, vOpt, vDefault
;InputBoxNew_Close(oGui, vBtn)
;InputBoxNew_Size(oGui)
;;FUNCTIONS - INPUTBOX MULTI
;InputBoxMulti(oParams*) ;oText, vWinTitle, vOpt, oDefault
;;FUNCTIONS - PROGRESS/SPLASHIMAGE (AHK V1 DIAGNOSTIC)
;Progress(ProgressParam1, SubText:="", MainText:="", WinTitle:="", FontName:="")
;SplashImage(ImageFile:="", Options:="", SubText:="", MainText:="", WinTitle:="", FontName:="")
;ProgressAhk1(oParams*)
;SplashImageAhk1(oParams*)
;SplashAhk1(vFunc, oParams*)
;;FUNCTIONS - PROGRESS/SPLASHIMAGE (AHK V2)
;ProgressNew(vParam1, vSubText:="", vMainText:="", vWinTitle:=" ", vFontName:="")
;SplashImageNew(vImageFile:="", vOpt:="", vSubText:="", vMainText:="", vWinTitle:=" ", vFontName:="")
;;FUNCTIONS - TOOLTIP
;ToolTipNew(vText:="", vPosX:="", vPosY:="", vWhichToolTip:=1, vColTxRGB:="", vColBkRGB:="")
;;FUNCTIONS - BORDERS
;Borders(vPosX, vPosY, vPosW:=0, vPosH:=0, vTime:=1000, vColRGB:=0xFFFF00, vBdrW:=5, vBdrH:=5)
;Borders_WndProc(hWnd, uMsg, wParam, lParam)

;;FUNCTIONS - AUXILIARY
;JEE_HIconGetDims(hIcon, ByRef vImgW, ByRef vImgH)
;JEE_HBitmapGetDims(hBitmap, ByRef vImgW, ByRef vImgH)
;JEE_FontCreate(vName, vSize, vFontStyle:="", vWeight:="")
;JEE_StrGetDim(vText, hFont, ByRef vTextW, ByRef vTextH, vDTFormat:=0x400, vLimW:="", vLimH:="")

;==================================================

global AX_DlgTitle
global AX_ScriptNameNoExt
global AX_MsgBoxResult
global AX_MsgBoxOpt ;object
MsgBoxNew_Init()
{
	static vDummy := MsgBoxNew_Init()
	SplitPath(A_ScriptFullPath,,,, AX_ScriptNameNoExt)
	AX_DlgTitle := AX_ScriptNameNoExt

	AX_MsgBoxOpt := {}
	AX_MsgBoxOpt.Prompt0 := "Press OK to continue."
	AX_MsgBoxOpt.Prompt1 := ""
	AX_MsgBoxOpt.TextAbort := "&Abort"
	AX_MsgBoxOpt.TextCancel := "Cancel"
	AX_MsgBoxOpt.TextContinue := "&Continue"
	AX_MsgBoxOpt.TextIgnore := "&Ignore"
	AX_MsgBoxOpt.TextNo := "&No"
	AX_MsgBoxOpt.TextOK := "OK"
	AX_MsgBoxOpt.TextRetry := "&Retry"
	AX_MsgBoxOpt.TextTryAgain := "&Try Again"
	AX_MsgBoxOpt.TextYes := "&Yes"
	AX_MsgBoxOpt.TextButton1 := "Btn1"
	AX_MsgBoxOpt.TextButton2 := "Btn2"
	AX_MsgBoxOpt.TextButton3 := "Btn3"
	AX_MsgBoxOpt.CustomButtonCount := ""
	AX_MsgBoxOpt.FontName := "Segoe UI"
	AX_MsgBoxOpt.FontSize := 10
	AX_MsgBoxOpt.FontWeight := 400
	;AX_MsgBoxOpt.HFont := hFont
	AX_MsgBoxOpt.StylesWin := "+0x94C803C5 +E0x00010101"
	AX_MsgBoxOpt.StylesButton1 := "+0x50030001 +E0x00000004"
	AX_MsgBoxOpt.StylesButton2 := "+0x50010000 +E0x00000004"
	AX_MsgBoxOpt.StylesButton3 := "+0x50010000 +E0x00000004"
	AX_MsgBoxOpt.StylesStatic := "+0x50022080 +E0x00000004"
	AX_MsgBoxOpt.StylesIcon := "+0x50020003 +E0x00000004"
	AX_MsgBoxOpt.WinMaxWidth := 800
	AX_MsgBoxOpt.HIconDefault := ""
	AX_MsgBoxOpt.HIconAsterisk := ""
	AX_MsgBoxOpt.HIconExclamation := ""
	AX_MsgBoxOpt.HIconHand := ""
	AX_MsgBoxOpt.HIconQuestion := ""
	AX_MsgBoxOpt.SoundDefault := "-"
	AX_MsgBoxOpt.SoundAsterisk := ""
	AX_MsgBoxOpt.SoundExclamation := ""
	AX_MsgBoxOpt.SoundHand := ""
	AX_MsgBoxOpt.SoundQuestion := ""
}

global AX_DlgTitle
global AX_ScriptNameNoExt
global AX_InputBoxResult
global AX_InputBoxOpt ;object
InputBoxNew_Init()
{
	static vDummy := InputBoxNew_Init()
	SplitPath(A_ScriptFullPath,,,, AX_ScriptNameNoExt)
	AX_DlgTitle := AX_ScriptNameNoExt

	AX_InputBoxOpt := {}
	AX_InputBoxOpt.Prompt0 := ""
	AX_InputBoxOpt.Prompt1 := ""
	AX_InputBoxOpt.TextButton1 := "OK"
	AX_InputBoxOpt.TextButton2 := "Cancel"
	AX_InputBoxOpt.FontName := "Segoe UI"
	AX_InputBoxOpt.FontSize := 10
	AX_InputBoxOpt.FontWeight := 400
	;AX_InputBoxOpt.HFont := hFont
	AX_InputBoxOpt.StylesWin := "+0x94CC0A4C +E0x00010100"
	AX_InputBoxOpt.StylesEdit := "+0x50010080 +E0x00000204"
	AX_InputBoxOpt.StylesEditPW := "+0x500100A0 +E0x00000204"
	AX_InputBoxOpt.StylesButton1 := "+0x50010001 +E0x00000004"
	AX_InputBoxOpt.StylesButton2 := "+0x50010000 +E0x00000004"
	AX_InputBoxOpt.StylesStatic := "+0x50020000 +E0x00000004"
}

global AX_DlgTitle
global AX_ScriptNameNoExt
global AX_ToolTipOpt ;object
ToolTipNew_Init()
{
	static vDummy := ToolTipNew_Init()
	SplitPath(A_ScriptFullPath,,,, AX_ScriptNameNoExt)
	AX_DlgTitle := AX_ScriptNameNoExt

	AX_ToolTipOpt := {}
	AX_ToolTipOpt.FontName := "Segoe UI"
	AX_ToolTipOpt.FontSize := 9
	AX_ToolTipOpt.FontWeight := 400
	;AX_ToolTipOpt.ColTxRGB := 0x404040
	;AX_ToolTipOpt.ColBkRGB := 0xF0F0F0
	AX_ToolTipOpt.ColTxRGB := 0x000000
	AX_ToolTipOpt.ColBkRGB := 0xFFFFFF
	AX_ToolTipOpt.SetWindowTheme := "" ;like AutoHotkey ToolTip command (faded colours and rounded corners)
	AX_ToolTipOpt.SetWindowTheme := 0
}

;==================================================

;FUNCTIONS - MSGBOX

MsgBoxNew(oParams*) ;vText, vWinTitle, vOpt
{
	static oType := {"OK":0, "O":0, "OKCancel":1, "O/C":1, "OC":1, "AbortRetryIgnore":2, "A/R/I":2, "ARI":2
	, "YesNoCancel":3, "Y/N/C":3, "YNC":3, "YesNo":4, "Y/N":4, "YN":4, "RetryCancel":5, "R/C":5, "RC":5
	, "CancelTryAgainContinue":6, "C/T/C":6, "CTC":6, "Iconx":16, "Icon?":32, "Icon!":48, "Iconi":64
	, "Default2":256, "Default3":512, "Default4":768}
	static hIconHand := DllCall("user32\LoadIcon", Ptr,0, Ptr,32513, Ptr) ;IDI_HAND := 32513
	static hIconQuestion := DllCall("user32\LoadIcon", Ptr,0, Ptr,32514, Ptr) ;IDI_QUESTION := 32514
	static hIconExclamation := DllCall("user32\LoadIcon", Ptr,0, Ptr,32515, Ptr) ;IDI_EXCLAMATION := 32515
	static hIconAsterisk := DllCall("user32\LoadIcon", Ptr,0, Ptr,32516, Ptr) ;IDI_ASTERISK := 32516
	global oMsgBoxRet

	vText := !oParams.Length() ? AX_MsgBoxOpt.Prompt0 : oParams.HasKey(1) ? oParams.1 : AX_MsgBoxOpt.Prompt1
	vWinTitle := oParams.HasKey(2) ? oParams.2 : AX_DlgTitle
	vOpt := oParams.3

	if !IsObject(oMsgBoxRet)
		oMsgBoxRet := []
	hWnd := WinExist("A")

	vDHW := A_DetectHiddenWindows
	DetectHiddenWindows(1)
	vPosWin := "", vTimeout := "", vType := 0, vHasIcon := 0, vHasIconCustom := 0
	vMgnX := 5, vMgnY := 5
	vWinW := 368, vWinH := 166

	Loop Parse, vOpt, " `t"
	{
		if (SubStr(A_LoopField, 1, 1) = "W")
			vWinW := SubStr(A_LoopField, 2)
		else if (SubStr(A_LoopField, 1, 1) = "H")
			vWinH := SubStr(A_LoopField, 2)

		if (A_LoopField ~= "i)^[XYWH]\d")
			vPosWin .= A_LoopField " "
		else if (SubStr(A_LoopField, 1, 1) = "T")
			vTimeout := SubStr(A_LoopField, 2)
		else if ((A_LoopField is "number") && (vTemp := Abs(A_LoopField))
		|| (vTemp := oType[A_LoopField]))
			vType |= vTemp
	}

	vStyleReset := "-0xFFFFFFFF -E0xFFFFFFFF "

	vBtnClose := 0
	if (AX_MsgBoxOpt.CustomButtonCount = 1)
		vBtnList := "Button1"
	else if (AX_MsgBoxOpt.CustomButtonCount = 2)
		vBtnList := "Button1,Button2"
	else if (AX_MsgBoxOpt.CustomButtonCount = 3)
		vBtnList := "Button1,Button2,Button3"
	else if (vType & 0xF = 0) ;O
		vBtnList := "OK", vBtnClose := 1
	else if (vType & 0xF = 1) ;OC
		vBtnList := "OK,Cancel", vBtnClose := 2
	else if (vType & 0xF = 2) ;ARI
		vBtnList := "Abort,Retry,Ignore", vDisableClose := 1
	else if (vType & 0xF = 3) ;YNC
		vBtnList := "Yes,No,Cancel", vBtnClose := 3
	else if (vType & 0xF = 4) ;YN
		vBtnList := "Yes,No", vDisableClose := 1
	else if (vType & 0xF = 5) ;RC
		vBtnList := "Retry,Cancel", vBtnClose := 2
	else if (vType & 0xF = 6) ;CTC
		vBtnList := "Cancel,TryAgain,Continue", vBtnClose := 1
	StrReplace(vBtnList, ",",, vBtnCount), vBtnCount += 1
	oTemp := StrSplit(vBtnList, ",")
	vTextBtn0 := "Cancel"
	Loop vBtnCount
		vTextBtn%A_Index% := AX_MsgBoxOpt["Text" oTemp[A_Index]]

	if (vType & 0xF0 = 0x10) ;Iconx
		vTypeIcon := "Hand", vHasIcon := 1
	else if (vType & 0xF0 = 0x20) ;Icon?
		vTypeIcon := "Question", vHasIcon := 1
	else if (vType & 0xF0 = 0x30) ;Icon!
		vTypeIcon := "Exclamation", vHasIcon := 1
	if (vType & 0xF0 = 0x40) ;Iconi
		vTypeIcon := "Asterisk", vHasIcon := 1

	if vHasIcon
	&& !(hIcon := AX_MsgBoxOpt["HIcon" vTypeIcon])
		hIcon := hIcon%vTypeIcon%
	if !vHasIcon
	&& (hIcon := AX_MsgBoxOpt.HIconDefault)
		vHasIconCustom := 1

	vFontName := AX_MsgBoxOpt.FontName
	vFontSize := AX_MsgBoxOpt.FontSize
	vFontWeight := AX_MsgBoxOpt.FontWeight
	vStylesWin := vStyleReset AX_MsgBoxOpt.StylesWin
	vStylesButton1 := AX_MsgBoxOpt.StylesButton1
	vStylesButton2 := AX_MsgBoxOpt.StylesButton2
	vStylesButton3 := AX_MsgBoxOpt.StylesButton3
	vStylesStatic := AX_MsgBoxOpt.StylesStatic
	vStylesIcon := AX_MsgBoxOpt.StylesIcon

	vFontOpt := "s" vFontSize
	if AX_MsgBoxOpt.HFont
		hFont := AX_MsgBoxOpt.HFont
	else
		hFont := JEE_FontCreate(vFontName, vFontSize, "", vFontWeight)
	JEE_StrGetDim("Try Again", hFont, vBtn0W, vBtn0H, vDTFormat:=0x400, vLimW:="", vLimH:="") ;minimum width
	JEE_StrGetDim(vTextBtn1, hFont, vBtn1W, vBtn1H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	JEE_StrGetDim(vTextBtn2, hFont, vBtn2W, vBtn2H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	JEE_StrGetDim(vTextBtn3, hFont, vBtn3W, vBtn3H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	vBtnW := Max(vBtn0W, vBtn1W, vBtn2W, vBtn3W) + 16
	vBtnH := Max(vBtn0H, vBtn1H, vBtn2H, vBtn3H) + 7

	if !InStr(vPosWin, "W") || !InStr(vPosWin, "H")
	{
		if AX_MsgBoxOpt.WinMaxWidth
			vLimW := AX_MsgBoxOpt.WinMaxWidth - 2*vMgnX
		else
			vLimW := A_ScreenWidth - 2*vMgnX
		JEE_StrGetDim(vText, hFont, vTextW, vTextH, vDTFormat:=0x2410, vLimW, vLimH:="")
		vWinW := vTextW + 2*vMgnX
		vWinH := vTextH + vBtnH + 3*vMgnY
		vWinW := Max(vWinW, 368)
		vWinH := Max(vWinH, 166)
	}

	if vHasIcon || vHasIconCustom
	{
		vWinW += vMgnX + 32
		vOffsetX := vMgnX + 32
	}
	else
		vOffsetX := 0
	if !InStr(vPosWin, "W")
		vPosWin .= " W" vWinW
	if !InStr(vPosWin, "H")
		vPosWin .= " H" vWinH

	vEditW := vWinW - 2*vMgnX - vOffsetX
	vGap := Round((vWinW - vBtnCount*vBtnW - 2*vMgnX)/(vBtnCount*2))
	;[][BTN1][]
	;[][BTN1][][][BTN2][]
	;[][BTN1][][][BTN2][][][BTN3][]
	;centred buttons:
	;vBtn1X := vMgnX + vGap
	;vBtn2X := vMgnX + 3*vGap + vBtnW
	;vBtn3X := vMgnX + 5*vGap + 2*vBtnW
	;offset from right
	vBtn1X := vWinW - vBtnCount*vMgnX - vBtnCount*vBtnW
	vBtn2X := vWinW - (vBtnCount-1)*vMgnX - (vBtnCount-1)*vBtnW
	vBtn3X := vWinW - (vBtnCount-2)*vMgnX - (vBtnCount-2)*vBtnW
	vBtnY := vWinH - vMgnY - vBtnH
	;vEditY := vBtnY - vMgnY - vBtnH
	;vStcH := vEditY - vMgnY
	vStcH := vBtnY - vMgnY
	;vPosEdit := Format("X{} Y{} W{} H{}", vMgnX, vEditY, vEditW, vBtnH)
	vPosBtn1 := Format("X{} Y{} W{} H{}", vBtn1X, vBtnY, vBtnW, vBtnH)
	vPosBtn2 := Format("X{} Y{} W{} H{}", vBtn2X, vBtnY, vBtnW, vBtnH)
	vPosBtn3 := Format("X{} Y{} W{} H{}", vBtn3X, vBtnY, vBtnW, vBtnH)
	vPosStc := Format("X{} Y{} W{} H{}", vMgnX+vOffsetX, vMgnY, vEditW, vStcH)
	vPosStcIcon := Format("X{} Y{} W{} H{}", vMgnX, vMgnY, 32, 32)

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	;WS_POPUPWINDOW := 0x80880000 ;composite style (WS_POPUP | WS_BORDER | WS_SYSMENU)
	;WS_VISIBLE := 0x10000000
	;WS_CLIPSIBLINGS := 0x4000000
	;WS_CAPTION := 0xC00000 ;composite style (WS_BORDER | WS_DLGFRAME)
	;WS_THICKFRAME := 0x40000
	;WS_EX_CONTROLPARENT := 0x10000
	;WS_EX_WINDOWEDGE := 0x100
	oGui := GuiCreate(vStylesWin, vWinTitle)
	oGui.SetFont(vFontOpt, vFontName)
	;types: O,OC,ARI,YNC,YN,RC,CTC
	;press Esc or alt+space, c: OK/Cancel/-/Cancel/-/Cancel/Cancel
	oGui.OnEvent("Close", Func("MsgBoxNew_Close").Bind(oGui, vTextBtn%vBtnClose%))
	if !vDisableClose
		oGui.OnEvent("Escape", Func("MsgBoxNew_Close").Bind(oGui, vTextBtn%vBtnClose%))
	hGui := oGui.hWnd

	;SC_RESTORE := 0xF120 ;SC_MOVE := 0xF010
	;SC_SIZE := 0xF000 ;SC_MINIMIZE := 0xF020
	;SC_MAXIMIZE := 0xF030 ;SC_CLOSE := 0xF060
	hSysMenu := DllCall("user32\GetSystemMenu", Ptr,hGui, Int,0, Ptr)
	vList := "0xF120,0xF000,0xF020,0xF030,0,"
	Loop Parse, vList, ","
		DllCall("user32\DeleteMenu", Ptr,hSysMenu, UInt,A_LoopField, UInt,0x0) ;MF_BYCOMMAND := 0x0
	if vDisableClose
		DllCall("user32\DeleteMenu", Ptr,hSysMenu, UInt,0xF060, UInt,0x0) ;MF_BYCOMMAND := 0x0
	;DllCall("user32\EnableMenuItem", Ptr,hSysMenu, UInt,0xF060, UInt,0x3) ;MF_DISABLED := 0x2, MF_GRAYED := 0x1

	oGui.MarginX := vMgnX
	oGui.MarginY := vMgnY

	;oGui.Add("Edit", vStylesEdit " " vPosEdit, vDefault)

	Loop vBtnCount
	{
		oBtn%A_Index% := oGui.Add("Button", vStylesButton%A_Index% " " vPosBtn%A_Index%, vTextBtn%A_Index%)
		oBtn%A_Index%.OnEvent("Click", Func("MsgBoxNew_Close").Bind(oGui, "*" vTextBtn%A_Index%))
	}
	if vHasIcon | vHasIconCustom
	{
		oGui.Add("Picture", vStylesIcon " " vPosStcIcon)
		SendMessage(0x172, 1, hIcon, "Static1", "ahk_id " hGui) ;STM_SETIMAGE := 0x172 ;IMAGE_ICON := 1 ;IMAGE_BITMAP := 0
	}
	oGui.Add("Text", vStylesStatic " " vPosStc, vText)

	if (vType & 0x200)
		ControlFocus("Button3", "ahk_id " hGui)
	else if (vType & 0x100)
		ControlFocus("Button2", "ahk_id " hGui)
	else
		ControlFocus("Button1", "ahk_id " hGui)

	;oGui.OnEvent("Size", "MsgBoxNew_Size")

	if !(vTimeout = "")
	{
		BoundFuncTemp := Func("MsgBoxNew_Close").Bind(oGui, "Timeout")
		SetTimer(BoundFuncTemp, -vTimeout)
	}

	oGui.Show(vPosWin)

	DetectHiddenWindows(vDHW)

	if vHasIcon
	{
		vPathSound := AX_MsgBoxOpt["Sound" vTypeIcon]
		if (vPathSound = "")
			;vPathSound := RegRead("HKEY_CURRENT_USER\AppEvents\Schemes\Apps\.Default\System" vTypeIcon "\.Current")
			vPathSound := "*" ({Hand:16, Question:32, Exclamation:48, Asterisk:64}[vTypeIcon])
	}
	else
	{
		vPathSound := AX_MsgBoxOpt.SoundDefault
		if (vPathSound = "")
			;vPathSound := RegRead("HKEY_CURRENT_USER\AppEvents\Schemes\Apps\.Default\.Default\.Current")
			vPathSound := "*-1"
	}
	if (SubStr(vPathSound, 1, 1) = "*")
	|| (InStr(vPathSound, "\") && FileExist(vPathSound))
		SoundPlay(vPathSound)

	WinWaitClose("ahk_id " hGui)

	;if WinExist("ahk_id " hWnd)
	;	WinActivate("ahk_id " hWnd)

	if oMsgBoxRet.Length()
		return oMsgBoxRet[hGui]
	else
		return
}
MsgBoxNew_Close(oGui, vBtn)
{
	global oMsgBoxRet
	try hWnd := oGui.hWnd
	catch
		vBtn := ""
	vBtn := StrReplace(vBtn, "&")
	vBtn := StrReplace(vBtn, " ") ;e.g. 'Try Again' to 'TryAgain'
	;MsgBox(A_ThisFunc " " hWnd " " vBtn)
	;Abort,Cancel,Continue,Ignore,No,OK,Retry,TryAgain,Yes
	if (SubStr(vBtn, 1, 1) = "*")
	{
		oMsgBoxRet[hWnd] := SubStr(vBtn, 2)
		ErrorLevel := 0
		AX_MsgBoxResult := SubStr(vBtn, 2)
	}
	;Timeout
	else
	{
		oMsgBoxRet[hWnd] := vBtn
		ErrorLevel := 1
		AX_MsgBoxResult := vBtn
	}
	try oGui.Destroy
}

;==================================================

;FUNCTIONS - INPUTBOX

InputBoxNew(oParams*) ;vText, vWinTitle, vOpt, vDefault
{
	global oInputBoxRet

	vText := !oParams.Length() ? AX_InputBoxOpt.Prompt0 : oParams.HasKey(1) ? oParams.1 : AX_InputBoxOpt.Prompt1
	vWinTitle := oParams.HasKey(2) ? oParams.2 : AX_DlgTitle
	vOpt := oParams.3
	vDefault := oParams.4

	if !IsObject(oInputBoxRet)
		oInputBoxRet := []
	hWnd := WinExist("A")

	DetectHiddenWindows(1)
	vPosWin := "", vTimeout := "", vPassChar := ""
	vMgnX := 5, vMgnY := 5
	vWinW := 368, vWinH := 166

	Loop Parse, vOpt, " `t"
	{
		if (SubStr(A_LoopField, 1, 1) = "W")
			vWinW := SubStr(A_LoopField, 2)
		else if (SubStr(A_LoopField, 1, 1) = "H")
			vWinH := SubStr(A_LoopField, 2)

		if (A_LoopField ~= "i)^[XYWH]")
			vPosWin .= A_LoopField " "
		else if (SubStr(A_LoopField, 1, 1) = "T")
			vTimeout := SubStr(A_LoopField, 2)
		else if (A_LoopField = "Password")
			vHasPassChar := 1
		else if (SubStr(A_LoopField, 1, 8) = "Password")
		&& (StrLen(A_LoopField) > 8)
			vPassChar := SubStr(A_LoopField, 9, 1), vHasPassChar := 1
	}
	if !InStr(vPosWin, "W")
		vPosWin .= " W" vWinW
	if !InStr(vPosWin, "H")
		vPosWin .= " H" vWinH

	vStyleReset := "-0xFFFFFFFF -E0xFFFFFFFF "

	vTextBtn1 := AX_InputBoxOpt.TextButton1
	vTextBtn2 := AX_InputBoxOpt.TextButton2
	vFontName := AX_InputBoxOpt.FontName
	vFontSize := AX_InputBoxOpt.FontSize
	vFontWeight := AX_InputBoxOpt.FontWeight
	vStylesWin := vStyleReset AX_InputBoxOpt.StylesWin
	vStylesEdit := AX_InputBoxOpt.StylesEdit
	vStylesEditPW := AX_InputBoxOpt.StylesEditPW
	vStylesButton1 := AX_InputBoxOpt.StylesButton1
	vStylesButton2 := AX_InputBoxOpt.StylesButton2
	vStylesStatic := AX_InputBoxOpt.StylesStatic

	vFontOpt := "s" vFontSize
	if AX_InputBoxOpt.HFont
		hFont := AX_InputBoxOpt.HFont
	else
		hFont := JEE_FontCreate(vFontName, vFontSize, "", vFontWeight)
	JEE_StrGetDim(vTextBtn1, hFont, vBtn1W, vBtn1H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	JEE_StrGetDim(vTextBtn2, hFont, vBtn2W, vBtn2H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	vBtnW := Max(vBtn1W, vBtn2W) + 16
	vBtnH := Max(vBtn1H, vBtn2H) + 7

	vEditW := vWinW - 2*vMgnX
	vGap := Round((vWinW - 2*vBtnW - 2*vMgnX)/4)
	vBtn1X := vMgnX + vGap
	vBtn2X := vMgnX + vGap*3 + vBtnW
	vBtnY := vWinH - vMgnY - vBtnH
	vEditY := vBtnY - vMgnY - vBtnH
	vStcH := vEditY - vMgnY
	vPosEdit := Format("X{} Y{} W{} H{}", vMgnX, vEditY, vEditW, vBtnH)
	vPosBtn1 := Format("X{} Y{} W{} H{}", vBtn1X, vBtnY, vBtnW, vBtnH)
	vPosBtn2 := Format("X{} Y{} W{} H{}", vBtn2X, vBtnY, vBtnW, vBtnH)
	vPosStc := Format("X{} Y{} W{} H{}", vMgnX, vMgnY, vEditW, vStcH)

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	;WS_POPUPWINDOW := 0x80880000 ;composite style (WS_POPUP | WS_BORDER | WS_SYSMENU)
	;WS_VISIBLE := 0x10000000
	;WS_CLIPSIBLINGS := 0x4000000
	;WS_CAPTION := 0xC00000 ;composite style (WS_BORDER | WS_DLGFRAME)
	;WS_THICKFRAME := 0x40000
	;WS_EX_CONTROLPARENT := 0x10000
	;WS_EX_WINDOWEDGE := 0x100
	oGui := GuiCreate(vStylesWin, vWinTitle)
	oGui.SetFont(vFontOpt, vFontName)
	oGui.OnEvent("Close", Func("InputBoxNew_Close").Bind(oGui, "Close"))
	oGui.OnEvent("Escape", Func("InputBoxNew_Close").Bind(oGui, "Escape"))
	hGui := oGui.hWnd

	oGui.MarginX := vMgnX
	oGui.MarginY := vMgnY

	if !vHasPassChar
		oGui.Add("Edit", vStylesEdit " " vPosEdit, vDefault)
	else
	{
		oGui.Add("Edit", vStylesEditPW " " vPosEdit, vDefault)
		if !(vPassChar = "")
			PostMessage(0xCC, Ord(vPassChar),, "Edit1", "ahk_id " hGui) ;EM_SETPASSWORDCHAR := 0xCC
	}
	ControlFocus("Edit1", "ahk_id " hGui)
	PostMessage(0xB1, 0, -1, "Edit1", "ahk_id " hGui) ;EM_SETSEL := 0xB1 ;select all

	oBtn1 := oGui.Add("Button", vStylesButton1 " " vPosBtn1, vTextBtn1)
	oBtn2 := oGui.Add("Button", vStylesButton2 " " vPosBtn2, vTextBtn2)
	oBtn1.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "OK"))
	oBtn2.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "Cancel"))
	oGui.Add("Text", vStylesStatic " " vPosStc, vText)

	oGui.Show(vPosWin)

	oGui.OnEvent("Size", "InputBoxNew_Size")

	if !(vTimeout = "")
	{
		BoundFuncTemp := Func("InputBoxNew_Close").Bind(oGui, "Timeout")
		SetTimer(BoundFuncTemp, -vTimeout)
	}

	WinWaitClose("ahk_id " hGui)

	;if WinExist("ahk_id " hWnd)
	;	WinActivate("ahk_id " hWnd)

	if oInputBoxRet.Length()
		return oInputBoxRet[hGui].1
	else
		return
}
InputBoxNew_Close(oGui, vBtn)
{
	global oInputBoxRet
	try hWnd := oGui.hWnd
	catch
		vBtn := ""
	;MsgBox(A_ThisFunc " " hWnd " " vBtn)
	if (vBtn = "OK")
	{
		oArray := []
		Loop
		{
			hCtl := ControlGetHwnd("Edit" A_Index, "ahk_id " hWnd)
			if !hCtl
				break
			oArray.Push(ControlGetText("", "ahk_id " hCtl))
		}
		oInputBoxRet[hWnd] := oArray
		ErrorLevel := 0
		AX_InputBoxResult := 0
	}
	else
	{
		ErrorLevel := 1
		AX_InputBoxResult := 1
	}
	try oGui.Destroy
}
InputBoxNew_Size(oGui)
{
	try hWnd := oGui.hWnd
	catch
		return
	WinGetClientPos(vWinX, vWinY, vWinW, vWinH, "ahk_id " hWnd)
	vMgnX := oGui.MarginX
	vMgnY := oGui.MarginY
	ControlGetPos(,, vBtnW, vBtnH, "Button1", "ahk_id " hWnd)

	vEditW := vWinW - 2*vMgnX
	vGap := Round((vWinW - 2*vBtnW - 2*vMgnX)/4)
	vBtn1X := vMgnX + vGap
	vBtn2X := vMgnX + vGap*3 + vBtnW
	vBtnY := vWinH - vMgnY - vBtnH
	vEditY := vBtnY - vMgnY - vBtnH
	vStcH := vEditY - 5
	ControlMove(vMgnX, vEditY, vEditW, vBtnH, "Edit1", "ahk_id " hWnd)
	ControlMove(vBtn1X, vBtnY, vBtnW, vBtnH, "Button1", "ahk_id " hWnd)
	ControlMove(vBtn2X, vBtnY, vBtnW, vBtnH, "Button2", "ahk_id " hWnd)
	ControlMove(vMgnX, vMgnY, vEditW, vStcH, "Static1", "ahk_id " hWnd)
}

;==================================================

;FUNCTIONS - INPUTBOX MULTI

;note: no handling for passwords
;note: no handling for resize
InputBoxMulti(oParams*) ;oText, vWinTitle, vOpt, oDefault
{
	global oInputBoxRet

	oText := oParams.1
	vWinTitle := oParams.HasKey(2) ? oParams.2 : AX_DlgTitle
	vOpt := oParams.3
	oDefault := oParams.4

	if !IsObject(oInputBoxRet)
		oInputBoxRet := []
	hWnd := WinExist("A")

	if !IsObject(oText)
		oText := [oText]
	if !IsObject(oDefault)
		oDefault := [oDefault]

	DetectHiddenWindows(1)
	vPosWin := "", vTimeout := "", vPassChar := ""
	vMgnX := 5, vMgnY := 5
	vWinW := 368, vWinH := 166

	Loop Parse, vOpt, " `t"
	{
		if (SubStr(A_LoopField, 1, 1) = "W")
			vWinW := SubStr(A_LoopField, 2)
		else if (SubStr(A_LoopField, 1, 1) = "H")
			vWinH := SubStr(A_LoopField, 2)

		if (A_LoopField ~= "i)^[XYWH]")
			vPosWin .= A_LoopField " "
		else if (SubStr(A_LoopField, 1, 1) = "T")
			vTimeout := SubStr(A_LoopField, 2)
	}

	vStyleReset := "-0xFFFFFFFF -E0xFFFFFFFF "

	vTextBtn1 := AX_InputBoxOpt.TextButton1
	vTextBtn2 := AX_InputBoxOpt.TextButton2
	vFontName := AX_InputBoxOpt.FontName
	vFontSize := AX_InputBoxOpt.FontSize
	vFontWeight := AX_InputBoxOpt.FontWeight
	vStylesWin := vStyleReset AX_InputBoxOpt.StylesWin
	vStylesEdit := AX_InputBoxOpt.StylesEdit
	vStylesEditPW := AX_InputBoxOpt.StylesEditPW
	vStylesButton1 := AX_InputBoxOpt.StylesButton1
	vStylesButton2 := AX_InputBoxOpt.StylesButton2
	vStylesStatic := AX_InputBoxOpt.StylesStatic

	vFontOpt := "s" vFontSize
	hFont := JEE_FontCreate(vFontName, vFontSize, "", vFontWeight)
	JEE_StrGetDim(vTextBtn1, hFont, vBtn1W, vBtn1H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	JEE_StrGetDim(vTextBtn2, hFont, vBtn2W, vBtn2H, vDTFormat:=0x400, vLimW:="", vLimH:="")
	vBtnW := Max(vBtn1W, vBtn2W) + 16
	vBtnH := Max(vBtn1H, vBtn2H) + 7

	vGap := Round((vWinW - 2*vBtnW - 2*vMgnX)/4)
	vBtn1X := vMgnX + vGap
	vBtn2X := vMgnX + vGap*3 + vBtnW

	vEditW := vWinW - 2*vMgnX
	vEditH := vBtnH
	vStcW := vEditW

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	;WS_POPUPWINDOW := 0x80880000 ;composite style (WS_POPUP | WS_BORDER | WS_SYSMENU)
	;WS_VISIBLE := 0x10000000
	;WS_CLIPSIBLINGS := 0x4000000
	;WS_CAPTION := 0xC00000 ;composite style (WS_BORDER | WS_DLGFRAME)
	;WS_THICKFRAME := 0x40000
	;WS_EX_CONTROLPARENT := 0x10000
	;WS_EX_WINDOWEDGE := 0x100
	oGui := GuiCreate(vStylesWin, vWinTitle)
	oGui.SetFont(vFontOpt, vFontName)
	oGui.OnEvent("Close", Func("InputBoxNew_Close").Bind(oGui, "Close"))
	oGui.OnEvent("Escape", Func("InputBoxNew_Close").Bind(oGui, "Escape"))
	hGui := oGui.hWnd

	vPosX := vMgnX, vPosY := vMgnY
	Loop Max(oText.Length(), oDefault.Length())
	{
		if (A_Index <= oDefault.Length())
		{
			JEE_StrGetDim(oText[A_Index], hFont, vStcW2, vStcH, vDTFormat:=0x400, vLimW:=vWinW, vLimH:="")
			vPosStc := Format("X{} Y{} W{} H{}", vPosX, vPosY, vStcW, vStcH)
			oGui.Add("Text", vStylesStatic " " vPosStc, oText[A_Index])
			vPosY += vStcH + vMgnY
		}
		if (A_Index <= oDefault.Length())
		{
			vPosEdit := Format("X{} Y{} W{} H{}", vPosX, vPosY, vEditW, vEditH)
			oGui.Add("Edit", vStylesEdit " " vPosEdit, oDefault[A_Index])
			vPosY += vEditH + vMgnY
		}
	}

	vBtnY := vPosY
	vPosBtn1 := Format("X{} Y{} W{} H{}", vBtn1X, vBtnY, vBtnW, vBtnH)
	vPosBtn2 := Format("X{} Y{} W{} H{}", vBtn2X, vBtnY, vBtnW, vBtnH)
	vPosY += vBtnH + vMgnY

	hWnd := WinExist("A")
	if (vWinTitle = "")
		vWinTitle := AX_DlgTitle

	oGui.MarginX := vMgnX
	oGui.MarginY := vMgnY

	ControlFocus("Edit1", "ahk_id " hGui)
	PostMessage(0xB1, 0, -1, "Edit1", "ahk_id " hGui) ;EM_SETSEL := 0xB1 ;select all

	oBtn1 := oGui.Add("Button", vStylesButton1 " " vPosBtn1, vTextBtn1)
	oBtn2 := oGui.Add("Button", vStylesButton2 " " vPosBtn2, vTextBtn2)
	oBtn1.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "OK"))
	oBtn2.OnEvent("Click", Func("InputBoxNew_Close").Bind(oGui, "Cancel"))

	if !InStr(vPosWin, "W")
		vPosWin .= " W" vWinW
	if !InStr(vPosWin, "H")
		vPosWin .= " H" vPosY
	oGui.Show(vPosWin)

	;oGui.OnEvent("Size", "InputBoxNew_Size")

	if !(vTimeout = "")
	{
		BoundFuncTemp := Func("InputBoxNew_Close").Bind(oGui, "Timeout")
		SetTimer(BoundFuncTemp, -vTimeout)
	}

	WinWaitClose("ahk_id " hGui)

	;if WinExist("ahk_id " hWnd)
	;	WinActivate("ahk_id " hWnd)

	if oInputBoxRet.Length()
		return oInputBoxRet[hGui]
	else
		return
}

;==================================================

;FUNCTIONS - PROGRESS/SPLASHIMAGE (AHK V1 DIAGNOSTIC)

;function wrappers for AHK v1
;to compare with these clone functions
/*
Progress(ProgressParam1, SubText:="", MainText:="", WinTitle:="", FontName:="")
{
	Progress, % ProgressParam1, % SubText, % MainText, % WinTitle, % FontName
}
SplashImage(ImageFile:="", Options:="", SubText:="", MainText:="", WinTitle:="", FontName:="")
{
	SplashImage, % ImageFile, % Options, % SubText, % MainText, % WinTitle, % FontName
}
*/

;diagnostic functions to test Progress/SplashImage commands using an AHK v1 exe
ProgressAhk1(oParams*)
{
	SplashAhk1("Progress", oParams*)
}
SplashImageAhk1(oParams*)
{
	SplashAhk1("SplashImage", oParams*)
}
SplashAhk1(vFunc, oParams*)
{
	global vGblPathAhk1
	global vGblDelayAhk1
	vPathAhk1 := vGblPathAhk1
	vDelay := vGblDelayAhk1
	vScript := vFunc
	if !FileExist(vPathAhk1)
		return
	Loop oParams.Length()
	{
		vValue := oParams[A_Index]
		if (vValue = "")
			vScript .= ", % " Chr(34) Chr(34)
		else
		{
			vValue := StrReplace(vValue, "`r", "``r")
			vValue := StrReplace(vValue, "`n", "``n")
			vScript .= ", % " Chr(34) vValue Chr(34)
		}
	}
	vScript .= "`r`nSleep, " vDelay
	;MsgBox(vScript)

	vDoWait := 1
	;based on ExecScript
	oShell := ComObjCreate("WScript.Shell")
	oExec := oShell.Exec(vPathAhk1 " /ErrorStdOut *")
	oExec.StdIn.Write(vScript)
	oExec.StdIn.Close()
	if vDoWait
		return oExec.StdOut.ReadAll()
}

;==================================================

;FUNCTIONS - PROGRESS/SPLASHIMAGE (AHK V2)

ProgressNew(vParam1, vSubText:="", vMainText:="", vWinTitle:=" ", vFontName:="")
{
	SplashImageNew("*PROGRESS*", vParam1, vSubText, vMainText, vWinTitle, vFontName)
}
SplashImageNew(vImageFile:="", vOpt:="", vSubText:="", vMainText:="", vWinTitle:=" ", vFontName:="")
{
	static vIsInit, oColRGB, vFontNameDefault, oGuiStore
	local vX, vY
	local vZX, vZY, vFM, vFS, vWM, vWS
	local vZW, vZH
	local vCB, vCT, vCW
	if !vIsInit
	{
		vListColRGB := " ;continuation section
		(LTrim
		Black=000000
		Silver=C0C0C0
		Gray=808080
		White=FFFFFF
		Maroon=800000
		Red=FF0000
		Purple=800080
		Fuchsia=FF00FF
		Green=008000
		Lime=00FF00
		Olive=808000
		Yellow=FFFF00
		Navy=000080
		Blue=0000FF
		Teal=008080
		Aqua=00FFFF
		)"
		oColRGB := Object(StrSplit(vListColRGB, ["=", "`n"])*)

		;DEFAULT_GUI_FONT := 17
		hFontDefault := DllCall("gdi32\GetStockObject", Int,17, Ptr)
		vSize := DllCall("gdi32\GetObject", Ptr,hFontDefault, Int,vSize, Ptr,0)
		VarSetCapacity(LOGFONT, vSize, 0)
		DllCall("gdi32\GetObject", Ptr,hFontDefault, Int,vSize, Ptr,&LOGFONT)
		vFontNameDefault := StrGet(&LOGFONT + 28, 32)
		DllCall("gdi32\DeleteObject", Ptr,hFontDefault)

		oGuiStore := {}
		vIsInit := 1
	}

	vDHW := A_DetectHiddenWindows
	DetectHiddenWindows("On")

	vNum := 1
	if (vImageFile = "*PROGRESS*") ;Progress
	{
		vIsProgress := 1
		vP := 0 ;progress bar (filled) position [Progress windows only]
		vZH := 20 ;progress bar thickness
		if RegExMatch(vOpt, "^(\d+):(.*)$", oMatch) ;window number (which window to change)
			vNum := oMatch.1, vOpt := oMatch.2
		if (vOpt = "Off")
		|| (vOpt = "Show")
			vImageFile := vOpt, vOpt := ""
		;else if !(vOpt+0 = "")
		else if vOpt is "number"
			vP := vOpt
	}

	if (vWinTitle = " ")
		vWinTitle := AX_DlgTitle

	if (SubStr(vFontName, 1, 6) = "HFONT:")
		hFont := SubStr(vFontName, 7)+0
	if !hFont && (vFontName = "")
		vFontName := vFontNameDefault

	if RegExMatch(vImageFile, "^(\d+):(.*)$", oMatch) ;window number (which window to change)
		vNum := oMatch.1, vImageFile := oMatch.2

	vID := (vIsProgress ? "P" : "S") vNum
	if (vImageFile = "Off")
	{
		if !oGuiStore.HasKey(vID)
			return
		oGui := GuiFromHwnd(oGuiStore[vID])
		oGuiStore.Delete(vID)
		oGui.Destroy()
		DetectHiddenWindows(vDHW)
		return
	}
	else if (vImageFile = "Show")
	{
		WinShow("ahk_id " oGuiStore[vID])
		DetectHiddenWindows(vDHW)
		return
	}
	else if (vImageFile = "Hide")
	{
		WinHide("ahk_id " oGuiStore[vID])
		DetectHiddenWindows(vDHW)
		return
	}
	else if !vIsProgress
	&& !(vImageFile = "")
	&& !InStr(vImageFile, "\")
	&& !(SubStr(vImageFile, 1, 8) = "HBITMAP:")
	&& !(SubStr(vImageFile, 1, 6) = "HICON:")
		vImageFile := A_WorkingDir "\" vImageFile

	;get image dimensions
	if !vIsProgress
		if (SubStr(vImageFile, 1, 8) = "HBITMAP:")
		{
			hImg := SubStr(vImageFile, 9)+0
			if !(hImg ~= "^\d+$")
				return
			JEE_HBitmapGetDims(hImg, vImgW, vImgH)
		}
		else if (SubStr(vImageFile, 1, 6) = "HICON:")
		{
			hImg := SubStr(vImageFile, 7)+0
			if !(hImg ~= "^\d+$")
				return
			JEE_HIconGetDims(hImg, vImgW, vImgH)
		}
		else if InStr(vImageFile, "\")
		{
			hImg := LoadPicture(vImageFile,, vImgType) ;IMAGE_ICON := 1 ;IMAGE_BITMAP := 0
			if (vImgType = 0)
				JEE_HBitmapGetDims(hImg, vImgW, vImgH)
			else if (vImgType = 1)
				JEE_HIconGetDims(hImg, vImgW, vImgH)
		}

	;WS_POPUP := 0x80000000	;WS_DISABLED := 0x8000000
	;WS_CLIPSIBLINGS := 0x4000000
	;WS_CAPTION := 0xC00000 := WS_BORDER|WS_DLGFRAME := 0x800000|0x400000
	vWinStyle := 0x8CC00000
	;WS_EX_WINDOWEDGE := 0x100 ;WS_EX_TOPMOST := 0x8
	vWinExStyle := 0x108
	vWinX := vWinY := vWinW := vWinX := ""
	vCM := 1, vCS := 1 ;main/sub text centred by default
	vFM := 0, vFS := 0 ;main/sub font size
	;FW_BOLD := 700 ;FW_SEMIBOLD := 600
	;FW_NORMAL := 400 ;FW_DONTCARE := 0
	vWM := 600, vWS := 400 ;main/sub font weight
	if vIsProgress || !(vMainText = "") || !(vSubText = "")
		vZX := 10, vZY := 5 ;margins
	else
		vZX := 0, vZY := 0 ;margins
	vR1 := "" ;progress bar range ;[Progress windows only]
	vR2 := "" ;progress bar range ;[Progress windows only]

	Loop Parse, vOpt, " `t"
	{
		vTemp := A_LoopField
		if (vTemp = "A") ;always-on-top *off*
		|| (vTemp = "A0") ;always-on-top *off*
			vWinExStyle &= 0x8 ^ -1 ;WS_EX_TOPMOST := 0x8
		else if (vTemp = "B") ;*no* border, no title bar
		|| (vTemp = "B0") ;*no* border, no title bar
			vWinStyle &= 0xC00000 ^ -1 ;WS_CAPTION := 0xC00000
		else if (vTemp = "B1") ;thin border, no title bar
		{
			vWinStyle &= 0xC00000 ^ -1 ;WS_CAPTION := 0xC00000
			vWinStyle |= 0x800000 ;WS_BORDER := 0x800000
		}
		else if (vTemp = "B2") ;dialog-style border, no title bar
		{
			vWinStyle &= 0xC00000 ^ -1 ;WS_CAPTION := 0xC00000
			vWinStyle |= 0x400000 ;WS_DLGFRAME := 0x400000
		}
		else if (vTemp = "M") ;movable
			vWinStyle &= 0x8000000 ^ -1 ;WS_DISABLED := 0x8000000
		else if (vTemp = "M1") ;resizeable
		{
			vWinStyle |= 0x40000 ;WS_THICKFRAME := 0x40000
			vWinStyle &= 0x8000000 ^ -1 ;WS_DISABLED := 0x8000000
		}
		else if (vTemp = "M2") ;resizeable + sysmenu + title bar buttons
		;WS_MINIMIZEBOX := 0x20000 ;WS_MAXIMIZEBOX := 0x10000
		;WS_SYSMENU := 0x80000 ;WS_THICKFRAME := 0x40000
		{
			vWinStyle |= 0xF0000
			vWinStyle &= 0x8000000 ^ -1 ;WS_DISABLED := 0x8000000
		}
		;else if (vTemp = "T") ;taskbar button
		;	vIsOwned := 0
		else if (vTemp = "Hide")
		{
			WinHide("ahk_id " oGuiStore[vID])
			DetectHiddenWindows(vDHW)
			return
		}
		else if (vTemp ~= "i)^[XY]-?\d") ;X/Y coordinates
		|| (vTemp ~= "i)^[WH]\d")
		{
			vTemp1 := SubStr(vTemp, 1, 1)
			vWin%vTemp1% := SubStr(vTemp, 2)+0
		}
		else if (vTemp ~= "i)^C\d\d$") ;centred
		{
			vCM := SubStr(vTemp, 2, 1)
			vCS := SubStr(vTemp, 3, 1)
		}
		else if (vTemp ~= "i)^(ZX|ZY|FM|FS|WM|WS)\d+$") ;X/Y margins, main/sub font size, main/sub font weight
		|| (vTemp ~= "i)^(ZW|ZH)-?\d+$") ;image width/height, progress bar thickness
		{
			vTemp1 := SubStr(vTemp, 1, 2)
			v%vTemp1% := SubStr(vTemp, 3)
		}
		;CB## [Progress windows only]
		else if (vTemp ~= "i)^(CB|CT|CW)[a-z0-9]+") ;progress bar/text/window colours
		{
			vTemp1 := SubStr(vTemp, 1, 2)
			vTemp2 := SubStr(vTemp, 3)
			if oColRGB.HasKey(vTemp2)
				v%vTemp1% := oColRGB[vTemp2] ;hex number without 0x
				;v%vTemp1% := "0x" oColRGB[vTemp2] ;hex number with 0x
			else
				v%vTemp1% := Format("{:06X}", vTemp2) ;hex number without 0x
				;v%vTemp1% := vTemp ;dec number/hex number with 0x
		}
		;P## [Progress windows only] progress bar (filled) position
		else if RegExMatch(vTemp, "i)^P-?\d+")
			vP := SubStr(vTemp, 2)
		;R##-## [Progress windows only] progress bar range
		else if RegExMatch(vTemp, "i)^R(-?\d+)-(-?\d+)$", oMatch)
			vR1 := oMatch.1+0, vR2 := oMatch.2+0
		else if (SubStr(vTemp, 1, 2) = "IE") ;use Internet Explorer_Server control
			vIsIE := 1, vZoomIE := SubStr(vTemp, 3)
		else if (SubStr(vTemp, 1, 6) = "HICON:")
			hIcon := SubStr(vTemp, 7), hIcon += 0
	}
	vStylesWin := "-0xFFFFFFFF " vWinStyle " E" vWinExStyle
	if !vZH ;if no progress bar, place MainText slightly higher
		vZY := 0

	if oGuiStore.HasKey(vID)
	{
		if vIsProgress && !(vP = "")
			SendMessage(0x402, vP,, "msctls_progress321", "ahk_id " oGuiStore[vID]) ;PBM_SETPOS := 0x402
		vText1 := ControlGetText("Static1", "ahk_id " oGuiStore[vID])
		vText2 := ControlGetText("Static2", "ahk_id " oGuiStore[vID])
		if !(vText1 = vSubText)
			ControlSetText(vText1, "Static1", "ahk_id " oGuiStore[vID])
		if !(vText1 = vMainText)
			ControlSetText(vText2, "Static2", "ahk_id " oGuiStore[vID])
		if !(vCB = "")
		{
			vCB := "0x" vCB
			vCBBGR := ((0xFF & vCB) << 16) + (0xFF00 & vCB) + ((0xFF0000 & vCB) >> 16)
			SendMessage(0x409, 0, vCBBGR, "msctls_progress321", "ahk_id " oGuiStore[vID]) ;PBM_SETBARCOLOR := 0x409 ;doesn't work if set theme to blank
			;SendMessage(0x2001, 0, vCBBGR, "msctls_progress321", "ahk_id " oGuiStore[vID]) ;PBM_SETBKCOLOR := 0x2001 ;doesn't work if set theme to blank
		}
		if !(vCT = "")
		{
			hCtl1 := ControlGetHwnd("Static1", "ahk_id " oGuiStore[vID])
			hCtl2 := ControlGetHwnd("Static2", "ahk_id " oGuiStore[vID])
			GuiCtrlFromHwnd(hCtl1).Opt("C" vCW)
			GuiCtrlFromHwnd(hCtl2).Opt("C" vCW)
		}
		if !(vCW = "")
		{
			oGui := GuiFromHwnd(oGuiStore[vID])
			oGui.BackColor := vCW
		}
		DetectHiddenWindows(vDHW)
		return
	}

	oGui := GuiCreate(vStylesWin, vWinTitle)
	;oGui.SetFont(vFontOpt, vFontName)
	;oGui.OnEvent("Close", Func("MsgBoxNew_Close").Bind(oGui, vTextBtn%vBtnClose%))
	;oGui.OnEvent("Escape", Func("MsgBoxNew_Close").Bind(oGui, vTextBtn%vBtnClose%))
	hGui := oGui.hWnd
	oGuiStore[vID] := hGui

	if (vWinStyle & 0x80000) ;WS_SYSMENU := 0x80000
		SendMessage(0x80, 0, hIcon,, "ahk_id " hGui) ;WM_SETICON := 0x80 ;ICON_SMALL := 0 ;sets title bar icon + taskbar icon
	if !vIsOwned
		SendMessage(0x80, 1, hIcon,, "ahk_id " hGui) ;WM_SETICON := 0x80 ;ICON_BIG := 1 ;sets alt+tab icon

	if !(vCW = "")
		oGui.BackColor := vCW

	if hFont
		hFontM := hFontS := hFont
	else
	{
		if !(vMainText = "")
			hFontM := JEE_FontCreate(vFontName, vFM, "", vWM)
		if !(vSubText = "")
			hFontS := JEE_FontCreate(vFontName, vFS, "", vWS)
	}

	;WS_CHILD := 0x40000000 ;WS_VISIBLE := 0x10000000
	;PBS_SMOOTH := 0x1
	;WS_EX_CLIENTEDGE := 0x200
	;vStylesPgs := 0x50000001
	vStylesPgs := 0x50000000

	vTextMW := vTextMH := 0
	vTextSW := vTextSH := 0
	if !(vMainText = "")
		JEE_StrGetDim(vMainText, hFontM, vTextMW, vTextMH, vDTFormat:=0x400, vLimW:="", vLimH:="")
	if !(vSubText = "")
		JEE_StrGetDim(vSubText, hFontS, vTextSW, vTextSH, vDTFormat:=0x400, vLimW:="", vLimH:="")

	if (vZW = -1) && (vZH = -1)
		vZW := vZH := ""
	if (vZW = -1)
		vZW := Round(vImgW * (vZH/vImgH))
	if (vZH = -1)
		vZH := Round(vImgH * (vZW/vImgW))
	if !(vZW = "")
		vImgW := vZW
	if !(vZH = "")
		vImgH := vZH
	if !vImgW
		vImgW := 0
	if !vImgH && !vIsProgress
		vImgH := 20
	if !vWinW
		if vIsProgress
			vWinW := Max(280, vTextMW, vTextSW) + 2*vZX
		else
			vWinW := vImgW + 2*vZX

	vStcMY := vZY
	vImgY := vStcMY + (vTextMH = 0 ? 0 : 21)
	vStcSY := vImgY + vImgH + vZY
	if !vWinH
		vWinH := vStcSY + vTextSH

	;WS_THICKFRAME require a minimum window width
	if (vWinStyle & 0x40000) ;WS_THICKFRAME := 0x40000
	{
		vMinW := SysGet(34) ;SM_CXMINTRACK := 34
		vMinH := SysGet(35) ;SM_CYMINTRACK := 35
		;specify negative values to convert window to client
		;What is the inverse of AdjustWindowRect and AdjustWindowRectEx? – The Old New Thing
		;https://blogs.msdn.microsoft.com/oldnewthing/20131017-00/?p=2903
		VarSetCapacity(RECT, 16, 0)
		NumPut(-vMinW, RECT, 8, "Int")
		NumPut(-vMinH, RECT, 12, "Int")
		DllCall("user32\AdjustWindowRectEx", Ptr,&RECT, UInt,vWinStyle, Int,0, UInt,vWinExStyle)
		vWinW2 := Abs(NumGet(&RECT, 8, "Int"))
		vWinH2 := Abs(NumGet(&RECT, 12, "Int"))
		vWinW := Max(vWinW, vWinW2)
		;vWinH := Max(vWinH, vWinH2)
	}

	MonitorGetWorkArea(1, vLimX, vLimY, vLimR, vLimB)
	vLimW := vLimR-vLimX, vLimH := vLimB-vLimY
	if (vWinW > vLimW)
		vWinW := vLimW
	if (vWinH > vLimH)
		vWinH := vLimH

	if vIsProgress && !vImgW
		vImgW := vWinW - 2*vZX
	vTextMW := vWinW - 2*vZX
	vTextSW := vWinW - 2*vZX

	vMainTextPos := Format("X{} Y{} W{} H{}", vZX, vStcMY, vTextMW, vTextMH)
	vSubTextPos := Format("X{} Y{} W{} H{}", vZX, vStcSY, vTextSW, vTextSH)
	vImgPos := Format("X{} Y{} {} {}", vZX, vImgY, (vImgW = "") ? "" : "W" vImgW, (vImgH = "") ? "" : "H" vImgH)

	;WS_CHILD := 0x40000000 ;WS_VISIBLE := 0x10000000
	;SS_NOPREFIX := 0x80
	;SS_CENTER := 0x1 ;SS_LEFT := 0x0
	vStylesMainText := (0x50000080|vCM) " E0x00000000"
	vStylesSubText := (0x50000080|vCS) " E0x00000000"
	if !(vCT = "")
		vCT := "C" vCT
	oCtl1 := oGui.Add("Text", vStylesMainText " " vMainTextPos " " vCT, vMainText)
	if vIsProgress && vImgH
	{
		vTemp := vStylesPgs " " vImgPos
		if !(vCB = "")
			vTemp .= " C" vCB
		if !(vR1 = "") && !(vR2 = "")
			vTemp .= " Range" vR1 "-" vR2
		oCtl3 := oGui.Add("Progress", vTemp, vP)
		if (vCB = "")
			DllCall("uxtheme\SetWindowTheme", Ptr,oCtl3.hWnd, Str,"", Ptr,0)
		;SendMessage(0x401, 0, (vR2 << 16) | (vR1 & 0xFFFF),, "ahk_id " oCtl3.hWnd) ;PBM_SETRANGE := 0x401
		;SendMessage(0x406, vR1, vR2,, "ahk_id " oCtl3.hWnd) ;PBM_SETRANGE32 := 0x406
	}
	if !(vCT = "")
		vCT := "C" vCT
	oCtl2 := oGui.Add("Text", vStylesSubText " " vSubTextPos " " vCT, vSubText)
	if !vIsProgress && !vIsIE
		oCtl3 := oGui.Add("Picture", vStylesImg " " vImgPos, vImageFile)
	if !vIsProgress && vIsIE
	{
		oWB := oGui.Add("ActiveX", vImgPos, "Shell.Explorer").Value
		oWB.Navigate(vImageFile)
		;OLECMDID_OPTICAL_ZOOM := 63 ;OLECMDEXECOPT_DONTPROMPTUSER := 2
		while oWB.Busy || !(oWB.ReadyState = 4)
			Sleep(100)
		;oWB.ExecWB(63, 2, 30, 0) ;zoom 30%
		if !(vZoomIE = "")
			oWB.ExecWB(63, 2, vZoomIE+0, 0)
		;oWB.document.parentWindow.scrollBy(120, 35)
		oWB.document.body.scroll := "no"
	}

	SendMessage(0x30, hFontM, 0,, "ahk_id " oCtl1.hWnd) ;WM_SETFONT := 0x30
	SendMessage(0x30, hFontS, 0,, "ahk_id " oCtl2.hWnd) ;WM_SETFONT := 0x30

	;vWinPos := Format("X{} Y{} W{} H{}", vWinX, vWinY, vWinW, vWinH)
	if !(vWinX = "")
		vWinPos .= "X" vWinX " "
	if !(vWinY = "")
		vWinPos .= "Y" vWinY " "
	if !(vWinW = "")
		vWinPos .= "W" vWinW " "
	if !(vWinH = "")
		vWinPos .= "H" vWinH " "

	oGui.Show(vWinPos " NoActivate")
	DetectHiddenWindows(vDHW)
	;WinWaitClose("ahk_id " hGui)
	return hGui
}

;==================================================

;FUNCTIONS - TOOLTIP

;note: specify c for vPosX or vPosY, to place the ToolTip in the centre of the screen
ToolTipNew(vText:="", vPosX:="", vPosY:="", vWhichToolTip:=1, vColTxRGB:="", vColBkRGB:="")
{
	static oArray := []
	if (vText = "")
	{
		if oArray[vWhichToolTip]
			DllCall("user32\DestroyWindow", Ptr,oArray[vWhichToolTip])
		oArray[vWhichToolTip] := 0
		return
	}
	else if oArray[vWhichToolTip]
		DllCall("user32\DestroyWindow", Ptr,oArray[vWhichToolTip])

	vFontName := AX_ToolTipOpt.FontName
	vFontSize := AX_ToolTipOpt.FontSize
	vFontWeight := AX_ToolTipOpt.FontWeight
	if (vColTxRGB = "")
		vColTxRGB := AX_ToolTipOpt.ColTxRGB
	if (vColBkRGB = "")
		vColBkRGB := AX_ToolTipOpt.ColBkRGB
	if (vFontName = "")
		vFontName := "Segoe UI"
	if (vFontSize = "")
		vFontSize := 9
	if (vFontWeight = "")
		vFontWeight := 400
	if (vColTxRGB = "")
		vColTxRGB := 0x000000
	if (vColBkRGB = "")
		vColBkRGB := 0xFFFFFF

	;if either X or Y is unspecified,
	;get cursor position
	if (vPosX = "") || (vPosY = "")
	{
		vCMM := A_CoordModeMouse
		CoordMode("Mouse", "Screen")
		MouseGetPos(vCurX, vCurY)
		CoordMode("Mouse", vCMM)
	}

	;if either X or Y is numeric,
	;get required offset
	if !((vPosX = "c") || (vPosX = ""))
	|| !((vPosY = "c") || (vPosY = ""))
	{
		if (A_CoordModeToolTip = "Window")
			WinGetPos(vOffsetX, vOffsetY,,, "A")
		else if (A_CoordModeToolTip = "Client")
			WinGetClientPos(vOffsetX, vOffsetY,,, "A")
		else
			vOffsetX := vOffsetY := 0
	}

	if (vPosX = "")
		vPosX := vCurX+16
	else if !(vPosX = "c")
		vPosX += vOffsetX
	if (vPosY = "")
		vPosY := vCurY+16
	else if !(vPosY = "c")
		vPosY += vOffsetY

	vColTxBGR := ((0xFF & vColTxRGB) << 16) + (0xFF00 & vColTxRGB) + ((0xFF0000 & vColTxRGB) >> 16)
	vColBkBGR := ((0xFF & vColBkRGB) << 16) + (0xFF00 & vColBkRGB) + ((0xFF0000 & vColBkRGB) >> 16)

	vDHW := A_DetectHiddenWindows
	DetectHiddenWindows("On")
	vSizeTI := A_PtrSize=8?72:48
	VarSetCapacity(TOOLINFO, vSizeTI, 0)
	NumPut(vSizeTI, &TOOLINFO, 0, "UInt") ;cbSize
	NumPut(0x20, &TOOLINFO, 4, "UInt") ;uFlags ;TTF_TRACK := 0x20
	NumPut(&vText, &TOOLINFO, A_PtrSize=8?48:36, "Ptr") ;lpszText

	;create window
	;WS_VISIBLE := 0x10000000
	;vWinStyle := 0x8
	;vWinExStyle := 0x3
	vWinStyle := 0x94000003
	;vWinStyle := 0x84000003
	vWinExStyle := 0x00080088

	;TTS_USEVISUALSTYLE := 0x100
	;TTS_CLOSE := 0x80 ;TTS_BALLOON := 0x40
	;TTS_NOFADE := 0x20 ;TTS_NOANIMATE := 0x10
	;TTS_NOPREFIX := 0x2 ;TTS_ALWAYSTIP := 0x1
	;WS_EX_TOPMOST := 0x8
	hTT := DllCall("user32\CreateWindowEx", UInt,vWinExStyle, Str,"tooltips_class32", Ptr,0, UInt,vWinStyle, Int,0, Int,0, Int,0, Int,0, Ptr,A_ScriptHwnd, Ptr,0, Ptr,0, Ptr,0, Ptr)
	;WinSet, Style, 0x94000003, % "ahk_id " hTT
	;WinSet, ExStyle, 0x00080088, % "ahk_id " hTT

	;set background colours/margins
	;the text/font/margins will determine the window size
	if (AX_ToolTipOpt.SetWindowTheme = 0)
		DllCall("uxtheme\SetWindowTheme", Ptr,hTT, Ptr,0, Str,"")
	VarSetCapacity(RECT, 16, 0)
	;vMgnL := vMgnR := 8
	;vMgnT := vMgnB := 4
	vMgnL := vMgnR := 4
	vMgnT := vMgnB := 1
	vRect := vMgnL "," vMgnT "," vMgnR "," vMgnB
	Loop Parse, vRect, ","
		NumPut(A_LoopField, &RECT, A_Index*4-4, "Int")
	SendMessage(0x41A, 0, &RECT,, "ahk_id " hTT) ;TTM_SETMARGIN := 0x41A
	SendMessage(0x413, vColBkBGR, 0,, "ahk_id " hTT) ;TTM_SETTIPBKCOLOR := 0x413
	SendMessage(0x414, vColTxBGR, 0,, "ahk_id " hTT) ;TTM_SETTIPTEXTCOLOR := 0x414

	;to allow multiline ToolTips
	SendMessage(0x418, 0, 200,, "ahk_id " hTT) ;TTM_SETMAXTIPWIDTH := 0x418

	;set font
	vFontHeight := -Round(vFontSize*A_ScreenDPI/72)
	hFont := DllCall("gdi32\CreateFont", Int,vFontHeight, Int,0, Int,0, Int,0, Int,vFontWeight, UInt,0, UInt,0, UInt,0, UInt,0, UInt,0, UInt,0, UInt,0, UInt,0, Str,vFontName, Ptr)
	SendMessage(0x30, hFont, 0,, "ahk_id " hTT) ;WM_SETFONT := 0x30

	if (vPosX = "c") || (vPosY = "c")
		JEE_StrGetDim(vText, hFont, vTextW, vTextH, vDTFormat:=0x400, vLimW:="", vLimH:="")
	if (vPosX = "c")
	{
		vWinW := vTextW + vMgnL + vMgnR + 5 ;5 is of unknown origin
		vPosX := Round((A_ScreenWidth - vWinW) / 2)
	}
	if (vPosY = "c")
	{
		vWinH := vTextH + vMgnT + vMgnB + 3 ;3 is of unknown origin
		vPosY := Round((A_ScreenHeight - vWinH) / 2)
	}

	;TTM_TRACKPOSITION will determine the window position
	SendMessage(A_IsUnicode?0x432:0x404, 0, &TOOLINFO,, "ahk_id " hTT) ;TTM_ADDTOOLW := 0x432
	SendMessage(0x412, 0, (vPosX&0xFFFF)|(vPosY<<16),, "ahk_id " hTT) ;TTM_TRACKPOSITION := 0x412
	SendMessage(0x411, 1, &TOOLINFO,, "ahk_id " hTT) ;TTM_TRACKACTIVATE := 0x411

	;vWinPos1 := Format("x{} y{} w{} h{}", vPosX, vPosY, vWinW, vWinH)
	;WinGetPos, vWinX, vWinY, vWinW, vWinH, % "ahk_id " hTT
	;vWinPos2 := Format("x{} y{} w{} h{}", vWinX, vWinY, vWinW, vWinH)
	;MsgBox, % vWinPos1 "`r`n" vWinPos2

	oArray[vWhichToolTip] := hTT

	DetectHiddenWindows(vDHW)
}

;==================================================

;FUNCTIONS - BORDERS

Borders(vPosX, vPosY, vPosW:=0, vPosH:=0, vTime:=1000, vColRGB:=0xFFFF00, vBdrW:=5, vBdrH:=5)
{
	;based on example at the very bottom of:
	;WinSet
	;https://autohotkey.com/docs/commands/WinSet.htm
	static vWinClass := "AHKBordersClass"
	static vFunc := "Borders_WndProc"
	static pWndProc := CallbackCreate(vFunc, "F")
	static vPIs64 := (A_PtrSize=8)
	static vSize := vPIs64?80:48
	VarSetCapacity(WNDCLASSEX, vSize, 0)
	NumPut(vSize, &WNDCLASSEX, 0, "UInt") ;cbSize
	NumPut(pWndProc, &WNDCLASSEX, 8, "Ptr") ;lpfnWndProc
	vColBGR := ((0xFF & vColRGB) << 16) + (0xFF00 & vColRGB) + ((0xFF0000 & vColRGB) >> 16)
	hBrush := DllCall("gdi32\CreateSolidBrush", UInt,vColBGR, Ptr)
	NumPut(hBrush, &WNDCLASSEX, vPIs64?48:32, "Ptr") ;hbrBackground
	NumPut(&vWinClass, &WNDCLASSEX, vPIs64?64:40, "Ptr") ;lpszClassName
	DllCall("user32\RegisterClassEx", Ptr,&WNDCLASSEX, UShort)

	vPosX -= vBdrW, vPosY -= vBdrH
	vPosW += vBdrW*2, vPosH += vBdrH*2

	DetectHiddenWindows("On")
	;WS_POPUP := 0x80000000
	;WS_EX_TOOLWINDOW := 0x80 ;WS_EX_TOPMOST := 0x8
	vWinText := "", vWinStyle := 0x80000000, vWinExStyle := 0x88
	hWnd := DllCall("user32\CreateWindowEx", UInt,vWinExStyle, Str,vWinClass, Str,vWinText, UInt,vWinStyle, Int,vPosX, Int,vPosY, Int,vPosW, Int,vPosH, Ptr,0, Ptr,0, Ptr,0, Ptr,0, Ptr)
	vBdrL := vBdrR := vBdrW
	vBdrT := vBdrB := vBdrH
	oArray := [vPosW, vPosH, vBdrL, vPosW-vBdrR, vBdrT, vPosH-vBdrB]
	vRegion := Format("0-0 {1:}-0 {1:}-{2:} 0-{2:} 0-0" " {3:}-{5:} {4:}-{5:} {4:}-{6:} {3:}-{6:} {3:}-{5:}", oArray*)
	WinSetRegion(vRegion, "ahk_id " hWnd)
	WinShow("ahk_id " hWnd)

	Sleep(vTime)
	DllCall("user32\DestroyWindow", Ptr,hWnd)
}
Borders_WndProc(hWnd, uMsg, wParam, lParam)
{
	return DllCall("user32\DefWindowProc", Ptr,hWnd, UInt,uMsg, UPtr,wParam, Ptr,lParam, Ptr)
}

;==================================================

;;FUNCTIONS - AUXILIARY

;==================================================

JEE_HIconGetDims(hIcon, ByRef vImgW, ByRef vImgH)
{
	vIsMask := 0
	VarSetCapacity(ICONINFO, A_PtrSize=8?32:20, 0)
	DllCall("user32\GetIconInfo", Ptr,hIcon, Ptr,&ICONINFO)
	hBitmapCol := NumGet(&ICONINFO, A_PtrSize=8?24:16, "Ptr") ;hbmColor
	hBitmapMask := NumGet(&ICONINFO, A_PtrSize=8?16:12, "Ptr") ;hbmMask
	if hBitmap := hBitmapCol
	{
		if hBitmapMask
			DllCall("gdi32\DeleteObject", Ptr,hBitmapMask)
	}
	else if hBitmap := hBitmapMask
		vIsMask := 1
	else
		return
	VarSetCapacity(BITMAP, A_PtrSize=8?32:24, 0)
	DllCall("gdi32\GetObject", Ptr,hBitmap, Int,A_PtrSize=8?32:24, Ptr,&BITMAP)
	vImgW := NumGet(&BITMAP, 4, "Int") ;bmWidth
	vImgH := NumGet(&BITMAP, 8, "Int") ;bmHeight
	if vIsMask
		vImgH //= 2
	DllCall("gdi32\DeleteObject", Ptr,hBitmap)
}

;==================================================

JEE_HBitmapGetDims(hBitmap, ByRef vImgW, ByRef vImgH)
{
	VarSetCapacity(BITMAP, A_PtrSize=8?32:24, 0)
	DllCall("gdi32\GetObject", Ptr,hBitmap, Int,A_PtrSize=8?32:24, Ptr,&BITMAP)
	vImgW := NumGet(&BITMAP, 4, "Int") ;bmWidth
	vImgH := NumGet(&BITMAP, 8, "Int") ;bmHeight
}

;==================================================

;e.g. hFont := JEE_FontCreate("Arial", 12, "bius")

;JEE_CreateFont
JEE_FontCreate(vName, vSize, vFontStyle:="", vWeight:="")
{
	vHeight := -DllCall("kernel32\MulDiv", Int,vSize, Int,A_ScreenDPI, Int,72)
	vWidth := 0
	vEscapement := 0
	vOrientation := 0
	vWeight := (vWeight != "") ? vWeight : InStr(vFontStyle, "b") ? 700 : 400
	vItalic := InStr(vFontStyle, "i") ? 1 : 0
	vUnderline := InStr(vFontStyle, "u") ? 1 : 0
	vStrikeOut := InStr(vFontStyle, "s") ? 1 : 0
	vCharSet := 0
	vOutPrecision := 0
	vClipPrecision := 0
	vQuality := 0
	vPitchAndFamily := 0
	vFaceName := vName
	vOutPrecision := 3
	vClipPrecision := 2
	vQuality := 1
	vPitchAndFamily := 34
	return DllCall("gdi32\CreateFont", Int,vHeight, Int,vWidth, Int,vEscapement, Int,vOrientation, Int,vWeight, UInt,vItalic, UInt,vUnderline, UInt,vStrikeOut, UInt,vCharSet, UInt,vOutPrecision, UInt,vClipPrecision, UInt,vQuality, UInt,vPitchAndFamily, Str,vFaceName, Ptr)
}

;==================================================

;vLimW and vLimH if present are used as limits
;JEE_DrawText
JEE_StrGetDim(vText, hFont, ByRef vTextW, ByRef vTextH, vDTFormat:=0x400, vLimW:="", vLimH:="")
{
	;DT_EDITCONTROL := 0x2000 ;DT_NOPREFIX := 0x800
	;DT_CALCRECT := 0x400 ;DT_NOCLIP := 0x100
	;DT_EXPANDTABS := 0x40 ;DT_SINGLELINE := 0x20
	;DT_WORDBREAK := 0x10

	;HWND_DESKTOP := 0
	hDC := DllCall("user32\GetDC", Ptr,0, Ptr)
	hFontOld := DllCall("gdi32\SelectObject", Ptr,hDC, Ptr,hFont, Ptr)
	VarSetCapacity(SIZE, 8, 0)
	vTabLengthText := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	DllCall("gdi32\GetTextExtentPoint32", Ptr,hDC, Str,vTabLengthText, Int,52, Ptr,&SIZE)
	vTabLength := NumGet(&SIZE, 0, "Int") ;cx ;logical units
	vTabLength := Floor((vTabLength/52)+0.5)
	vTabLength := Round(vTabLength*(72/A_ScreenDPI))
	vLen := StrLen(vText)

	VarSetCapacity(DRAWTEXTPARAMS, 20, 0)
	NumPut(20, &DRAWTEXTPARAMS, 0, "UInt") ;cbSize
	NumPut(vTabLength, &DRAWTEXTPARAMS, 4, "Int") ;iTabLength
	;NumPut(0, &DRAWTEXTPARAMS, 8, "Int") ;iLeftMargin
	;NumPut(0, &DRAWTEXTPARAMS, 12, "Int") ;iRightMargin
	NumPut(vLen, &DRAWTEXTPARAMS, 16, "UInt") ;uiLengthDrawn

	VarSetCapacity(RECT, 16, 0)
	if !(vLimW = "")
		NumPut(vLimW, &RECT, 8, "Int")
	if !(vLimH = "")
		NumPut(vLimH, &RECT, 12, "Int")
	DllCall("user32\DrawTextEx", Ptr,hDC, Str,vText, Int,vLen, Ptr,&RECT, UInt,vDTFormat, Ptr,&DRAWTEXTPARAMS)
	DllCall("gdi32\SelectObject", Ptr,hDC, Ptr,hFontOld, Ptr)
	DllCall("user32\ReleaseDC", Ptr,0, Ptr,hDC)

	vTextW := NumGet(&RECT, 8, "Int")
	vTextH := NumGet(&RECT, 12, "Int")
}

;==================================================
Last edited by jeeswg on 30 Oct 2019, 12:14, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: slightly-improved dialogs

15 Oct 2019, 02:23

Just found ExStyle 0x108. Doesn't seem to be in the MSDN. Since you use it here too, do you have any explanation!

Edit: O.k., lexikos explained to me it is a combination of two styles, what is obvious (now for me too) from your code. Thank You for your work!
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: slightly-improved dialogs

10 Nov 2019, 18:49

Hello rommmcek, I see that lexikos gave you a good explanation here:
Movable ToolTip - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=42257

I was already preparing some comments, so I'll post them here:

Code: Select all

;imagine 32 on/off switches (bits) each representing an extended window style:
;2**0 = 1 = 0x1 = 0b1
;2**31 = 2147483648 = 0x80000000 = 0b10000000000000000000000000000000

;I wrote the combined styles like this:
	;WS_EX_WINDOWEDGE := 0x100 ;WS_EX_TOPMOST := 0x8
	vWinExStyle := 0x108

;I could write them like this:
	WS_EX_WINDOWEDGE := 0x100 ;equivalent to 0b100000000
	WS_EX_TOPMOST := 0x8 ;equivalent to 0b1000
	vWinExStyle := WS_EX_WINDOWEDGE | WS_EX_TOPMOST

;check if the active window has an extended style:
	WinGet, vWinExStyle, ExStyle, A

	if (vWinExStyle & 0x100)
		MsgBox, % "has WS_EX_WINDOWEDGE"
	if (vWinExStyle & 0x8)
		MsgBox, % "has WS_EX_TOPMOST"

;check if the active window has both styles:
	WinGet, vWinExStyle, ExStyle, A

	if (vWinExStyle & 0x108 = 0x108)
		MsgBox, % "has both WS_EX_WINDOWEDGE and WS_EX_TOPMOST"
The key point is that styles are commonly a power of 2.

Two key things to understand are bitwise-and and bitwise-or, which use the same logic as AND gates and OR gates, but instead of comparing 1 v. 1 input value, you compare 64 v. 64 input values (AHK uses 64-bit signed integers), which I cover here:
jeeswg's mathematics tutorial - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=7&t=64545
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
rommmcek
Posts: 1470
Joined: 15 Aug 2014, 15:18

Re: slightly-improved dialogs

18 Nov 2019, 16:21

As said, it's easy once knowing styles can be combined.

P.s.: Sometimes I don't get e-mail notification of a new post.

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 34 guests