JUCE Components have lots of mouse and keyboard behavior baked right in. Let’s walk through their differing event models and get clear on how they work.
For more in-depth component spelunking, see my article How JUCE Components Work.
A Component already has what you need
A JUCE Component
(including your top level Editor in a plugin) already has most of the keyboard and mouse listening functionality ready to go. There’s a huge grab bag of event related functions exposed.

Component
inherits from MouseListener
. That provides a slew of mouse related methods that you can override, giving you “out of the box” mouse support:
void Component::mouseEnter (const MouseEvent&)
void Component::mouseExit (const MouseEvent&)
void Component::mouseDown (const MouseEvent&)
void Component::mouseUp (const MouseEvent&)
void Component::mouseDrag (const MouseEvent&)
void Component::mouseMove (const MouseEvent&)
void Component::mouseDoubleClick (const MouseEvent&)
Override mouseDown
and you’ll be responding to a click (and so on).
Component
also contains methods to get you started responding to the keyboard:
bool Component::keyPressed (const KeyPress&)
bool keyStateChanged (bool isKeyDown)
void modifierKeysChanged (const ModifierKeys& modifiers)
Key and Mouse Listeners have different mechanics
On the web, mouse and keyboard events behave similarly and “bubble up” the DOM.
In JUCE, mouse and keyboard events have different mechanics and gotchas. This starts with their internal implementation:
mouseDown
and friends is called from a Component
‘s internal implementation. All the logic about how and when to send the event lives in Component
itself.
keyPressed
is called from the ComponentPeer
(the actual OS window, read up on that here).
The way they interact with components differs:
A mouseDown
will always be sent to the “child-most” or “front most” component under the cursor.
A keyPressed
event will only fire for the component that currently has keyboard focus.
As well as their defaults:
mouseDown
will fire out of the box.
keyPressed
requires that you setWantsKeyboardFocus(true)
on the component or manually grab focus with grabKeyboardFocus
.
Mouse Listening
MouseInputSourceInternal
contains the glue code responsible for determining who gets the mouse events.
It does so by calling the top level Component
‘s getComponentAt
method, which recursively digs down the component hierarchy to find the “most specific” or “front most” component at the mouse coordinates.

Note that the mouse coordinates in the mouse event’s struct are translated to be relative to getLocalBounds
of the component in question.
Responding to Mouse Events
Ok, so let’s get into some details on how to work with mouse events.
You are passed the coordinates of the mouse event, relative to the local component. This lets you do things like take action when part of a component is clicked:
void mouseDown (const juce::MouseEvent& event) override
{
auto localPoint = event.getMouseDownPosition();
auto areaYouWantClicked = getLocalBounds().removeFromTop(200);
if (areaYouWantClicked.contains (localPoint.getX(), localPoint.getY()))
{
// top half was clicked
}
}
Controlling Mouse Events
There’s a method on Component that lets you completely disable the current and/or child components’ abilities to receive mouse events.
void setInterceptsMouseClicks (bool allowClicksOnThisComponent,
bool allowClicksOnChildComponents)
By default, all (front-most) components are candidates for receiving mouse events when they are under the cursor. So this is a good way to let components “surrender” or “bubble up” control of mouse events to the next higher component in the component hierarchy.
The method is poorly named and the docs are a bit roundabout. This setting applies to all mouse events (not just clicks). Here are the options:
# default
setInterceptsMouseClicks (true, true)
# this component gets mouse events, children do not
setInterceptsMouseClicks (true, false)
# only the children get mouse events
setInterceptsMouseClicks (false, true)
# neither this component nor children get events
setInterceptsMouseClicks (false, false)
Getting specific
By implementing a component’s hitTest
function, you can further detail that you only want to receive mouse events in certain areas of the component.
Note that in order for hitTest
to be useful, the component must be receiving mouse events overall. In other words, you must pass true
as the first argument to setInterceptsMouseClicks
.
bool hitTest (int x, int y)
For bounds (relative to the local component) where you want mouse events, return true
. For bounds where you don’t want mouse events, return false
.

true
for hitTest
(shown with a dashed border). Mouse events in the left half default to the yellow component underneath.Getting Fancy
You can create additional MouseListeners
and listen to mouse events from any Component
, adding them via addMouseListener
.
For example, you could have an “interactive help” feature where you add a listener to many UI components and depending on the component, show contextual help.
Daniel’s Forum Gotcha™️: Every component is already a MouseListener, so if you add a listener, avoid having the added listener be another Component. This gets hairy. You’ll have to disentangle the events coming in for the component itself from the ones added.
Gotcha #2: As with other listeners across JUCE, whatever listeners you add in the constructor, be sure to remove in the destructor. In this case, remember to call removeMouseListener
.
Gotcha #3: The bounds in the mouse event will be relative to the Component
it was added to — if you make another component the listener, you’ll have the confusing situation of mouse events firing with bounds relative to 2 different components.
Keyboard Listening
It’s nice and easy to have a component respond to key presses. Just test against the key you are looking for:
bool keyPressed (const juce::KeyPress& key) override
{
if (key == juce::KeyPress::escapeKey)
{
this->close();
return true;
}
return false;
}
As stated, keyboard events only care about “what component is focused.” But how do we know what’s focused?
Tell JUCE your component wants focus
Unlike mouse listening, you need to take an explicit step to ever receive keyboard events. For a component to get key events, it needs to fit these criteria:
- Component is enabled
- Component is visible
- You’ve called
setWantsKeyboardFocus(true)
on the component.
This doesn’t actually focus the component, though. These are just the pre-requisites. This is you telling JUCE “yeah, this component might grab focus at some point.”
Click to focus by default
By default, clicking a visible component focuses it. You can disable this by setting setMouseClickGrabsKeyboardFocus(false)
on a component.
So, out of the box, you’ll have to click on a component to give it focus.
Manually grab focus
Sometimes you want to dictate which component gets focus.
For example, if you click a button and an overlay appears, you might want to manually give that overlay keyboard focus vs. wait for the user to click somewhere on it.
For this, you can call grabKeyboardFocus
on the component in question.
Daniel’s Forum Gotcha™️: Don’t call grabKeyboardFocus
in a component’s constructor. It only works once the component has been placed in the component hierarchy.
Respond to focus
Each Component
has a focusGained
and a focusLost
callback. So you can do things like highlight a control when it has focus.
There’s also focusOfChildComponent
.
Which component exactly gets focused?
The docs are nice and specific on this:
- if the component that was clicked on actually wants focus (as indicated by calling getWantsKeyboardFocus), it gets it. - if the component itself doesn't want focus, it will try to pass it on to whichever of its children is the default component, as determined by the getDefaultComponent() implementation of the ComponentTraverser returned by createKeyboardFocusTraverser(). - if none of its children want focus at all, it will pass it up to its parent instead, unless it's a top-level component without a parent, in which case it just takes the focus itself.
Bubbling up key events
Unlike mouse events, you can respond to keyboard events AND let them bubble up to other components.
If you are looking to capture just the ESC key, for example, don’t always return true
— this will consume the keypress.
bool keyPressed (const juce::KeyPress& key, Component* /*originatingComponent*/) override
{
if (key == juce::KeyPress::escapeKey)
{
this->closeWindow();
return true;
}
return false; // let other components handle the event too
}
Advanced keyboarding
In addition to grabbing focus you can giveAwayKeyboardFocus()
.
You can specify the order of keyboard focus with Component
methods such as setExplicitFocusOrder
.
You can also ask if a component has focus with hasKeyboardFocus()
and ask if a child has focus via hasKeyboardFocus(true)
.
You can also explicitly pass focus to the previous or next sibling component with moveKeyboardFocusToSibling
. This depends on containers and traversers, oh my.
There are more manual ways to control focus such as scoping focus with setFocusContainerType
or creating a custom ComponentTransverser
.
Troubleshooting Mouse Listening
Not getting the mouseDown
you expected?
- Check to see if
setInterceptsMouseClicks
is set on the component. If so, the first argument should betrue
. - Check to see if
setInterceptsMouseClicks
is set on parents in the hierarchy. If so, the second argument should betrue
. - Verify that a child component isn’t responding to the mouse events.
setInterceptsMouseClicks
must be false on children for a parent to receive the events.
Troubleshooting Keyboard Listening
Not getting the keyPressed
you expected?
- Check that you have grabbed focus, either with
grabFocus
or by clicking on the element. - Check to make sure
setMouseClickGrabsKeyboardFocus
wasn’t set to false on the component. - If a child is also responding to keyboard events, make sure it returns
false
onkeyPressed
so that the event bubbles up to the component in question.
Acknowledgements
Thanks to Eyal and Daniel for their (as always) valuable contributions and advice!
Leave a Reply