The message box can display one, two and three button configurations, pop-up as the topmost window, accept and display long messages which will be wrapped and even displayed in a scrollable EditorGadget() if required, and auto-terminate with a pre-defined time-out, displaying the countdown in the selected default-button. I've switched the positions of the title and message texts, to enable a short and simple calling of the message box with just the message text, as such: TimerMsgBox("Message Text"). Besides one MessageBeep() call for the sound, and one SendMessage() call to set word wrapping for the EditorGadget(), it should also be cross-platform:
Code: Select all
;=====================================================================
; MULTI-LINE, MULTI-BUTTON, TIMER-ENABLED, STAY-ON-TOP MESSAGE BOX
;
; by TI-994A - 19th November, 2012
;
; - TimerMsgBox() is a fully self-contained procedure, ready-to-use
; - possible return values: #PB_MessageRequester_Yes /_No /_Cancel
; - all text & buttons are automatically sized for the message box
; - long message texts will be wrapped and displayed multi-lined
; - extremely long texts that exceed the allowable height of the
; desktop will be displayed in a scrollable read-only EditorGadget
; - message box minimum & maximum sizes, button sizes & offsets, and
; text fonts, sizes & offsets are fully customisable and can be set
; within the procedure in the variable definitions section
;
; - this message box takes a total of five (05) parameters:
; (parameters 2, 3, 4, & 5 are optional, with default values)
; 1. message text
; 2. title text
; 3. button config (#PB_MessageRequester_Yes /_YesNo /_YesNoCancel)
; 4. auto-close timer (in seconds, not milliseconds)
; 5. default button (set with #PB_MessageRequester_Yes /_No /_Cancel
; this button will receive the focus and be the timeout response;
; it will also display the active timeout countdown in seconds)
;
; - thank you for reading and trying
; - comments & corrections are welcome
;=====================================================================
EnableExplicit
Procedure.i TimerMsgBox(message.s, title.s = "", mbFlag.i = 0, timeOut.i = 0, defaultButton.i = 1)
Protected.s OKYes = "OK", defaultButtonTxt, fontName = "Arial", fontSize.i = 8
Protected.i parsePos, parseStart, lastSpace, controlledTabOrder = 1
Protected.i wFlags, mbQuit, useEditor, timerInc, response
Protected.i boxWidth, boxHeight, boxLeft, boxTop
Protected.i boxWidthMin = 120, boxWidthMax = 300, boxVMargins = 200
Protected.i buttonTop, buttonLeft, buttonWidth = 80, buttonHeight = 25
Protected.i messageTop = 15, messageLeft = 20, messageHMargins, msgLines = 1
Protected.i txtWidth, txtHeight, messageButtonSpace = 15, buttonBottomSpace = 15
Protected.i buttons = 1, multiButtonSpace = 15, multiButtonWidth, buttonLeft2, buttonLeft3
Enumeration
#OKButton = 1
#NoButton = 2
#CancelButton = 3
#MsgBox
#MsgFont
#Message
#Timer
#Enter
#Space
#Escape
#tabOrder
EndEnumeration
If title = ""
title = "MsgBox:"
EndIf
wFlags = #PB_Window_ScreenCentered | #PB_Window_TitleBar | #PB_Window_Invisible
OpenWindow(#MsgBox, 0, 0, boxWidthMin, boxHeight, title, wFlags)
messageHMargins = messageLeft * 2
If buttonWidth + messageHMargins => boxWidthMin
buttonWidth = boxWidthMin - messageHMargins
EndIf
If mbFlag
OKYes = "Yes"
If mbFlag = #PB_MessageRequester_YesNo
buttons = 2
Else
buttons = 3 ;#PB_MessageRequester_YesNoCancel
EndIf
multiButtonWidth = ((buttonWidth * buttons) + (multiButtonSpace * (buttons + 1)))
If multiButtonWidth > boxWidthMin
If multiButtonWidth > boxWidthMax
While boxWidthMax < multiButtonWidth
buttonWidth - 1
multiButtonSpace - 1
multiButtonWidth = ((buttonWidth * buttons) + (multiButtonSpace * (buttons + 1)))
Wend
boxWidthMin = boxWidthMax
Else
boxWidthMin = multiButtonWidth
EndIf
EndIf
EndIf
Select defaultButton
Case #PB_MessageRequester_Yes
defaultButton = 1
Case #PB_MessageRequester_No
defaultButton = 2
Case #PB_MessageRequester_Cancel
defaultButton = 3
Default
defaultButton = 1
EndSelect
If defaultButton > buttons
defaultButton = 1
EndIf
controlledTabOrder = defaultButton
LoadFont(#MsgFont, fontName, fontSize)
StartDrawing(WindowOutput(#MsgBox))
DrawingFont(FontID(#MsgFont))
message = Trim(message)
txtWidth = TextWidth(message)
txtHeight = TextHeight(message)
boxWidth = txtWidth + messageHMargins
If boxWidth > boxWidthMin
If boxWidth > boxWidthMax
boxWidth = boxWidthMax
EndIf
msgLines = 0
parsePos = 1
parseStart = 1
While parsePos
parsePos = FindString(message, Space(1), parsePos)
If parsePos
If TextWidth(Mid(message, parseStart, parsePos - parseStart)) > (boxWidth - messageHMargins)
msgLines + 1
parsePos = lastSpace
parseStart = parsePos
Else
parsePos + 1
lastSpace = parsePos
EndIf
Else
If Len(message) => lastSpace
msgLines + 1
EndIf
EndIf
Wend
txtWidth = boxWidth - messageHMargins
Else
boxWidth = boxWidthMin
If txtWidth < boxWidthMin - messageHMargins
messageLeft = ((boxWidth - txtWidth) / 2)
EndIf
EndIf
StopDrawing()
ExamineDesktops()
boxHeight = messageTop + (txtHeight * msgLines) + messageButtonSpace + buttonHeight + buttonBottomSpace
If boxHeight > DesktopHeight(0) - boxVMargins
boxHeight = DesktopHeight(0) - boxVMargins
msgLines = (boxHeight - (messageTop + messageButtonSpace + buttonHeight + buttonBottomSpace)) / txtHeight
useEditor = 1
EndIf
boxTop = (DesktopHeight(0) - boxHeight) / 2
boxLeft = (DesktopWidth(0) - boxWidth) / 2
buttonTop = messageTop + messageButtonSpace + (txtHeight * msgLines)
buttonLeft = (boxWidth - buttonWidth) / 2
ResizeWindow(#MsgBox, boxLeft, boxTop, boxWidth, boxHeight)
If mbFlag
If mbFlag = #PB_MessageRequester_YesNo
buttonLeft2 = buttonLeft + (buttonWidth / 2) + (multiButtonSpace / 2)
buttonLeft - ((buttonWidth / 2) + (multiButtonSpace / 2))
Else
buttonLeft3 = buttonLeft + buttonWidth + multiButtonSpace
buttonLeft2 = buttonLeft
buttonLeft - (buttonWidth + multiButtonSpace)
ButtonGadget(#CancelButton, buttonLeft3, buttonTop, buttonWidth, buttonHeight, "Cancel")
EndIf
ButtonGadget(#NoButton, buttonLeft2, buttonTop, buttonWidth, buttonHeight, "No")
EndIf
ButtonGadget(#OKButton, buttonLeft, buttonTop, buttonWidth, buttonHeight, OKYes)
If useEditor
EditorGadget(#Message, messageLeft, messageTop, txtWidth, txtHeight * msgLines, #PB_Editor_ReadOnly)
SendMessage_(GadgetID(#Message), #EM_SETTARGETDEVICE, 0, 0)
Else
TextGadget(#Message, messageLeft, messageTop, txtWidth, txtHeight * msgLines, "", #PB_Text_Center)
EndIf
SetGadgetFont(#Message, FontID(#MsgFont))
SetGadgetText(#Message, message)
SetActiveGadget(defaultButton)
AddKeyboardShortcut(#MsgBox, #PB_Shortcut_Return, #Enter)
AddKeyboardShortcut(#MsgBox, #PB_Shortcut_Space, #Space)
AddKeyboardShortcut(#MsgBox, #PB_Shortcut_Escape, #Escape)
AddKeyboardShortcut(#MsgBox, #PB_Shortcut_Tab, #tabOrder)
StickyWindow(#MsgBox, 1)
HideWindow(#MsgBox, 0)
MessageBeep_(#MB_ICONWARNING)
defaultButtonTxt = GetGadgetText(defaultButton)
If timeOut
timeOut * 1000
AddWindowTimer(#MsgBox, #Timer, 1000)
EndIf
Repeat
Select WaitWindowEvent()
Case #PB_Event_Menu
Select EventMenu()
Case #Enter, #Space, #Escape
mbQuit = 1
Case #tabOrder
controlledTabOrder + 1
If controlledTabOrder > buttons
controlledTabOrder = 1
EndIf
SetActiveGadget(controlledTabOrder)
EndSelect
Case #PB_Event_CloseWindow
mbQuit = 1
Case #PB_Event_Gadget
Select EventGadget()
Case #OKButton, #NoButton, #CancelButton
Select EventType()
Case #PB_EventType_LeftClick
mbQuit = 1
EndSelect
EndSelect
Case #PB_Event_Timer
Select EventTimer()
Case #Timer
timerInc + 1000
SetGadgetText(defaultButton, defaultButtonTxt + " (" + Str((timeOut - timerInc) / 1000) + ")")
If timerInc >= timeOut
RemoveWindowTimer(#MsgBox, #Timer)
mbQuit = 2
EndIf
EndSelect
EndSelect
Until mbQuit
If mbQuit = 2
SetActiveGadget(defaultButton)
EndIf
Select GetActiveGadget()
Case 1
response = #PB_MessageRequester_Yes
Case 2
response = #PB_MessageRequester_No
Case 3
response = #PB_MessageRequester_Cancel
EndSelect
ProcedureReturn response
EndProcedure
;===============
; TEST SECTION:
;===============
Define.i mbFlag, defaultButton, timeOut, loop, response
Define.s testTitle, testMessage, titleExt
;message box calling conventions
TimerMsgBox("Message")
response = TimerMsgBox("Message", "Title", #PB_MessageRequester_YesNo)
If response = #PB_MessageRequester_Yes
testMessage = "You clicked >Yes<"
Else
testMessage = "You clicked >No<"
EndIf
TimerMsgBox(testMessage, "Title", #PB_MessageRequester_YesNoCancel, #PB_MessageRequester_No, 5)
;loop-test with different button configurations, message lengths, and time-outs
timeOut = 5
titleExt = " with 1 button"
mbFlag = #PB_MessageRequester_Ok
defaultButton = #PB_MessageRequester_Yes
For loop = 1 To 9
Read.s testTitle
Read.s testMessage
testTitle + titleExt
response = TimerMsgBox(testMessage, testTitle, mbFlag, timeOut, defaultButton)
If loop = 3
Restore testMsgs
timeOut = 7
titleExt = " with 2 buttons"
defaultButton = #PB_MessageRequester_No
mbFlag = #PB_MessageRequester_YesNo
ElseIf loop = 6
Restore testMsgs
timeOut = 12
titleExt = " with 3 buttons"
defaultButton = #PB_MessageRequester_Cancel
mbFlag = #PB_MessageRequester_YesNoCancel
EndIf
Next loop
End
DataSection
testMsgs:
Data.s "Really short", "OK!"
Data.s "Multi-line, maximum width", "from Wikipedia: PureBasic is a commercially distributed procedural computer programming language and integrated development environment based on BASIC and developed by Fantaisie Software for Windows 32/64-bit, Linux 32/64-bit, and Mac OS X."
Data.s "Really long, maximum height", "from Wikipedia: PureBasic is a commercially distributed procedural computer programming language And integrated development environment based on BASIC And developed by Fantaisie Software For Windows 32/64-bit, Linux 32/64-bit, and Mac OS X. An Amiga version is available, although it has been discontinued And released As open source. The first public release of PureBasic For Windows was on December 17, 2000. It has been continually updated since. PureBasic has a unique lifetime license model. As cited on the website, the very first PureBasic user (who registered in 1998) still has free access To new updates And this is Not going To change. PureBasic compiles directly To x86, x86-64, PowerPC Or 680x0 instruction sets, generating small standalone executables And DLLs which need no runtime libraries beyond the standard system libraries. Programs developed without using the platform-specific application programming interfaces (APIs) can be built easily from the same source file With little Or no modification. PureBasic supports inline assembly, allowing the developer To include FASM assembler commands within PureBasic source code, While using the variables declared in PureBasic source code, enabling experienced programmers To improve the speed of speed-critical sections of code. PureBasic has an extensive set of over 1200 native commands And direct access To most OS API calls. The editor has full project support And the compiler is thread-safe With a powerful debugger that supports breakpoints With stepping mode, variable viewer And watcher, And other debugging features common To major BASIC products. It includes a multiplatform GUI editor, And is extensible With custom made libraries in DLL form Or To incorporate at the linking stage (only used functions To reduce final size). PureBasic supports And has integrated the OGRE 3D Environment. Other 3D environments such As the Irrlicht Engine are unofficially supported. PureBasic is used To create tools And games in a fast And easy way. The very active users community share large amounts of open-source code. PureBasic has its own form designer To aid in the creation of forms For applications, but other third-party solutions are also available."
EndDataSection
Thank you.