MessageBox with multi-line text & auto-close timer

Share your advanced PureBasic knowledge/code with the community.
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

MessageBox with multi-line text & auto-close timer

Post by TI-994A »

Hello everyone. I'm sure that this feature has been done time and again, but I thought that I'd share my particular version with the forum. As always, I'd appreciate your kind comments, corrections and suggestion, and if for no other reason, hopefully, we can all learn something from it.

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
It works well enough, but like anything else, it's not foolproof. Even so, I hope that it can be useful in some way or another. Please do give it a try, and let me know what you think.

Thank you.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
yrreti
Enthusiast
Enthusiast
Posts: 546
Joined: Tue Oct 31, 2006 4:34 am

Re: MessageBox with multi-line text & auto-close timer

Post by yrreti »

Nice job TI-994A

Looks real nice, and very functional.
I just made a slight change and added the bordered background to your code if you like.

oops:I just added the preface part of TI-994A's code above, which I forgot to include earlier when
I was playing with the code. Plus added a line stating that I Modified and added the bordered
background to distinguish between the two codes, in case you prefer not to add the bordered
background. Because whether you use the previous code, or this slightly modified one.
The credit goes to TI-994A for the nice code. :D

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
;=====================================================================
;Modified and  added the bordered background  - yrreti
;=====================================================================
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
    #Message1
  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(#Message1,  2, messageTop-5, WindowWidth(#MsgBox) -5, (txtHeight * msgLines)+10, "", #WS_BORDER)
    SetGadgetColor(#Message1,#PB_Gadget_BackColor,$F7F7F7)
    TextGadget(#Message, messageLeft, messageTop, txtWidth, txtHeight * msgLines, "");, #PB_Text_Center)
    SetGadgetColor(#Message,#PB_Gadget_BackColor,$F7F7F7)
  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
yrreti
Last edited by yrreti on Tue Nov 20, 2012 2:16 pm, edited 1 time in total.
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Re: MessageBox with multi-line text & auto-close timer

Post by rsts »

Nice and useful.

Thanks for sharing.
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: MessageBox with multi-line text & auto-close timer

Post by TI-994A »

yrreti wrote:Looks real nice, and very functional.
rsts wrote:Nice and useful.

Thanks for sharing.
I'm so glad! Thank you for saying so.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
said
Enthusiast
Enthusiast
Posts: 342
Joined: Thu Apr 14, 2011 6:07 pm

Re: MessageBox with multi-line text & auto-close timer

Post by said »

As mentioned above nice/useful, i like the one procedure call! Thanks for sharing
User avatar
Mindphazer
Enthusiast
Enthusiast
Posts: 346
Joined: Mon Sep 10, 2012 10:41 am
Location: Savoie

Re: MessageBox with multi-line text & auto-close timer

Post by Mindphazer »

Great post !
Too bad it uses windows API (send message_(), message beep_())
But it works if you comment the two lines with these APIs...
MacBook Pro 14" M1 Pro - 16 Gb - MacOS 14 - Iphone 15 Pro Max - iPad at home
...and unfortunately... Windows at work...
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: MessageBox with multi-line text & auto-close timer

Post by TI-994A »

said wrote:As mentioned above nice/useful, i like the one procedure call! Thanks for sharing
Mindphazer wrote:Great post !
Too bad it uses windows API (send message_(), message beep_())
But it works if you comment the two lines with these APIs...
Thank you guys. Much appreciated.

Mindphazer, the MessageBeep() function can easily be replaced with PureBasic's PlaySound() function, although it has to be assigned a sound resource. As for the SendMessage() function, I still have no idea how to set the EditorGadget() to wrap automatically from within PureBasic's native functions. The message box still works without it, but not with the extremely long texts.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
yrreti
Enthusiast
Enthusiast
Posts: 546
Joined: Tue Oct 31, 2006 4:34 am

Re: MessageBox with multi-line text & auto-close timer

Post by yrreti »

Hi TI-994A

I have a Desktop, and was playing around with the 'If useEditor' part of your code, but had to add
a 'Really Really' long data line to test it. I wanted to add margins, so I added the following code.
It works, but it does use API. At any rate the code changes are listed here if you would like to
experiment more, as I've got to go to work now.
Thanks again for this great code

yrreti

Code: Select all

;added the changes to If useEditor here
  If useEditor
    ;EditorGadget(#Message, messageLeft, messageTop, txtWidth, txtHeight * msgLines, #PB_Editor_ReadOnly)
    ;EditorGadget(#Message, 8, messageTop, WindowWidth(#MsgBox) -8, txtHeight * msgLines, #PB_Editor_ReadOnly)
    
    Define LMargin = 5
    Define RMargin = 5
    Define TMargin = 5
    Define BMargin = 10
        
    ContainerGadget(#cmnt_cntainer,6,6,WindowWidth(#MsgBox)-12,(txtHeight * msgLines)+18,#PB_Container_Flat)
    SetGadgetColor(#cmnt_cntainer,#PB_Gadget_BackColor,$F7F7F7)
    EditorGadget(#Message,LMargin,TMargin,WindowWidth(#MsgBox)-8-LMargin-RMargin,(txtHeight * msgLines),#PB_Editor_ReadOnly)
    SendMessage_(GadgetID(#Message), #EM_SETTARGETDEVICE, 0, 0)
    ;apply margins
    SetWindowLong_(GadgetID(#Message), #GWL_EXSTYLE,GetWindowLong_(GadgetID(#Message), #GWL_EXSTYLE)&(~#WS_EX_CLIENTEDGE))
    ;set to top of message
    SetWindowPos_(GadgetID(#Message), 0, 0, 0, 0, 0, #SWP_SHOWWINDOW | #SWP_NOSIZE | #SWP_NOMOVE | #SWP_FRAMECHANGED)
    CloseGadgetList()    
    SendMessage_(GadgetID(#Message), #EM_SETTARGETDEVICE, 0, 0)



;change the loop to 10
For loop = 1 To 10;9



;Just replace the Data section, I just added the fourth line Data.s "Really Really long,
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."
  Data.s "Really 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. 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
WilliamL
Addict
Addict
Posts: 1224
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: MessageBox with multi-line text & auto-close timer

Post by WilliamL »

Thanks TI-994A!

Works fine on my Mac (with above two lines rem'ed out). Unfortunately, the font is too small for me and when I use a larger size (up to 12 pt) the messa(ge) is truncated (first default example). I've looked at the code and can't see, right off, what is going on. Actually, even at 8 point, the messag(e) is truncated. So something is not calculating correctly. It would be easy, on the Mac, to add 'word-wrap' to the editor gadget for long strings using Cocoa api.

I see that using an event loop inside a Procedure makes the window 'modular' and also make the routine totally self-contained.

[later] I discovered that the problem I had above was due to the value returned by TextWidth() in the version of pb that I am using (Mac 5.00x64). I have reported it in the Mac Bugs forum.

for now this works for me

Code: Select all

  txtWidth = TextWidth(message)*1.4 
  txtHeight = TextHeight(message)*1.14
Last edited by WilliamL on Fri Dec 07, 2012 8:41 pm, edited 3 times in total.
MacBook Pro-M1 (2021), Sonoma 14.4.1, PB 6.10LTS M1
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: MessageBox with multi-line text & auto-close timer

Post by IdeasVacuum »

Thank you for sharing TI-994A. Very useful code, I haven't used it yet but I will since I have not played with timers as yet.

I noticed that there is an ExamineDesktops() section to size the box, but no parsing of the desktops is done.....
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
NicknameFJ
User
User
Posts: 90
Joined: Tue Mar 17, 2009 6:36 pm
Location: Germany

Re: MessageBox with multi-line text & auto-close timer

Post by NicknameFJ »

Thank you for sharing TI-994A. Great work - but ....

you should use #PB_ANY for the Gadget´s and Window so your procedure doesn´t interfere with the main Programm in which your proc is included.



NicknameFJ
PS: Sorry for my weird english, but english is not my native language.



Image
User avatar
TI-994A
Addict
Addict
Posts: 2512
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: MessageBox with multi-line text & auto-close timer

Post by TI-994A »

Firstly, thank you everyone for your kind comments. I truly appreciate all your invaluable feedback. :D
WilliamL wrote:Works fine on my Mac (with above two lines rem'ed out). Unfortunately, the font is too small for me and when I use a larger size (up to 12 pt) the messa(ge) is truncated (first default example). I've looked at the code and can't see, right off, what is going on. Actually, even at 8 point, the messag(e) is truncated. So something is not calculating correctly. It would be easy, on the Mac, to add 'word-wrap' to the editor gadget for long strings using Cocoa api.
Hi WilliamL, and sorry for the late reply. I managed to test the message box on OSX, but I can't seem to replicate the issue. Perhaps I have misunderstood, so I have attached a screenshot of them running properly on the Mac. BTW, regarding the word-wrapping setting in the Cocoa API, I would be grateful if you could guide me on how to implement it. Thank you.
Image
IdeasVacuum wrote:I noticed that there is an ExamineDesktops() section to size the box, but no parsing of the desktops is done.....
Hi IdeasVacuum. I'm not sure what parsing the desktop means, but I called the ExamineDesktops() function to obtain the DesktopWidth() and DesktopHeight() for centering the message box on the screen.
NicknameFJ wrote:...you should use #PB_ANY for the Gadget´s and Window so your procedure doesn´t interfere with the main Programm in which your proc is included.
Hello NicknameFJ. Thank you for the great tip. I placed the enumeration within the procedure for just that reason, but failed to include the #PB_Compiler_EnumerationValue flag. This, as you have pointed out, will most definitely interfere with the main program. #PB_Any is clearly the best option. I'll be sure to modify the listing with this. Thanks again.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: MessageBox with multi-line text & auto-close timer

Post by IdeasVacuum »

Ah, what I mean is, the code does not examine the Desktops - there could be more than one Monitor, so your sizing could be based on the wrong one.........

What I have found though is that it's difficult to define a MessageBox that suits all occasions, so I have used yours to learn about Window timers and for any apps I code that need it, they'll get a box specifically designed to suit - after all, it takes next to no time to design a form.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
jamirokwai
Enthusiast
Enthusiast
Posts: 772
Joined: Tue May 20, 2008 2:12 am
Location: Cologne, Germany
Contact:

Re: MessageBox with multi-line text & auto-close timer

Post by jamirokwai »

Hi there,

have a look here: http://purebasic.fr/english/viewtopic.php?f=12&t=50327

You may get some ideas from my code which also includes some languages, hotkeys, and icons...
Regards,
JamiroKwai
WilliamL
Addict
Addict
Posts: 1224
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: MessageBox with multi-line text & auto-close timer

Post by WilliamL »

With thanks to wilbert for his library and Cocoa code, word wrap can be added to TI-994A's code.
[update] With 5.10b1 the EditorGadget has a #PB_Editor_WordWrap flag that can be used for word-wrap.

Code: Select all

  If useEditor
      EditorGadget(#Message, messageLeft, messageTop, txtWidth, txtHeight * msgLines, #PB_Editor_ReadOnly)
      CompilerSelect  #PB_Compiler_OS
        CompilerCase #PB_OS_Windows
          SendMessage_(GadgetID(#Message), #EM_SETTARGETDEVICE, 0, 0)
        CompilerCase #PB_OS_MacOS ; must be using Cocoa library by wilbert!
          Protected Frame.NSRect, Size.NSSize, Inset.NSSize
          Protected Container.i = CocoaMessage(0, GadgetID(#Message), "textContainer")
          CocoaMessage(0, GadgetID(#Message), "setHorizontallyResizable:", ~#True)
          CocoaMessage(@Size, Container, "containerSize")
            CocoaMessage(@Frame, GadgetID(#Message), "frame")
            CocoaMessage(@Inset, GadgetID(#Message), "textContainerInset")
            Size\width = Frame\size\width - Inset\width
          CocoaMessage(0, Container, "setContainerSize:@", @Size)
          CocoaMessage(0, Container, "setWidthTracksTextView:",#True)
      CompilerEndSelect
  Else
    TextGadget(#Message, messageLeft, messageTop, txtWidth, txtHeight * msgLines, "", #PB_Text_Center) ; |#PB_Text_Border)
  EndIf
As a work-around of the TextWidth() not giving the correct width in some Mac pb versions this can be used.

Code: Select all

  txtWidth = TextWidth(message)*1.4 ; for Mac only
  txtHeight = TextHeight(message)*1.14 ; for Mac only
Last edited by WilliamL on Sun Dec 23, 2012 4:27 am, edited 4 times in total.
MacBook Pro-M1 (2021), Sonoma 14.4.1, PB 6.10LTS M1
Post Reply