CallOutBox from CallOutBox failing on OSX

As messy as it may sound, let me just pre-emptively state that I have a perfectly legitimate and reasonable reason to be triggering a CallOutBox from within another CallOutBox :D 

On windows, it works fine. However, on OSX, I'm finding that showing a CallOutBox will cause the existing (and currently modal) CallOutBox to get dismissed via inputAttemptWhenModal(). It seems that by setting the new box to visible, some NSWindow-related stuff happens that results in this behaviour.

Here's a screenshot of the callstack (the selection has no intentional relevance).

[FYI: The 'CallOutEditor' class is just my own alternative to the CallOutBoxCallback - certainly, at this point in the callstack (its constructor), it really is no different, since setVisible is the first thing to be called - i.e. there's nothing else funky going on here!].

It took me a while to figure out how to get the debugger to let me hit breakpoints in the juce library code (thank goodness for the existing thread on that topic!). Oddly, the first time I successfully had breakpoints hit, the old callout didn't actually get dismissed, though it did get brought to the front on top of the new one. I've not seen that behaviour since though (it always dismisses when I try it now).

Any idea what could be done about it?

 

Obscure edge-case problem of the week!

Can you can save me some faffing about by posting a bit of code that I can use to reproduce it?

Sure, I'll knock some up when I get a chance :) 

Here you go, just make a new plain GUI project in the Introjucer and paste this into the main.cpp file...

#include "../JuceLibraryCode/JuceHeader.h"

//==============================================================================
class InfiniteCallOutButton :   public TextButton
{
    JUCE_DECLARE_NON_COPYABLE(InfiniteCallOutButton);
public:
    
    InfiniteCallOutButton ()
    {
        setButtonText ("Click me!");
        setSize (100,30);
        changeWidthToFitText();
    }
    
    void clicked () override
    {
        InfiniteCallOutButton* spawnedButton = new InfiniteCallOutButton ();
        CallOutBox::launchAsynchronously (spawnedButton, getScreenBounds(), nullptr);
    }
};

//==============================================================================
class Window    :   public DocumentWindow
{
    JUCE_DECLARE_NON_COPYABLE(Window);
public:

    Window ()
    :   DocumentWindow ("Test", Colours::hotpink, DocumentWindow::closeButton)
    {
        setContentOwned (new InfiniteCallOutButton, true);
        setVisible(true);
        centreWithSize(100, 100);
    }
    
    void closeButtonPressed () override
    {
        JUCEApplication::getInstance()->systemRequestedQuit();
    }
};

//==============================================================================
class CallOutBoxDemoApplication  : public JUCEApplication
{
public:
    
    ScopedPointer< Window > window;
    
    CallOutBoxDemoApplication() {}
    const String getApplicationName()       { return ProjectInfo::projectName; }
    const String getApplicationVersion()    { return ProjectInfo::versionString; }
    bool moreThanOneInstanceAllowed()       { return true; }

    void initialise (const String& commandLine)
    {
        window = new Window;
    }
    void shutdown()
    {
        window = nullptr;
    }

    void systemRequestedQuit()
    {
        quit();
    }
};

//==============================================================================
START_JUCE_APPLICATION (CallOutBoxDemoApplication)

 

[Unrelated - pasting code into this forum seems to be a bit of a mess - it seems to insert an extra newline between each line when you apply the code style to it :/ ]

Ok, try now..

That’s certainly an improvement (the new box doesn’t dismiss the old one upon showing), but alas it’s still a bit wonky, as the new box appears *behind* the old one.

Oddly, I’ve noticed that this doesn’t seem to happen if I trigger it from a PopupMenu instead of a button. Not sure why that might be though (perhaps something to do with the popup menu itself being modal - but that has surely gone by the time the box is actually triggered).

Double-oddly, when I first tried these changes, I found that clicking outside of the new box (e.g. back in the old one), caused all of the boxes to be dismissed at once, not just the more recent one. Since going back to check further, this behaviour seems to have stopped, but it was initially entirely consistent (across both my app and the above test demo). Really weird! Perhaps it was cured by a clean and rebuild.

Yeah.. not sure what's going on there with it appearing behind the other one, TBH!