![]() ![]() |
![]() |
![]() ![]() ![]() ![]() |
![]() |
Swing's support for keyboard UI is designed with the following goals in mind:
The best way to make any Swing-based application usable without a mouse is to make sure that no additional work is required from the application developer to make the application support keyboard UI. This really means that widgets developed in Swing should be able by themselves to negotiate with the system, declaring their favorite keyboard equivalencies and when these keyboard equivalencies are available. For some widgets, such as menus, the application developer will have to define the accelerators that make sense. For widgets such as buttons, the application developer will have to specify which button is the default button. These features are really widget properties and will be easy to handle inside an application builder.
From a widget developer's point of view, it should be very easy to support keyboard input. For a given feature, the code should be the same whether the input device that has triggered the feature is a keyboard or a mouse. This means that the input device that has triggered the feature is hidden from the code that actually implements the feature. One way to do this in Swing is to encapsulate the feature inside a Action object. Action objects can also answer the needs of another requirement: It is useful for a keyboard UI system to be able to discover whether a widget can perform an action. If the widget cannot perform the action, perhaps another widget that supports the same combination of keys should perform its action.
Assuming that any feature accessible from the keyboard has been factored into a set of Action, the last piece of code that should be implemented to drive a widget from the keyboard is the code that actually maps a combination of keys to an action. Since it is possible for a widget to perform an action even if it does not have the focus (think about a keyboard mnemonic or a menu), and because it is desirable to avoid requiring every single widget to provide the same kind of code to check the key event to trigger the right action, a solution is to have a registry system that handles the mapping.
JComponent provides a set of methods that allows ComponentUI implementers to register and unregister an action for a set of keys. Also JComponent contains the implementation of the keyboard action registry.
The keyboard action registry's responsibility is to know about all the keys that can be handled by a tree of component and to invoke the right action when a set of keys is pressed. When a ComponentUI instance installs its UI, it registers its actions and keys. When the ComponentUI instance removes its UI, it removes its actions. Actual bindings are stored into the JComponent's properties.
Registering an action into the registry looks like this:
registerKeyboardAction(Action anAction, KeyStroke aKeyStroke, int aCondition)
The anAction parameter is the action that should be invoked when the proper keys are pressed and aCondition is verified. The aKeyStroke parameter describes the key(s) that will trigger the action. KeyStroke can store whether keys should be pressed or released for the action to be invoked. The aCondition parameter is an int value that describes when the action should be invoked. Its value can be:
When an action is invoked, the keyboard registry determines what component should carry out the action by examining components that could handle the action in the following order:
The following pseudo-code illustrates how this sequence of actions takes place:
Input: a KeyEvent KE
If the focused component has some registered actions and one of these registered action A has been registered with (charKey,modifiers) that matches KE, do if A is enabled, invoke A and stop done else for all components C on the path from the focused component's parent to the root of the component tree (both included) do if C has registered an action A with a (charKey,modifiers) that matches KE and a condition equals to WHEN_IN_FOCUSED_WINDOW or ALWAYS do if A is enabled, invoke A and stop done done for all components C in the focused component tree excluding the focused component do if C has registered an action A with a (charCode,modifiers) that matches KE and a condition equals to WHEN_IN_FOCUSED_WINDOW or ALWAYS, do if A is enabled invoke A and stop done done
Finally, if an action A has been registered with a (charCode,modifiers) that matches
KE and a condition equals to ALWAYS if A is enabled invoke A.
The basic idea is that JComponent overloads processKeyEvent() in the following way:
public void processKeyEvent(KeyEvent anEvent) { super.processKeyEvent(anEvent); if(anEvent.isConsumed()) return; /*** Give the keyboard UI registry system a chance to process the key event ***/ if(anEvent.getID() == KeyEvent.KEY_PRESSED) { processKeyBindings(anEvent); } }
It is the responsibility of all ComponentUI objects to mark the event consumed if the event has been consumed.
This is the proposed API for the keyboard registry:
public class JComponent { .... /** Register a new keyboard action. * anAction will be invoked if a key event matching aKeyStroke * occurs and aCondition is verified. aCondition can currently be: * <UL> * <LI> WHEN_FOCUSED: the action will be invoked only when the receiving * JComponent has the focus. * <LI> WHEN_IN_FOCUSED_WINDOW: the action will be invoked when the * receiving JComponent has the focus or is in the window that contains the focused container. * </UL> * If an action has already been registered for the receiving container, with the same key stroke, * anAction will replace the action. */ public void registerKeyboardAction(Action anAction,KeyStroke aKeyStroke,int aCondition); /** Unregister a keyboard action given a KeyStroke **/ public void unregisterKeyboardAction(KeyStroke aKeyStroke); /** Return the registered key strokes **/ public KeyStroke[] getRegisteredKeyStrokes(); /** Return the condition that has been registered with a key stroke **/ public int getConditionForKeyStroke(KeyStroke aKeyStroke); /** Return the action that has been registered with a key stroke **/ public Action getActionForKeyStroke(KeyStroke aKeyStroke); /** Unregister all keyboard actions **/ public void resetKeyboardActions(); .... }
The following examples illustrate two ways in which you might use Swing's
keyboard UI:
The first example shows how arrow keys could be used to move the selection in a list box up and down. In the list box's ComponentUI:
void intallUI(JComponent c) { /** Install sub-components **/ ... /** Install event listeners **/ ... /** Install key bindings scrollDownAction and scrollUpAction are defined actions **/ c.registerKeyboardAction(scrollDownAction, KeyStroke.getKeyStroke(KeyEvent.DOWN, 0), WHEN_FOCUSED); c.registerKeyboardAction(scrollUpAction, KeyStroke.getKeyStroke(KeyEvent.UP,0), WHEN_FOCUSED); } void deinstallUI(JComponent c) { /** Remove sub-components and event listeners **/ ... /** Remove key bindings **/ c.resetKeyboardActions(); }
In this example, a label needs to know when Control + A is pressed so its associated JTextField can get the focus. In The label's ComponentUI:
void intallUI(JComponent c) { /** Install sub-components **/ ... /** Install event listeners **/ ... /** Install key bindings setFocus is a defined action that causes the associated container to be focused **/ c.registerKeyboarAction(setFocus, KeyStroke.getKeyStroke('a',InputEvent.CTRL_MASK), WHEN_IN_FOCUSED_WINDOW); } void deinstallUI(JComponent c) { /** Remove sub-components and event listeners **/ ... /** Remove key bindings **/ c.resetKeyboardActions(); }
Version 0.5. Last modified 10/6/97.
Copyright © 1995-97 Sun
Microsystems, Inc. All Rights Reserved.