In this example program, a FocusableComponent is a container that intercepts all clicks anywhere, including child components, and hilites itself to indicate that it has received the focus. Everything works fine, except for the case where a popup button (or any other control) goes into a modal state in response to its mouseDown(). In this case, the focus is not changed until after the modal operation completes.
The reason is that Component::internalMouseDown() calls the mouse listeners after it calls Component::mouseDown():
// Component::internalMouseDown()
mouseDown (me);
if (checker.shouldBailOut())
return;
desktop.resetTimer();
desktop.mouseListeners.callChecked (checker, &MouseListener::mouseDown, me);
MouseListenerList::sendMouseEvent (this, checker, &MouseListener::mouseDown, me);
Here is an example program. Clicking on the button pops a menu, but the focus is not changed until after the popup menu is done with its modal state. Clicking outside the button gives focus to the container as normal.
#include "juce.h"
struct PopupButton : Button
{
PopupButton() : Button(String::empty)
{
setTriggeredOnMouseDown (true);
}
void paintButton (Graphics& g, bool over, bool down)
{
Colour c;
if (down)
c=Colours::red;
else if (over)
c=Colours::green;
else
c=Colours::blue;
g.setColour (c);
g.fillAll();
g.setColour (Colours::black);
g.drawRect (getLocalBounds(), 1);
}
void clicked ()
{
PopupMenu m;
m.addItem (1, "Juce!");
m.addSeparator ();
m.addItem (2, "Test");
m.showAt (this);
}
};
Component* focusedComponent=0;
struct FocusableContainer : Component
{
PopupButton button;
FocusableContainer ()
{
button.setBounds (48, 32, 64, 32);
addAndMakeVisible (&button);
}
void paint (Graphics& g)
{
g.setColour (Colours::grey);
g.fillAll ();
if (focusedComponent==this)
g.setColour (Colours::orange);
else
g.setColour (Colours::black);
g.drawRect (getLocalBounds(), 2);
}
};
struct FocusCatcher : MouseListener
{
FocusableContainer* owner;
FocusCatcher (FocusableContainer* owner_) : owner(owner_)
{
owner->addMouseListener (this, true);
}
void mouseDown (const MouseEvent& e)
{
focusedComponent=owner;
owner->getPeer()->getComponent()->repaint();
}
};
struct Panel : Component
{
FocusableContainer container[2];
FocusCatcher c1, c2;
Panel() : c1 (&container[0]), c2 (&container[1])
{
container[0].setBounds (0, 0, 160, 128);
addAndMakeVisible (&container[0]);
container[1].setBounds (160, 0, 160, 128);
addAndMakeVisible (&container[1]);
}
};
struct MainWindow
: DocumentWindow
, Button::Listener
{
MainWindow()
: DocumentWindow (JUCE_T("Test")
, Colours::black
, DocumentWindow::allButtons
, true )
{
Panel* p = new Panel;
p->setSize( 320, 128 );
setContentComponent (p, true, true);
centreWithSize (getWidth(), getHeight());
setVisible( true );
}
~MainWindow() {}
void buttonClicked (Button* button)
{
Component* c = getContentComponent()->getChildComponent(1);
c->setVisible (true);
c->setTopLeftPosition (64, 64);
}
void closeButtonPressed() { JUCEApplication::quit(); }
};
struct MainApp : JUCEApplication
{
MainApp() : mainWindow(0) { s_app=this; }
~MainApp() { s_app=0; }
static MainApp& GetInstance() { return *s_app; }
const String getApplicationName() { return JUCE_T("JuceTest"); }
const String getApplicationVersion() { return JUCE_T("0.1.0"); }
bool moreThanOneInstanceAllowed() { return true; }
void anotherInstanceStarted (const String& commandLine) {}
void initialise (const String& commandLine)
{
mainWindow = new MainWindow;
}
void shutdown()
{
delete mainWindow;
}
static MainApp* s_app;
MainWindow* mainWindow;
};
MainApp* MainApp::s_app = 0;
START_JUCE_APPLICATION (MainApp)
Is there something I am missing?