TextEditor inside a custom component in a PopupMenu


#1

Hi,

We are trying to include a TextEditor in a custom component in a popup menu. On Windows it works as expected, but on Mac OS the text editor can't receive any keyboard input. The same happens for editable labels.

I attached a small test program showing the problem.

--
Roeland

 

Since file upload doesn't work, source code below:

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

class OurSwatch : public Component,
                  public TextEditor::Listener
{
public:
    OurSwatch() : showing(false)
    {
        t.setSize(200, 20);
        t.setText(text, dontSendNotification);
        t.setColour(TextEditor::backgroundColourId, Colours::yellow);

        t.addListener(this);
    }

    void paint(Graphics &g) override
    {
        g.fillAll(Colours::yellow);
        g.setColour(findColour(Label::textColourId));
        g.drawText(text, getLocalBounds(), juce::Justification::centred, false);
    }

    void mouseDown(const MouseEvent& event) override
    {
        if (showing) return;
        showing = true;

        PopupMenu m;
        m.addCustomItem(1, &t, 200, 40, false);

        m.showMenuAsync(PopupMenu::Options().withTargetComponent(this),
                        ModalCallbackFunction::forComponent (popupMenuFinishedCallback, this));
        repaint();
    }

    void textEditorTextChanged(TextEditor &t) override
    {
        text = t.getText();
        repaint();
    }

    static void popupMenuFinishedCallback(int result, OurSwatch* us)
    {
        us->showing = false;
    }

    String text;
    bool   showing;
    TextEditor t;
};

class OurWindow : public DocumentWindow
{
public:
    OurWindow()
    :
        DocumentWindow("test", Colours::black, DocumentWindow::closeButton)
    {}

    void closeButtonPressed() override
    {
        JUCEApplication::getInstance()->quit();
    }
};


//==============================================================================
class TestApplication  : public JUCEApplication
{
public:

    DocumentWindow *w;

    //==============================================================================
    TestApplication()
    :
        w(nullptr)
    {}

    const String getApplicationName()       { return ProjectInfo::projectName; }
    const String getApplicationVersion()    { return ProjectInfo::versionString; }
    bool moreThanOneInstanceAllowed()       { return true; }

    //==============================================================================
    void initialise (const String&)
    {
        OurSwatch *sw = new OurSwatch();
        sw->setSize(200, 200);
        
        w = new OurWindow();
        w->setUsingNativeTitleBar(true);
        w->setTopLeftPosition(100,100);
        w->setContentOwned(sw, false);
        w->setVisible(true);
    }

    void shutdown()
    {
        delete w;
    }

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

    void anotherInstanceStarted (const String& commandLine) {}
};

//==============================================================================
// This macro generates the main() routine that launches the app.
START_JUCE_APPLICATION (TestApplication)

#2

I'm interested in hearing about a fix for this as well, I've spent hours on trying to fix it myself without luck unfortunately..

On OS X it's working in my plugins, but in the standalone versions the popupmenu itself is stealing the keyboard focus and any attempt of giving the texteditor keyfocus is blocked.


#3

If you don't really need a menu you can use a CallOutBox instead. The following code is roughly equivalent to popping up a menu.

    juce::CallOutBox::launchAsynchronously(popupContents, theButton->getScreenBounds(), NULL);

There are a few differences in behaviour with a PopupMenu:

  • The component will usually appear on the side of the component rather than below.
  • There is no way of launching an action when the call-out box is closed. launchAsynchronously always immediately returns.
  • popupContents is always deleted. Wrap it in another component if that's a problem.
  • If you want your software to run on Linux you'll get black borders around the CallOutBox component on any system which doesn't run a compositing window manager.

--
Roeland