Я прочитал определенное руководство по привязкам клавиш несколько раз, но кэш моего мозга кажется недостаточно большим, чтобы вместить сложные процессы.
Я отлаживал проблему привязки клавиш (оказалось, что использовал неправильное условие JComponent.WHEN_*
) и наткнулся на краткий и веселый javadoc для пакета private javax.swing.KeyboardManager
(к сожалению) анонимным инженером Java.
У меня такой вопрос: за исключением KeyEventDispatcher
, который проверяется в самом начале, есть ли в описании что-нибудь пропущенное и/или ошибочное?
Класс KeyboardManager используется для помощи в диспетчеризации действий клавиатуры для действий стиля WHEN_IN_FOCUSED_WINDOW. Действия с другими условиями обрабатываются непосредственно в JComponent.
Вот описание симантики [так в оригинале] того, как должна работать диспетчеризация клавиатуры, по крайней мере [так в оригинале], как я это понимаю.
KeyEvents отправляются в сфокусированный компонент. Диспетчер фокуса первым приступает к обработке этого события. Если менеджер фокуса этого не хочет, то JComponent вызывает super.processKeyEvent(), что дает слушателям возможность обработать событие.
Если ни один из слушателей не «потребляет» событие, то привязки клавиш получают выстрел. Вот тут-то и начинается самое интересное. Во-первых, KeyStokes [так в оригинале], определенные с условием WHEN_FOCUSED, получают шанс. Если ни один из них не хочет события, тогда компонент проходит, хотя его [sic] родители искали действия типа WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.
Если его еще никто не забрал, то он попадает сюда. Затем мы ищем компоненты, зарегистрированные для событий WHEN_IN_FOCUSED_WINDOW, и запускаем их. Обратите внимание, что если ни один из них не найден, мы передаем событие в строки меню и позволяем им взломать его. Они обрабатываются по-разному.
Наконец, мы проверяем, смотрим ли мы на внутреннюю рамку. Если да, и никто не хотел этого события, то мы переходим к создателю InternalFrame и смотрим, хочет ли кто-нибудь это событие (и так далее, и тому подобное).
(ОБНОВЛЕНИЕ) Если вы когда-нибудь задумывались об этом смелом предупреждении в руководстве по привязкам клавиш:
Поскольку порядок поиска компонентов непредсказуем, избегайте дублирования привязок WHEN_IN_FOCUSED_WINDOW!
Это из-за этого сегмента в 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;
}
}
Таким образом, порядок поиска на самом деле предсказуем, но, очевидно, зависит от конкретной реализации, поэтому лучше не на него вообще полагаться. Держите его непредсказуемым.
(Javadoc и код взяты из jdk1.6.0_b105 для WinXP.)