KeyListener documentation request

Important note! It's obviously not possible for a component to be focused 
unless it's actually visible, on-screen, and inside a window that is also visible. 
So there's no point trying to call this in the component's own constructor 
or before all of its parent hierarchy has been fully instantiated.

This text is found in the documentation for Component::grabKeyboardFocus(). After spending an hour or so trying to get KeyListener::keyPressed() to work to no avail, the documentation in this method provided some clarity and I got my component to receive key presses successfully.

It would be great if the documentation for KeyListener included this same text so other users realize you need to add your KeyListener object inside of a handleAsyncUpdate(). I believe, and please correct me if I’m wrong, if you call triggerAsyncUpdate() in your component’s constructor, the handleAsyncUpdate() will be called when that component is finally visible.
If that’s the wrong solution for getting KeyListener::keyPressed() to work, please clarify the proper solution. But if it is the correct solution, I think a lot of people would be helped if the documentation for KeyListener::keyPressed() included something along the lines of

if you're trying to listen for key presses on a target component, 
you must wait to add your KeyListener callback until the target component 
is visible.  Use a triggerAsyncUpdate or timerCallback to add it.
Don't add it in your component's constructor, when the target component is not visible yet.

I think this would help a lot of new users

MainContentComponent::MainContentComponent()
{
    triggerAsyncUpdate();
    setSize(600, 400);
}
void MainContentComponent::handleAsyncUpdate()
{
    getTopLevelComponent()->addKeyListener(this);
}
bool MainContentComponent::keyPressed(const juce::KeyPress &key, juce::Component *originatingComponent)
{
    DBG( "key pressed: " << key.getTextCharacter() );
    if( key.getTextCharacter() == 'r' )
    {
        DBG( "you pressed r" );
    }
    return false;  //this will cause a system "beep" if no component ends up consuming the event
}
1 Like

I feel your frustration, it is always bad to spend time finding such problems.
However, the comment is in Component::grabKeyboardFocus(), which is what you called to get the keyboard focus.

There could be a reminder, that the KeyListener also only receives events the original component receives (which implies, it has the keyboard focus).

The text you are proposing is actually a red herring, there is nothing wrong in attaching a KeyListener to a invisible Component or in a constructor, see the source:

You can add the listener already in advance, but you will only get an event, as soon as you managed to give the focus to that listened to component.

your component can’t gain focus until it’s visible. that’s my point. That is what needs to be explained in the keyListener docs if you’re trying to get any component to intercept keyboard commands.

@ed95 @fabian etc
suggested addition to the end of keyListener::keyPressed() description:

If no components consume the event, the OS will beep.  
you can override this behavior by overriding 
virtual void LookAndFeel::playAlertSound() 
for the top level component's assigned LookAndFeel

or something like that

I never bothered to look in the Component::keyPressed() methods because when you option-click in Xcode on the overridden keyPressed() method, it shows the KeyListener:: method help:
59%20PM

This is because you inherit the KeyListener. Do you actually need to redirect the event?

The natural way would be to implement the keyPress method of the Component itself, according to the encapsulation paradigm: keep the data and the methods who use it in one class, if possible.

The signature of the two methods are distinct, since in one case the Component is “this”:
virtual bool Component::keyPressed (const KeyPress & key)
virtual bool KeyListener::keyPressed (const KeyPress & key, Component * originatingComponent)

But a note what happens if no one consumes a keyPressed event with link to playAlertSound() would be useful, I guess.

there’s way to inherit KeyListener from a class that does’t inherit component? I’m trying to do it and ** keyPressed (const KeyPress& key, Component* originatingComponent)** is never called…

Yes it is and it should be working. It compiles, that’s step one.
The listener needs to get attached to a Component though.
You need to verify that Component actually has the focus.

A common pitfall is grabFocus() in the constructor cannot work, because it has no parent yet.

1 Like

really thank you!