I've read the definite tutorial on key bindings a few times, but my brain cache doesn't seem large enough to hold the complicated processes.
I was debugging a key binding problem (turned out I was using the wrong JComponent.WHEN_*
condition), and I stumbled upon a concise and hilarious javadoc for the package private javax.swing.KeyboardManager
by an (unfortunately) anonymous Java engineer.
My question is this: except for KeyEventDispatcher
which is checked at the very beginning, does the description miss and/or mistake anything?
The KeyboardManager class is used to help dispatch keyboard actions for the WHEN_IN_FOCUSED_WINDOW style actions. Actions with other conditions are handled directly in JComponent.
Here's a description of the symantics [sic] of how keyboard dispatching should work atleast [sic] as I understand it.
KeyEvents are dispatched to the focused component. The focus manager gets first crack at processing this event. If the focus manager doesn't want it, then the JComponent calls super.processKeyEvent() this allows listeners a chance to process the event.
If none of the listeners "consumes" the event then the keybindings get a shot. This is where things start to get interesting. First, KeyStokes [sic] defined with the WHEN_FOCUSED condition get a chance. If none of these want the event, then the component walks though it's parents looked for actions of type WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.
If no one has taken it yet, then it winds up here. We then look for components registered for WHEN_IN_FOCUSED_WINDOW events and fire to them. Note that if none of those are found then we pass the event to the menubars and let them have a crack at it. They're handled differently.
Lastly, we check if we're looking at an internal frame. If we are and no one wanted the event then we move up to the InternalFrame's creator and see if anyone wants the event (and so on and so on).
(UPDATE) If you've ever wondered about this bold warning in the key bindings guide:
Because the order of searching the components is unpredictable, avoid duplicate WHEN_IN_FOCUSED_WINDOW bindings!
It's because of this segment in KeyboardManager#fireKeyboardAction
:
Object tmp = keyMap.get(ks);
if (tmp == null) {
// don't do anything
} else if ( tmp instanceof JComponent) {
...
} else if ( tmp instanceof Vector) { //more than one comp registered for this
Vector v = (Vector)tmp;
// There is no well defined order for WHEN_IN_FOCUSED_WINDOW
// bindings, but we give precedence to those bindings just
// added. This is done so that JMenus WHEN_IN_FOCUSED_WINDOW
// bindings are accessed before those of the JRootPane (they
// both have a WHEN_IN_FOCUSED_WINDOW binding for enter).
for (int counter = v.size() - 1; counter >= 0; counter--) {
JComponent c = (JComponent)v.elementAt(counter);
//System.out.println("Trying collision: " + c + " vector = "+ v.size());
if ( c.isShowing() && c.isEnabled() ) { // don't want to give these out
fireBinding(c, ks, e, pressed);
if (e.isConsumed())
return true;
}
}
So the order of searching is actually predictable, but obviously dependent on this particular implementation, so it's better not to rely on it at all. Keep it unpredictable.
(Javadoc and code is from jdk1.6.0_b105 on WinXP.)