Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

A handy dialogue technique! (and colorful msgbox's ^_^)


  • Please log in to reply
16 replies to this topic
Areilius
  • Members
  • 42 posts
  • Last active: May 25 2007 12:57 PM
  • Joined: 16 Feb 2006
This is an incredibly simple but useful trick that I thought the world should know.

Using WinWaitClose you can simplify GUI-forms into a single function that returns the user's input, much like FileSelectFile, InputBox and MsgBox.

Here's some examples!

This function will just wait until the user clicks OK or closes the window.
But while we're simulating MsgBox behaviour, lets add colors!
CustomMsgBox("Title","This MsgBox has a red font!","","cRed")
CustomMsgBox("Title","This MsgBox has a blue italic Tahoma font!","Tahoma","cBlue Italic")
CustomMsgBox("Title","This MsgBox has a white font and black background!!","","cWhite","Black")
CustomMsgBox("Title","This MsgBox has a black Lucida Console font and horrible yellow background!","Lucida Console","cBlack","Yellow")
ExitApp


CustomMsgBox(Title,Message,Font="",FontOptions="",WindowColor="")
{
	Gui,66:Destroy
	Gui,66:Color,%WindowColor%
	
	Gui,66:Font,%FontOptions%,%Font%
	Gui,66:Add,Text,,%Message%
	Gui,66:Font
	
	GuiControlGet,Text,66:Pos,Static1
	
	Gui,66:Add,Button,% "Default y+10 w75 g66OK xp+" (TextW / 2) - 38 ,OK
	
	Gui,66:-MinimizeBox
	Gui,66:-MaximizeBox
	
	SoundPlay,*-1
	Gui,66:Show,,%Title%
	
	Gui,66:+LastFound
	WinWaitClose
	Gui,66:Destroy
	return
	
	66OK:
	Gui,66:Destroy
	return
}

This one will return user input, like an InputBox or FileSelect does.
Choice:=MsgBox_SelectString("Title","Select a string.","One|Two|Three|Four|Five")
If !Choice
	MsgBox,You didn't choose!
Else
	MsgBox,You chose "%Choice%".
ExitApp


MsgBox_SelectString(Title,Message,Strings)
{
	Gui,55:Add,Text,,%Message%
	Gui,55:Add,ListBox,%Size%,%Strings%
	GuiControlGet,Box,55:Pos,ListBox1
	Gui,55:Add,Button,% "Default g55OK w75 y+10 xp+" (BoxW / 2) - 38,OK
	
	Gui,55:-MinimizeBox
	Gui,55:-MaximizeBox
	
	Gui,55:Show,,%Title%
	Gui,55:+LastFound
	WinWaitClose
	Gui,55:Destroy
	return Result
	
	55OK:
	GuiControlGet,Selected,55:,ListBox1
	Result:=Selected
	Gui,55:Destroy
	return ;This won't end the function, just the g55OK thread.
}

It's very useful when you want to use the same GUI many times as a dialogue.

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004
Nice idea. Thanks :)

Moderator!!
  • Members
  • 25 posts
  • Last active: Nov 05 2010 12:27 AM
  • Joined: 24 May 2006
nice idea
a suggestion
displaying the icons in user32.dll will make the message box look cool

The six icons available in USER32.DLL
used by MsgBox command

Posted Image Posted Image Posted Image Posted Image Posted Image Posted Image

Example usage: Gui, Add, Picture, Icon1, User32.dll


Edit
important note
the notable characteristic of a msgbox is that the main gui cannot be interacted before msgbox is dismissed
therefore main gui should be disabled until message gui is dismissed
Posted Image

corrupt
  • Members
  • 2558 posts
  • Last active: Nov 01 2014 03:23 PM
  • Joined: 29 Dec 2004

Edit
important note
the notable characteristic of a msgbox is that the main gui cannot be interacted before msgbox is dismissed
therefore main gui should be disabled until message gui is dismissed

The current MsgBox doesn't behave that way by default though...

Moderator!!
  • Members
  • 25 posts
  • Last active: Nov 05 2010 12:27 AM
  • Joined: 24 May 2006

The current MsgBox doesn't behave that way by default though...


yes, is not default behavior, only if Gui +OwnDialogs is specified.

Areilius
  • Members
  • 42 posts
  • Last active: May 25 2007 12:57 PM
  • Joined: 16 Feb 2006
Meh it was just an example. You can add to it if you want :)

Moderator!!
  • Members
  • 25 posts
  • Last active: Nov 05 2010 12:27 AM
  • Joined: 24 May 2006
i only thought the info would be useful for future visitors
Posted Image

Roland
  • Members
  • 307 posts
  • Last active: Mar 09 2014 07:55 PM
  • Joined: 08 Jun 2006
Thanks for this idea Areilius. I've been using custom Msgboxes a lot (because they just look nicer and can be centered on the main Gui) but up until now they all only had an "OK" button :)

Meh it was just an example. You can add to it if you want :)


Done. :)

It got a bit crazy with all the options (I can't remember the order of 5 parameters, let alone 20...), so all the options just go in one big Options parameter. Zero or more of the following strings may be present in Options :wink: :

ButtonsXXXX: The Msgbox can have up to three buttons. Either just Buttons1 (Okay), Buttons2 (Okay|Cancel), Buttons3 (Yes|No|Cancel) or specify custom button names, like this: ButtonsSave|Discard. Note: You have to replace spaces with "_": Save_and_Exit.
The Msgbox() function returns the name of the button the user clicked (note that ampersands are omitted. &Okay -> Okay).

Default*: Specifies which button should be the default button. If this option is not present, it's always the last button. To have no default button, specify Default0.

Icon*: can be one of the following: !,?,X,i; Should be self-explanatory...

Checkbox*: Adds a checkbox at the bottom left corner, for instance: CheckboxDon't_ask_me_again. The funtion sets ErrorLevel to 1 if the user checked the checkbox.

w*: The width of the Msgbox. This is useful because the text will be word-wrapped automatically. Ex: w300

c*: The text color. Ex: cGreen

Background*: Background color.

s*: Size of font.

bold, italic

h*: This is a bit confusing - this is the height of the button(s), not the window. The default is 20.

Owner*: For instance "Owner14". By default the Msgbox is owned and modal. If the owner is unspecified, the function used A_Gui, or simply 1 when A_gui is blank (though in any case it checks if the owner exists; if it doesn't, the Msgbox is simply shown normally).

-Owner: If you don't want the the Msgbox to be owned (in this case it isn't modal either, of course).

-Modal: The Msgbox can still be owned, but the user can interact with the owner while the Msgbox is beeing displayed.

-Toolwindow: By default it's a toolwindow because I like toolwindows.
+AlwaysOnTop: Guess what.
-Caption: I like to use this in combination with...
+0x800000: ...sometimes. (+0x800000 is a thinline border).

FontName has to be an extra param because it contains spaces...
Note that I used "FFFACD" as the default for Background and Comic Sans MS as the default FontName... you can change that to suit your needs.

I figured GuiNumber should be an extra param because that way it can be an expression (and the only reason I can think of to not use the default is when you're dynamically keeping track of Gui numbers...).

Note that because of the way the Options param works I added a small helper funtion to keep the code at least somewhat tidy. Make sure you include it along with the main function.

One thing that should be mentioned is that we have to turn Critical off before "WinWaitClose" in case the thread that called the function was critical. Otherwise the button subroutines are buffered until the Gui closes, which never happens because the button subroutines are buffered... :) I don't think it matters much in most cases, but if it's really critical that the thread be critical, better don't use this function in it, or at least turn on Critical after the function returns.

I just used preview - I'll better shut up. Just try it.

Gui,10: Color, FFFACD 
Gui,10: Font, s14 bold, Comic Sans MS 
Gui,10:Add, Text, c00008B, Msgbox(message="",Options="",Title="",FontName="",GuiNumber=66) 
Gui,10:Font, cCC0000 s12 
Gui,10: Add, Text, 
, Options: Buttons* Default* Icon* Checkbox* w* c* Background* s* bold italic h* Owner*`n  -Toolwindow -Modal -Owner +AlwaysOnTop -Caption +0x800000 
Gui,10:Font, s10 cblack 


message1 = Do you really want to delete these files? 
message2 = The changes you made have been saved. Do you wish to continue? 
message3 = 
( 
Sorry, an error occured. Error code: 66. 
(Note: Press Ctrl+C to copy this message to the clipboard). 
Do you wish to continue? 
) 
message4 = 
( 
Note: If you tried out Example #5, this Msgbox is will be owned by that Gui (which you couldn't close because it has no buttons:)) 
Therefore we use a different Gui numer and set the owner manually. 
) 

ex1 = Msgbox() 
ex2 = Msgbox(message1,"Buttons&Yes|&No Icon! w150 cGreen s11 BackgroundFFFACD bold","Warning","Times New Roman") 
ex3 = Msgbox(message2,"ButtonsYes|I_don't_know|Discard Icon? bold c6495ED BackgroundFFF8DC s10 w300 -Modal","Save changes?","Matisse ITC") 
ex4 = Msgbox(message3,"ButtonsYes|No IconX w250 s8 bold c8B0000 BackgroundF5DEB3 CheckboxDon't_ask_me_again.","Invalid Entry", "Comic Sans MS") 
ex5 =  Msgbox(message3,"Buttons IconX w300 s10 bold c8B0000 BackgroundF5DEB3 -Toolwindow +AlwaysOnTop -Caption +0x800000 -Owner h25","Invalid Hotkey", "Comic Sans MS") 
ex6 = Msgbox(message4, "ButtonsDestroy_Gui66|Don't_destroy_owner IconI w250 s8 bold cGreen BackgroundFFFACD Owner66","Gui67","Comic Sans MS",67) 
Loop 6 
   { 
      If a_index = 1 
            Gui,10: Add, Button, vbutton%a_index% h20 Section gG, Example %a_index% 
      else 
            Gui,10: Add, Button, vbutton%a_index% xs Section h20 gG, Example %a_index% 
      Gui, 10: Add, Text, vtxt%a_index% ys w500, % ex%a_index% 
   } 
Gui,10: Show, Autosizex100 y100, Gui 10 
return 

10GuiClose: 
   ExitApp 
return 

G: 
   If a_guicontrol = button1 
         t := Msgbox() 
   else if a_guicontrol = button2 
         t := Msgbox(message1,"Buttons&Yes|&No Icon! w150 cGreen s11 BackgroundFFFACD bold","Warning","Times New Roman") 
   else if a_guicontrol = button3 
         t := Msgbox(message2,"ButtonsYes|I_don't_know|Discard Icon? bold c6495ED BackgroundFFF8DC s10 w300 -Modal","Save changes?","Matisse ITC") 
   else if a_guicontrol = button4 
         t := Msgbox(message3,"ButtonsYes|No IconX w250 s8 bold c8B0000 BackgroundF5DEB3 CheckboxDon't_ask_me_again.","Invalid Entry", "Comic Sans MS") 
   else if a_guicontrol = button5 
      { 
         t := Msgbox(message3 
         ,"Buttons IconX w300 s10 bold c8B0000 BackgroundF5DEB3 -Toolwindow +AlwaysOnTop -Caption +0x800000 -Owner h25" 
         ,"Invalid Hotkey", "Comic Sans MS") 
      } 
   else if a_guicontrol = button6 
      { 
         t := Msgbox(message4 
         , "ButtonsDestroy_Gui66|Don't_destroy_owner IconI w250 s8 bold cGreen BackgroundFFFACD Owner66","Gui67" 
         ,"Comic Sans MS",67) 
         If t = Destroy Gui66 
               Gui, 66: Destroy 
      } 
   Tooltip, ReturnValue: %t%`nErrorLevel: %ErrorLevel% 
   SetTimer, Tip, 2500 
return 

Tip: 
   Tooltip 
return 

Msgbox(message="",Options="",Title="",FontName="",GuiNumber=66) {

global MessageBoxIcon,MessageBoxText,MessageBoxButton1,MessageBoxButton2
,MessageBoxButton3,MessageBoxButtonName1,MessageBoxButtonName2,MessageBoxCheckbox

Gui, %GuiNumber%:Default
Gui, Destroy
autotrim = %a_autotrim%   ;store it so we can reset it later
AutoTrim Off   ;necessary because of the "Options" param
Options = %a_space%%Options%%a_space%   ;leading and trailing spaces (for StrEnd())
If message =
	message = Press okay to continue.   ;this is the default for the normal Msgbox command also

;the "Buttons" option can be numeric (1-3) or a string seperated by pipes (|).
;the default is "Okay"
If (InStr(Options, "Buttons") = 0 OR InStr(Options, "Buttons1"))
	buttons = Okay
else IfInString, Options, Buttons2
	buttons = Okay|Cancel
else IfInString, Options, Buttons3
	buttons = Yes|No|Cancel
else
 buttons := StrEnd(Options,"buttons")

;If Owner is unspecified, use A_Gui (if non-blank)
;else, use Gui#1 as owner (unless -Owner" is present in Options)
;in any case, we check if the owner exists before using it
If ! InStr(Options, "-Owner") {
	owner := StrEnd(Options,"owner")
	If ! owner  {
		If a_gui <>
			owner = %a_gui%
		else
			owner = 1
		 }
	Gui, %owner%: +LastfoundExist
	If WinExist() {
			;unless specified otherwise, disable the owner.
			;note that this causes that neat flickering when
			;the user tries to interact with the owner
			IfNotInString, Options, -Modal
				Gui, %owner%: +Disabled
			Gui, +Owner%owner%
			WinGetPos, x, y, w, h   ;get the position of the owner
		 }
	else
		owner =
}

IfNotInString, Options, -Toolwindow
	Gui, +Toolwindow
IfInString, Options, +AlwaysOnTop
	Gui, +AlwaysOnTop
IfInString, Options, -Caption
	Gui, -Caption
IfInString, Options, +0x800000
	Gui, +0x800000
Gui, +lastfound -SysMenu
GuiID := WinExist()   ;used later in the Hotkey command

;get BackgroundColor and TextColor, as well as bold/italic
BgColor := StrEnd(Options, "Background")
color := StrEnd(Options, "c")
IfInString, Options, %a_space%bold%a_space%
	bold = bold
IfInString, Options, %a_space%italic%a_space%
	italic = italic
size := StrEnd(Options, "s")

;change these default values to whatever you  prefer
If FontName =
	FontName = Comic Sans MS
If BgColor =
	BgColor = FFFACD
If color =
	color = black
If size =
	size = 8

Gui, Color, %BgColor%
Gui, Font, s%size% %bold% %italic% c%color%, %fontName%
Gui, Margin, 5, 5

icon := StrEnd(Options, "Icon")
;the corresponding icon #'s
If Icon = !
	i = 2
else if icon = ?
	i = 3
else if icon = x
	i = 4
else if icon = i
	i = 5

;it gets a bit ugly now because of the sectioning with/without the icon...
If icon {
	Gui, Add, Picture, vMessageBoxIcon Section Icon%i%, User32.dll
	ys = ys
	xs = xs
}

width := StrEnd(Options, "w")
Gui, Add, Text, vMessageBoxText %ys% w%width%, %message%

Gui, Font, norm   ;I think buttons with bold font are ugly ;)

button_h := StrEnd(Options, "h")   ;just in case anyone wants to specify the button height...
If button_h =
	button_h = 20
If button_h not integer
button_h = 20

;add the buttons:
Loop, Parse, buttons, |
 {
	StringReplace, b, a_loopfield, _, %a_space%,1
	numButtons++   ;for the subroutine that positions the buttons
	If a_index = 1
		Gui, Add, Button, vMessageBoxButton%a_index% Section h%button_h% %xs% gMessageBox_MessageBoxButtonPushed, %b%
	else
		Gui, Add, Button, vMessageBoxButton%a_index% ys h%button_h% gMessageBox_MessageBoxButtonPushed, %b%
 }

checkboxText := StrEnd(Options,"Checkbox")
StringReplace, checkboxText, checkboxText, _, %a_space%,1
If checkboxText <>
	Gui, Add, Checkbox, vMessageBoxCheckbox %xs%, %checkboxText%

default_button := StrEnd(Options, "Default")

;if default_button is unspecified, use the last button
If default_button =
	default_button = %numButtons%

;to have NO default button, specify Default0
If default_button <> 0
	GuiControl, +Default, MessageBoxButton%default_button%
GuiControl,  Focus, MessageBoxButton%default_button%

;this works for the real Msgbox, so we need it too :)
Hotkey, IfWinActive, ahk_id%GuiID%
Hotkey, ^c, MessageBox_CopyMessageBoxTextToClipboard

Gui, Show, Autosize Hide, %title%
Gosub, MessageBox_MoveMessageBoxButtons   ;move the buttons

;if we have no owner, just show the Gui in the middle of the screen
;else, position is in the middle of its owner
If owner {
			Gui, +Lastfound
			WinGetPos,,,msg_w,msg_h
			msg_x := x + w/2 - msg_w/2
			msg_y := y + h/2 - msg_h/2
			Gui, Show, x%msg_x% y%msg_y%, %title%
	 }	else
Gui, Show,, %title%

;this thread can't be critical (otherwise the MessageBox_ButtonPushed thread
;would be buffered endlessly...)
Critical Off
WinWaitClose      ;wait until the user clicks one of the buttons

;turn off the hotkey and reset autotrim
Hotkey, ^c, MessageBox_CopyMessageBoxTextToClipboard, Off
Autotrim %autotrim%
If MessageBoxCheckbox = 1
	ErrorLevel = 1
else
	ErrorLevel = 0
If owner
	Gui %owner%:Default
else
	Gui, 1:Default
return return_value   ;return the return value set by the ButtonPushed thread

MessageBox_MessageBoxButtonPushed:
;if we've been asked to redraw:
StringRight, num, a_guicontrol, 1   ;the button #
GuiControlGet, return_value,,%a_guicontrol%
StringReplace, return_value, return_value, & ;get rid of the ampersand
Gui, Submit
If owner
	Gui, %owner%: -Disabled
Gui, Destroy
return

;the hotkey thread
MessageBox_CopyMessageBoxTextToClipboard:
 clipboard = %message%
return

;center the buttons:
MessageBox_MoveMessageBoxButtons:
Gui %GuiNumber%:Default
WinGetPos,,,gw
gw -= 6
GuiControlGet, txt, Pos, MessageBoxText
If ! icon
	GuiControl, Move, MessageBoxText, % "x" gw/2 - txtW/2

Loop 3
 GuiControlGet, p%a_index%, Pos, MessageBoxButton%a_index%

If numButtons = 1
 GuiControl, Move, MessageBoxButton1, % "x" gw/2 - p1w/2
else if (numButtons = 2)  {
 GuiControl, Move, MessageBoxButton1, % "x" gw/2 - ((p1w + p2w + 5)/2)
 GuiControl, Move, MessageBoxButton2, % "x" gw/2 - ((p1w + p2w + 5)/2) + p1w + 5
} else if (numButtons = 3) {
 GuiControl, Move, MessageBoxButton1, % "x" gw/2 - ((p1w + p2w + p3w + 10)/2)
 GuiControl, Move, MessageBoxButton2, % "x" gw/2 - ((p1w + p2w + p3w + 10)/2) + p1w + 5
 GuiControl, Move, MessageBoxButton3, % "x" gw/2 - ((p1w + p2w + p3w + 10)/2) + p1w + p2w + 10
}	return

}

;########## this small helper funtion also has to be present ############

;this function extracts the trailing characters of a string
;(for instance "White" from BackgroundWhite etc.)
StrEnd(O,str) {
IfNotInString, O, %a_space%%str%
	return
StringMid, e, O, InStr(O, " " str) + StrLen(str) + 1
, InStr(O, a_space, "", InStr(O, " " str) + 1) - InStr(O, " " str) - StrLen(str) - 1
return e
}



Terrapin
  • Members
  • 107 posts
  • Last active: Feb 06 2007 03:38 PM
  • Joined: 15 Aug 2005
To Both of You.... I was halfway working on modifying the original code, never finished it, kind of glad I didn't now. I think this is very cool, and appreciate it.

To either of you, what I *really* want, more than anything, is either the ability to add a control to a 'msgbox' or actually to be specific, an option to have a checkbox in the lower left corner, with smaller (probably) text, saying something like: 'Don't show this again.' Understand? Like so many apps use for helping to learn how to use the app, then when the user no longer wants the prompt or tip, they check the box. I suppose it could be returned either as ErrorLevel or in a ByRef parameter.

Thanks guys,

Bob

Roland
  • Members
  • 307 posts
  • Last active: Mar 09 2014 07:55 PM
  • Joined: 08 Jun 2006

To either of you, what I *really* want, more than anything, is either the ability to add a control to a 'msgbox' or actually to be specific, an option to have a checkbox in the lower left corner, with smaller (probably) text, saying something like: 'Don't show this again.'


Good idea - I added it. Have a look at example #4.
Not sure about the layout and/or text size though. It looks okay if the main text is bold and the window isn't to narrow - there's probably no way to get it to always look okay I guess. :)

Terrapin
  • Members
  • 107 posts
  • Last active: Feb 06 2007 03:38 PM
  • Joined: 15 Aug 2005
@Roland... somehow I missed seeing that this thread had been updated, but then noticed you posted last. So I just got this earlier tonight, and will try it out soon. I appreciate it very much that you added the checkbox, and with defineable text. I've been wanting something like that but hadn't built it, and yours goes way beyond what I had ever thought about. I also went to pick up the hotkey control to check out, before I even noticed it was you. I can be slow as Christmas, but I'll plan on letting you know how I like them, soon. Again, thanks! :!:

Roland
  • Members
  • 307 posts
  • Last active: Mar 09 2014 07:55 PM
  • Joined: 08 Jun 2006

I can be slow as Christmas, but I'll plan on letting you know how I like them


Hehe - I was wondering what happened. :) And yes, let me know what you think.

Terrapin
  • Members
  • 107 posts
  • Last active: Feb 06 2007 03:38 PM
  • Joined: 15 Aug 2005
Well, I made it here again. :) I started using the msgbox() function a couple of days ago. It is working very nicely for me, and is very helpful to me. The only thing I really modified for my use was to comment out the two lines which set the background color default, so that my default (no 'Backround' option) will be the user's system colors. I did find one problem which would most likely occur in only rare cases.

You use only the button's a_Index as a base for the 1W, 2W, etc. The app I am working on can have command-line parameters, which are accessed through digits as variable names. Not really sure how it is done, but 1W does not get the width of the button, since I am accessing %1%. I simply tacked on a 'b' for 'button' so that the resulting variables are 1bW, etc, and this corrected the problem.

The function is very, very much appreciated by me! Again, I am slow, lol, but possibly some day I will be able to show you how I've used it. BTW, your coding style is excellent. My style is pretty ragged. : )

Thanks,

Bob

Roland
  • Members
  • 307 posts
  • Last active: Mar 09 2014 07:55 PM
  • Joined: 08 Jun 2006

The only thing I really modified for my use was to comment out the two lines which set the background color default, so that my default (no 'Backround' option) will be the user's system colors.


I hate the default color. :)
Btw, you can very easily make other options the default by putting something like

Options = %Options%%a_space% bold

right at the top. (Doesn't apply to the background color, but nevertheless).


The app I am working on can have command-line parameters


Lol - that caught my eye the other day when I was looking for some built-in var and saw 1,2,3... But I thought, "Ah well, as long as nobody complains..". :D
Anyway, I edited the code to prevent this problem.

your coding style is excellent. My style is pretty ragged. : )



Oh, I write my fair share of ragged code, but thx. :)

Roland
  • Members
  • 307 posts
  • Last active: Mar 09 2014 07:55 PM
  • Joined: 08 Jun 2006
After our little discussion in Bug Reports I decided to modify the funtion to set the default window, thus omitting the ugly "Gui, %GuiNumber:". :)