MessageBox() - How to make it safe?

Just starting out? Need help? Post your questions and find answers here.
oO0XX0Oo
User
User
Posts: 78
Joined: Thu Aug 10, 2017 7:35 am

MessageBox() - How to make it safe?

Post by oO0XX0Oo »

Hi,

I haven't found a way to subclass the MessageRequester() and using a different font in it (I need a monospaced font!)
so I've created a normal window that looks like it.

We are supposed to have only one! Repeat .. Until loop regardless of how many windows we are using.

So my problem with my solution is, that it's using its own loop here. I could integrate it into a main loop that decides
per window what to do but I'd like to put this thing into a module to allow easier integration in small programs (that
don't necessarily use a window on its own)...

Additionally, will I run into problems as well because I'm using #PB_Any for all of my gadgets here, knowing that a larger
application with it's own window will get there gadget ids via an enumeration?

Code: Select all

#SQ = Chr(39)
#DCRLF = #CRLF$ + #CRLF$

; Possible icons:
; #IDI_ERROR, #IDI_EXCLAMATION, #IDI_QUESTION, #IDI_INFORMATION
Procedure MessageBox(title.s="", message.s="", width.i=0, height.i=0, icon.i=#IDI_ERROR, exit.b=#True)
  Protected.i minwidth = 460, minHeight = 325
  Protected.i hWnd, hButton, hContainer, hFont, hText
  Protected.b endLoop = #False

  ; Make sure we reach at least the minimum width & height
  If width < minwidth : width = minwidth : EndIf
  If height < minHeight : height = minHeight : EndIf

  hWnd = OpenWindow(#PB_Any, 0, 0, width, height, title, #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  If hWnd
    hButton = ButtonGadget(#PB_Any, width - 95, height - 35, 85, 25, "OK")

    hContainer = ContainerGadget(#PB_Any, 0, 0, width, height - 45)
      SetGadgetColor(hContainer, #PB_Gadget_BackColor, $FFFFFF)
      ImageGadget(#PB_Any, 25, 25, 20, 20, LoadIcon_(0, icon))

      hFont = LoadFont(#PB_Any, "Consolas", 8)
      If hFont
        SetGadgetFont(#PB_Default, FontID(hFont))
        hText = TextGadget(#PB_Any, 70, 25, width - 80, height - 70, message)
        SetGadgetColor(hText, #PB_Gadget_BackColor, $FFFFFF)
      EndIf
    CloseGadgetList()
    SetActiveGadget(hButton)

    StickyWindow(hWnd, #True)
    AddKeyboardShortcut(hWnd, #PB_Shortcut_Escape, 11)
    AddKeyboardShortcut(hWnd, #PB_Shortcut_Return, 12)

    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_Gadget, #PB_Event_CloseWindow
          endLoop = #True

        Case #PB_Event_Menu
          Select EventMenu()
            Case 11, 12 ; Escape or return key
              endLoop = #True
          EndSelect
      EndSelect
    Until endLoop = #True
  EndIf
  RemoveKeyboardShortcut(hWnd, #PB_Shortcut_All)

  If exit : End : EndIf

EndProcedure


message = #SQ + "Our app name" + #SQ + #DCRLF + " was started without parameters!" + #DCRLF +
          "Command line parameters" + #CRLF$ +
          "=======================" + #CRLF$ +
          "Required:" + #CRLF$ +
          "/file=''     | File that contains all items to rename" + #CRLF$ +
          "/editor=''   | Full path to the editor to start" + #DCRLF +
          "Optional:" + #CRLF$ +
          "/timeout=<x> | When to quit automatically [Default: 60]" + #CRLF$ +
          "/flags=<x>   | Binary combined value      [Default: 0]" + #CRLF$ +
          "               1 = Use a preview window" + #CRLF$ +
          "               2 = Allow only one instance" + #DCRLF +
          "This process will exit now!"
MessageBox("App name", message)
User avatar
TI-994A
Addict
Addict
Posts: 2700
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: MessageBox() - How to make it safe?

Post by TI-994A »

oO0XX0Oo wrote:We are supposed to have only one! Repeat .. Until loop regardless of how many windows we are using.
That's purely a recommended guideline, because while a separate event loop is running, all other processes from the main event loop would be suspended; for example, a timer. Otherwise, it's perfectly valid and safe.
oO0XX0Oo wrote:...will I run into problems as well because I'm using #PB_Any for all of my gadgets all of my gadgets here, knowing that a larger application with it's own window will get there gadget ids via an enumeration?
Dynamic object assignments via the #PB_Any directive is the safest way as they are assigned by the compiler. However, it doesn't preclude the risk of clashing if manual object assignments deliberately use large numbers; which is highly discouraged, and disallowed by the debugger.
oO0XX0Oo wrote:I could integrate it into a main loop ... but I'd like to put this thing into a module to allow easier integration
You might consider implementing a custom callback for the message box, like so:

Code: Select all

#SQ = Chr(39)
#DCRLF = #CRLF$ + #CRLF$

Procedure MessageBoxHandler()
  Select Event()
      
    Case #PB_Event_CloseWindow
      CloseWindow(EventWindow())
      
    Case #PB_Event_Gadget
      CloseWindow(EventWindow())
     
    Case #PB_Event_Menu
      Select EventMenu()          
        Case 11, 12 ; Escape or return key
          CloseWindow(EventWindow())
      EndSelect
      
  EndSelect
EndProcedure

; Possible icons:
; #IDI_ERROR, #IDI_EXCLAMATION, #IDI_QUESTION, #IDI_INFORMATION
Procedure MessageBox(title.s="", message.s="", width.i=0, height.i=0, icon.i=#IDI_ERROR, exit.b=#True)
  Protected.i minwidth = 460, minHeight = 325
  Protected.i hWnd, hButton, hContainer, hFont, hText
  Protected.b endLoop = #False
  
  ; Make sure we reach at least the minimum width & height
  If width < minwidth : width = minwidth : EndIf
  If height < minHeight : height = minHeight : EndIf
  
  hWnd = OpenWindow(#PB_Any, 0, 0, width, height, title, #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  If hWnd
    hButton = ButtonGadget(#PB_Any, width - 95, height - 35, 85, 25, "OK")
    
    hContainer = ContainerGadget(#PB_Any, 0, 0, width, height - 45)
    SetGadgetColor(hContainer, #PB_Gadget_BackColor, $FFFFFF)
    ImageGadget(#PB_Any, 25, 25, 20, 20, LoadIcon_(0, icon))
    
    hFont = LoadFont(#PB_Any, "Consolas", 8)
    If hFont
      SetGadgetFont(#PB_Default, FontID(hFont))
      hText = TextGadget(#PB_Any, 70, 25, width - 80, height - 70, message)
      SetGadgetColor(hText, #PB_Gadget_BackColor, $FFFFFF)
    EndIf
    CloseGadgetList()
    SetActiveGadget(hButton)
    
    StickyWindow(hWnd, #True)
    AddKeyboardShortcut(hWnd, #PB_Shortcut_Escape, 11)
    AddKeyboardShortcut(hWnd, #PB_Shortcut_Return, 12)
    
    BindGadgetEvent(hButton, @MessageBoxHandler())
    BindEvent(#PB_Event_Menu, @MessageBoxHandler(), hWnd)
    BindEvent(#PB_Event_CloseWindow, @MessageBoxHandler(), hWnd)
  EndIf
  
EndProcedure

wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
mainWindow = OpenWindow(#PB_Any, 0, 0, 300, 200, "Custom Message Box", wFlags)
clockDisplay = TextGadget(#PB_Any, 0, 40, 300, 50, "Clock Display", #PB_Text_Center)
showMessage = ButtonGadget(#PB_Any, 50, 100, 200, 50, "Show Message Box")
AddWindowTimer(mainWindow, clock, 1000)

Repeat
  Select WaitWindowEvent()
      
    Case #PB_Event_CloseWindow
      Select EventWindow()
        Case mainWindow
          appQuit = 1          
      EndSelect
      
    Case #PB_Event_Gadget
      Select EventGadget()
        Case showMessage          
          message.s = #SQ + "Our app name" + #SQ + #DCRLF + " was started without parameters!" + #DCRLF +
                      "Command line parameters" + #CRLF$ +
                      "=======================" + #CRLF$ +
                      "Required:" + #CRLF$ +
                      "/file=''     | File that contains all items to rename" + #CRLF$ +
                      "/editor=''   | Full path to the editor to start" + #DCRLF +
                      "Optional:" + #CRLF$ +
                      "/timeout=<x> | When to quit automatically [Default: 60]" + #CRLF$ +
                      "/flags=<x>   | Binary combined value      [Default: 0]" + #CRLF$ +
                      "               1 = Use a preview window" + #CRLF$ +
                      "               2 = Allow only one instance" + #DCRLF +
                      "This process will exit now!"          
          MessageBox("App name", message)          
          
      EndSelect
      
    Case #PB_Event_Timer
      Select EventTimer()
        Case clock
          SetGadgetText(clockDisplay, FormatDate("%hh:%ii:%ss", Date()))
      EndSelect  

  EndSelect
  
Until appQuit
Continue clicking on the Show Message Box button in the main window and multiple message boxes will pop up. Notice that they will all be handled by the same event handler independently, without suspending the main event loop.
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
infratec
Always Here
Always Here
Posts: 7583
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: MessageBox() - How to make it safe?

Post by infratec »

Sorry, was to late.
oO0XX0Oo
User
User
Posts: 78
Joined: Thu Aug 10, 2017 7:35 am

Re: MessageBox() - How to make it safe?

Post by oO0XX0Oo »

That's purely a recommended guideline, because while a separate event loop is running, all other processes from the main event loop would be suspended; for example, a timer. Otherwise, it's perfectly valid and safe.
Ok, I'll use these kind of message boxes as a "hey user, I need your attention now! (and in 99% of the cases the app is going to terminate after showing the notice)" and it's perfectly fine if the app is suspended from doing any other loop while this window is displayed.
Dynamic object assignments via the #PB_Any directive is the safest way as they are assigned by the compiler. However, it doesn't preclude the risk of clashing if manual object assignments deliberately use large numbers; which is highly discouraged, and disallowed by the debugger.
This won't be a problem, the enumerations for windows, gadgets, etc. start with 0 and end all in the low range < 100...

Thanks for the code example!

But doing it that way (of using non-blocking message boxes) couldn't I run into problems when all of them assigning the same event number to the keyboard shortcuts?

Code: Select all

    AddKeyboardShortcut(hWnd, #PB_Shortcut_Escape, 11)
    AddKeyboardShortcut(hWnd, #PB_Shortcut_Return, 12)
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: MessageBox() - How to make it safe?

Post by Josh »

@oO0XX0Oo

Could it be that you are the first victim of PedroMartin's pattern codes? :mrgreen:
  • Two event loops
  • StickyWindow()
StickyWindow() is definitely wrong for a messagebox. I think you have to put a ParentWindow on OpenWindow. Sorry, currently no PB available to test it.
sorry for my bad english
User avatar
TI-994A
Addict
Addict
Posts: 2700
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: MessageBox() - How to make it safe?

Post by TI-994A »

oO0XX0Oo wrote:...couldn't I run into problems when all of them assigning the same event number to the keyboard shortcuts?

Code: Select all

    AddKeyboardShortcut(hWnd, #PB_Shortcut_Escape, 11)
    AddKeyboardShortcut(hWnd, #PB_Shortcut_Return, 12)
No, there'll be no problems. The keyboard shortcuts are window-specific. The AddKeyboardShortcut() function indicates the window number in the first parameter, and each time the message box is called, it is assigned a different window number.
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: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: MessageBox() - How to make it safe?

Post by IdeasVacuum »

StickyWindow() is definitely wrong for a messagebox
It's not a standard MessageBox, it's a custom defined Window and StickyWindow() is intended for exactly this purpose!

Concerning the Message Window having it's own event loop, at the moment in time when it's presented to the User, it has sole focus. In that circumstance it's perfectly fine for the secondary event loop to be the 'King'.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: MessageBox() - How to make it safe?

Post by Josh »

IdeasVacuum wrote:It's not a standard MessageBox, it's a custom defined Window and StickyWindow() is intended for exactly this purpose!
Definitely NO. oO0XX0Oo was asking for a MessageBox.
  • A MessageBox blocks the application because it expects a user's decision.
  • The window of a MessageBox is displayed above the windows of the own application and not over all applications. Other applications are not interested in this message.
Maybe it will be different in Linux and MacOs (I don't know), but in Windows is a modal window for a MessageBox standard.
sorry for my bad english
oO0XX0Oo
User
User
Posts: 78
Joined: Thu Aug 10, 2017 7:35 am

Re: MessageBox() - How to make it safe?

Post by oO0XX0Oo »

It isn't a problem, another parameter "ontop.b=#False" is enough to solve that situation...
Post Reply