showMessageBoxAsync doesn't take a callback as a parameter


#1

I’m in need of a regular “OK” message box on the mobile platforms. I want to use the NativeMessageBox::showMessageBoxAsync method but it doesn’t appear to take in a callback as a parameter like the OK/Cancel and Yes/No ones do. The documentation suggests it does (maybe it did at one point). Is it like that on purpose?


#2

(Oops, looks like I copy-pasted the comments for those functions without checking them, thanks, I’ll tidy it up)

Not sure why I didn’t add a callback for that one - I should do that.


#3

I see you’ve added the callback to AlertWindow::showMessageBoxAsync. I made the necessary changes to the NativeMessageBox methods:

juce_AlertWindow.h (278) - Edited the documentation for the callback param, doesn’t talk about Ok/Cancel anymore.

@param callback if this is non-null, the box will be launched asynchronously, returning immediately, and the callback will receive a call to its modalStateFinished() when the box is dismissed, with its parameter being 0. The callback object will be owned and deleted by the system, so make sure that it works safely and doesn't keep any references to objects that might be deleted before it gets called.

juce_AlertWindow.cpp (642) - NativeMessageBox::showMessageBoxAsync is given the callback param

[code]void AlertWindow::showMessageBoxAsync (AlertIconType iconType,
const String& title,
const String& message,
const String& buttonText,
Component* associatedComponent,
ModalComponentManager::Callback* callback)
{
if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
{
NativeMessageBox::showMessageBoxAsync (iconType, title, message, associatedComponent, callback);
}
else
{
AlertWindowInfo info (title, message, associatedComponent, iconType, 1, callback, false);
info.button1 = buttonText.isEmpty() ? TRANS(“ok”) : buttonText;

    info.invoke();
}

}[/code]

juce_NativeMessageBox.h (65) - Added callback param documentation, added function parameter

@param callback if this is non-null, the box will be launched asynchronously, returning immediately, and the callback will receive a call to its modalStateFinished() when the box is dismissed, with its parameter being 0. The callback object will be owned and deleted by the system, so make sure that it works safely and doesn't keep any references to objects that might be deleted before it gets called. */ static void JUCE_CALLTYPE showMessageBoxAsync (AlertWindow::AlertIconType iconType, const String& title, const String& message, #if JUCE_MODAL_LOOPS_PERMITTED Component* associatedComponent = nullptr, ModalComponentManager::Callback* callback = nullptr); #else Component* associatedComponent, ModalComponentManager::Callback* callback); #endif

juce_win32_Windowing.cpp (2892) - Added parameter to showMessageBoxAsync, using same message box creation code as NativeMessageBox::showOkCancelBox

[code]void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
const String& title, const String& message,
Component* associatedComponent,
ModalComponentManager::Callback* callback)
{
//new WindowsMessageBox (iconType, title, message, associatedComponent, MB_OK, 0, true);

ScopedPointer<WindowsMessageBox> mb (new WindowsMessageBox (iconType, title, message, associatedComponent,
                                                            MB_OK, callback, callback != nullptr));
if (callback == nullptr)
{
    mb->getResult();
    return;
}

mb.release();

}[/code]

juce_mac_Windowing.mm (116) - Added parameter to showMessageBoxAsync, using same message box creation code as NativeMessageBox::showOkCancelBox

void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType, const String& title, const String& message, Component* /*associatedComponent*/, ModalComponentManager::Callback* callback) { //new OSXMessageBox (iconType, title, message, "OK", nullptr, nullptr, nullptr, true); OSXMessageBox::show (iconType, title, message, callback, "OK", nullptr, nullptr); }

juce_ios_Windowing.mm (188) - Added parameter to showMessageBoxAsync, using same message box creation code as NativeMessageBox::showOkCancelBox

[code]void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
const String& title, const String& message,
Component* associatedComponent,
ModalComponentManager::Callback* callback)
{
//JUCE_AUTORELEASEPOOL
//new iOSMessageBox (title, message, @“OK”, nil, nil, 0, true);
ScopedPointer mb (new iOSMessageBox (title, message, @“OK”, nil, nil, callback, callback != nullptr));

if (callback == nullptr)
{
    mb->getResult();
    return;
}

mb.release();

}[/code]

juce_linux_Windowing.cpp (3387) - Added parameter to showMessageBoxAsync, using same message box creation code as NativeMessageBox::showOkCancelBox

void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType, const String& title, const String& message, Component* associatedComponent, ModalComponentManager::Callback* callback) { AlertWindow::showMessageBoxAsync (iconType, title, message, associatedComponent, callback); }


#4

Thanks, though there were some problems with that code - the method must always run asynchronously, but some of your code would have blocked if the callback was null. I’ve had a quick go at implementing it now if you want to try it out…


#5

Oh right, I hadn’t even thought about the fact that the method’s sole purpose is to be async. I’ll just blame it on copy + paste.

I will check it out, thanks.


#6

Unless I've very much mistaken, the callback objects can leak when using native alert windows. With a few changes, this is (fairly) easy to reproduce in the JUCE Demo.


In WidgetsDemo.cpp performDemoMenuItem, change the showMessageBoxAsync call to pass in a callback:

AlertWindow::showMessageBoxAsync (icon,
                                  "This is an AlertWindow",
                                  "And this is the AlertWindow's message.",
                                  "ok",
                                  0,
                                  ModalCallbackFunction::forComponent (asyncMsgBoxCallback, this));
                                              


And add an implementation for the callback function:

static void asyncMsgBoxCallback (int result, WidgetsDemo*)
{
}


    
By default, JUCE will use its own alert boxes, which don't leak. You can witness this by going to the Widgets page, "click for a popup menu..", "AlertWindow demonstrations", "Show an alert-window with a warning icon...".


However, if we change the default look and feel to use the OS native Alert Windows by adding the following line to ApplicationStartup.cpp JUCEDemoApplication::initialise

LookAndFeel::getDefaultLookAndFeel().setUsingNativeAlertWindows (true);
        

then the callback object will leak.


The difference seems to be due to the fact that JUCE's async alert windows are handled by the ModalComponentManager, which cleans up nicely after itself.


I'd suggest changing the weak references in juce_mac_Windowing.mm, juce_win32_Windowing.cpp and juce_ios_Windowing.mm from ModalComponentManager::Callback* callback to ScopedPointer<ModalComponentManager::Callback> callback, and let these native alert window wrapper objects take ownership of the callbacks.
 


#7

Thanks, I think you're right about that - I'd better take a look..