How to highlight/select the text in a TextEditor in an AlertWindow?

I’m displaying an AlertWindow in my plugin to allow the user to name a preset and I’d like the text in the TextEditor to be highlighted/selected when the AlertWindow opens to make it easier for the user.

I’m doing this:

AlertWindow w ("Save preset as...", "", AlertWindow::NoIcon, this);
w.addTextEditor ("presetName", "Untitled", "Preset Name");
w.addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey, 0, 0));
w.addButton ("OK",     1, KeyPress (KeyPress::returnKey, 0, 0));

// this does nothing...
TextEditor* te = w.getTextEditor("presetName");
Range<int>r(0, 8);
te->setHighlightedRegion(r);

if (w.runModalLoop() != 0) // is they picked 'ok'
{
    const int index = w.getComboBoxComponent ("list")->getSelectedItemIndex();
    String newName = w.getTextEditorContents ("presetName");     
    printf("saveAs: %d - %s\n", index, newName.toRawUTF8());
    // do the actual save
}

Nothing I do highlights the text. What am I missing?

Looks like this is a timing issue where the AlertWindow is running before it is repainted with the highlighted text. I’ll take a look at this but a way around it for the time being is to set up your AlertWindow and its TextEditor somewhere else like the constructor of the parent class before the code to run the modal loop is called. Alternatively, you could delay the runModalLoop() call by a small amount using a timer.

Ed

1 Like

Thanks for taking a look at this.

I tried putting in a very long for loop before opening the AlertWindow and it didn’t make any difference.

Try adding a te->toFront(true) after your wait loop before the modal loop. When the TextEditor gets focus it should select all the text.

You’ll notice that if you step through your code with the debugger it works… so this is a race issue.

Actually I doubt putting a loop will work unless it’s asynchronous which is why ed recommended a Timer.

Rail

Is there a way to do this without using a Timer callback? I’d really like to not fragment my code just to highlight this text.

Then follow Ed’s advice and make the AlertWindow a class object and initialize it in your class constructor so it’s ready to go when you call runModalLoop(). The only thing you’ll have to adjust is the persistence of the TextEditor text between uses.

Rail

1 Like

BTW - You shouldn’t use any modal windows in a plug-in. Rather create a new Component which covers your complete plugin UI then put a custom Component on that.

I use something like:

class CCoverComponent : public Component
{
public:
    
    CCoverComponent();
    
    void paint (Graphics&) override;
    
    void setChild (Component* pChild);
    
private:
    
    Component*  m_pChild;
    
    ////////////////////////////////////////////////////////////
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CCoverComponent)
};
CCoverComponent:: CCoverComponent() : m_pChild (nullptr)
{
    
}

void CCoverComponent::paint (Graphics& g)
{
    g.setColour (Colours::darkgrey);
    
    g.setOpacity (0.50f);
    
    g.fillAll();
    
    /////////////////////////
    
    if (m_pChild != nullptr)
        {
        DropShadow ds;
        ds.colour = Colours::white;
        
        Rectangle<int> workArea;
        
        workArea.setWidth (m_pChild->getWidth() + 6);
        workArea.setHeight (m_pChild->getHeight() + 6);
        workArea.setCentre (getLocalBounds().getCentre());
        
        ds.drawForRectangle (g, workArea);
        }
}

void CCoverComponent::setChild (Component* pChild)
{
    m_pChild = pChild;
    
    repaint();
}

I then use it like:

// Init these to nullptr in your class ctor

ScopedPointer< CCoverComponent >  m_pCover;
ScopedPointer<COptionsDlg>        m_pOptionsDlg;   // My dialog
void CParentComp::coverApp()
{
    m_pCover = new CCoverComponent();
    
    m_pCover->setBounds (getLocalBounds());
    
    addAndMakeVisible (m_pCover);
}

void CParentComp::uncoverApp()
{
    m_pCover = nullptr;
}

void CParentComp::showOptionsDlg()
{
    coverApp();
    
    m_pOptionsDlg = new COptionsDlg (this);
    
    Point<int> centerPnt = getLocalBounds().getCentre();
    
    m_pOptionsDlg->setTopLeftPosition (centerPnt.x - m_pOptionsDlg->getWidth() / 2, centerPnt.y - m_pOptionsDlg->getHeight() / 2);
    
    addAndMakeVisible (m_pOptionsDlg);

    m_pCover->setChild (m_pOptionsDlg);
}

void CParentComp::closeOptionsDlg (bool bOkay)
{
    m_pOptionsDlg = nullptr;
    
    uncoverApp();

    if (bOkay)
        {
        // Do something...
        }
}

And in my dialog component class:

void COptionsDlg::buttonClicked (Button* buttonThatWasClicked)
{
    :
    
    if (buttonThatWasClicked == &m_OkayButton)
        {
        m_pParent->closeOptionsDlg (true);
        }

    if (buttonThatWasClicked == &m_CancelButton)
            {
            m_pParent->closeOptionsDlg (false);
            }
}

Rail

1 Like

Whoa… I made the somewhat horrible assumption that because it’s in Juce, using an AlertWindow would be ok. It would be nice if the documentation explained this.

And why wouldn’t there be a plugin safe AlertWindow class?

Oh well.

From the Component class source code:

/** Runs a component modally, waiting until the loop terminates.

        This method first makes the component visible, brings it to the front and
        gives it the keyboard focus.

        It then runs a loop, dispatching messages from the system message queue, but
        blocking all mouse or keyboard messages from reaching any components other
        than this one and its children.

        This loop continues until the component's exitModalState() method is called (or
        the component is deleted), and then this method returns, returning the value
        passed into exitModalState().

        Note that you SHOULD NEVER USE THIS METHOD! Modal loops are a dangerous construct
        because things that happen during the events that they dispatch could affect the
        state of objects which are currently in use somewhere on the stack, so when the
        loop finishes and the stack unwinds, horrible problems can occur. This is especially
        bad in plugins, where the host may choose to delete the plugin during runModalLoop(),
        so that when it returns, the entire DLL could have been unloaded from memory!
        Also, some OSes deliberately make it impossible to run modal loops (e.g. Android),
        so this method won't even exist on some platforms.

        @see enterModalState, exitModalState, isCurrentlyModal, getCurrentlyModalComponent,
             isCurrentlyBlockedByAnotherModalComponent, ModalComponentManager
    */
    int runModalLoop();

Rail

I see. This warning should also be in AlertWindow.

Thanks again.

1 Like