[SOLVED] Keeping keyboard focus

If i want to have a single component implement the ApplicationCommandTarget and receive all keyboard input, how should i configure the keyboard focus matters?

ATM i’m calling setWantsKeyboardFocus (true) and grabKeyboardFocus () (with a delay) in the constructor, but as there are tons of other components in the UI, those other components autonomously grab the keyboard focus on various occasions.

I’ve started adding calls to setWantsKeyboardFocus (false) and setMouseClickGrabsKeyboardFocus (false) on any offending child components, but that feels just wrong, as there are many and the whole app would break the minute any new button was added without adding also these calls. What is the proper way to control keyboard focus for this - what seems to me - extremely simple goal of keeping it in a single component?

[EDIT] I’m interested in standalone Windows app. AFAIK this is not so much of a problem on Mac apps.

You should use ApplicationCommandManager::setFirstCommandTarget to set your one command target as the first to be checked. This takes out the hole keyboard focus stuff. When you overwrite ::getNextTarget (or so) from ApplicationCommandTarget you may return null as your target handles all commands anyway.

Thanks @Rincewind ! I was missing the setFirstCommandTarget, but unfortunately adding it doesn’t help. The app stops responding to keyboard commands if i click on a list or a button for example.

Here’s how i’m setting it up in the ApplicationCommandTarget component’s constructor.

commandManager.registerAllCommandsForTarget (this);
getTopLevelComponent ()->addKeyListener (commandManager.getKeyMappings ());
if (model.getConfiguration ().getKeymapConfigurationFile ().exists ()) {
	commandManager.getKeyMappings ()->restoreFromXml (*juce::parseXML (model.getConfiguration ().getKeymapConfigurationFile ()));
}
commandManager.setFirstCommandTarget (this);

I think in that case you will have to debug that :confused: As far as I know this should work.

Thanks for your help!

In an attempt to debug this i wrote a timer that traverses the component hierarchy once per second and reports the component that has focus. You were correct. In this way the keyboard focus has no effect to the command manager, which is good.

What i found accidentally, was that the main component somehow gets kicked out of the update loop as a result of one of my commands. The command manager does not receive key presses and the timer doesn’t execute. The updates can only be restored by clicking on the main component (i.e. background).

I’m trying to figure out the cause of this but it doesn’t appear obvious how to do it.

JUCE uses this stuff in AudioPluginHost but not perfectly, there are a few pitfalls in there that I completely missed concerning the menu bar.

In my project(s) I always connect the keypress listener directly to the document window. Is that possible for you/ are you sure your getTopLevelComponent() is doing that?

1 Like

Yes yes. The getTopLevelComponent () returns this instead of the DocumentWindow during construction. Ensuring commandManager is listening the DocumentWindow key presses seems to fix everything and remove the need for grabbing keyboard focus in the main component.

Thank you @Rincewind ! You saved me countless hours of miserable debugging in despair.

2 Likes