Associating Keyboard Accelerators with Menu Items
A keyboard accelerator is the keyboard equivalent of a menu command, and using it produces the same result as selecting the command from the menu.
A Second Way to Reach the Same Command
Most people who have used a word processor have pressed a key combination to save a file or undo a change without ever opening a menu. That key combination is not a separate feature bolted onto the program. It is a second way to reach a command that already sits in a menu. In this article you will follow a few lines of Java that create key combinations in code, trace what each line produces, and see how those combinations end up attached to menu commands.
A keyboard accelerator is the keyboard equivalent of a menu command. Using it produces the same result as selecting the command from the menu. Many programs give their frequently used commands such an equivalent, so the user can type the key combination and the program carries out exactly the same command. The source gives two familiar cases: the Save command is commonly reached with CONTROL-S, and the Undo command with CONTROL-Z.
An accelerator is a shortcut to an existing command, not a new action. Whether the user presses the key combination or picks the command from the menu, the result is the same. This is why accelerators are usually shown beside their menu entries: they tell the user the same command is available from the keyboard.
Tracing a Few Lines of Java
Before reading any explanation, look at what each line produces. The example below creates a KeyStroke for CONTROL-S, creates a menu item named Save, and attaches the key stroke to that item. The code is written for this article to illustrate the source's description; the source itself describes the same ideas in prose.
KeyStroke saveKey = KeyStroke.getKeyStroke("ctrl S");
JMenu fileMenu = new JMenu("File");
JMenuItem saveItem = new JMenuItem("Save");
saveItem.setAccelerator(saveKey);
fileMenu.add(saveItem);- 1The text ctrl S is handed to
getKeyStroke, which returns a KeyStroke that stands for the combination of Control and S. - 2The File menu is created as an empty menu object; nothing is connected to it yet.
- 3The Save menu item is created with the label Save. At this point it has no keyboard equivalent.
- 4
setAcceleratoris called on the Save item with the KeyStroke. This is the step that ties the key combination to this specific command. - 5The Save item is added to the File menu, so the user can also select it with the mouse or keyboard navigation.
- Line 1 builds a KeyStroke object from the text description ctrl S
- Line 2 creates the File menu
- Line 3 creates the Save menu item
- Line 4 attaches the KeyStroke to the Save menu item as its accelerator
- Line 5 places the Save item inside the File menu
Read the sequence diagram from top to bottom. The KeyStroke exists after the first statement, but it does nothing to the interface yet. The menu item only gains a keyboard equivalent at the third step. If you stopped reading after the first line, you would have a correct KeyStroke that is not yet connected to anything.
What a KeyStroke Records
The class javax.swing.KeyStroke is the Java object used to represent a key combination that a user can type. A KeyStroke can describe three kinds of physical event: pressing a key, releasing a key, or typing a character. Any of these can happen while one or more modifier keys are held down. The modifier keys named in the source are control, shift, alt, and meta.
A KeyStroke is a description, not an action. It records which key combination is meant. It does not check whether the user has pressed anything, and it does not run any command. The object simply holds the answer to the question: which combination is this?
The static method getKeyStroke(String) is the convenient way to build these objects. The source says it makes it easy to create key stroke objects that represent key pressed events. You pass it a text description such as ctrl S or ctrl shift Z, and it returns a KeyStroke that stands for that combination of modifiers and a key.
Reading the Text Description
The string argument follows a simple pattern: modifiers first, then the key. The two source examples both follow it. In ctrl S there is one modifier and one key. In ctrl shift Z there are two modifiers before the key. Because the pattern is consistent, you can read any such description by splitting it into parts and asking which word names a modifier and which names the key itself.
Read the tokens left to right. Every word before the last one is a modifier, and the final word is the key. Reading ctrl shift Z therefore means: Control and Shift are held, and Z is the key. The method then turns that description into one KeyStroke object.
Two Objects with Separate Jobs
A KeyStroke by itself does nothing to the user interface. It has to be connected to a menu item so that the key combination is tied to a specific command. The source states there are at least two ways to make this association, and the first it describes is calling the setAccelerator() method on the menu item object.
The division of labor is the central idea. The KeyStroke records which key combination is meant. The menu item records that this combination is its accelerator. Creating the KeyStroke and attaching it are two separate steps, and an error in one step can look like a failure in the other.
Finding Where the Chain Breaks
Suppose a program is supposed to save when the user presses CONTROL-S, but nothing happens. Rather than guessing, work through the chain in order. The source describes three checks, and the first place where the chain stops matching what you expect is where the problem lies.
- 1Check whether the string describing the combination follows the pattern of modifiers followed by a key.
- 2Check whether the KeyStroke returned by
getKeyStrokewas attached to the correct menu item. - 3Check whether the menu item's command is the Save command.
What do you think happens?
If the setAccelerator call is missing but getKeyStroke("ctrl S") still runs, which check fails first?
Reveal answer
The attachment check, because the KeyStroke was never connected to the Save item
The string can be correct and the KeyStroke can be created, yet the menu item still has no accelerator. The chain stops at the second check, because creating the KeyStroke and attaching it are separate steps.
Mistakes When Attaching Accelerators
Common mistake Assuming that creating a KeyStroke makes the shortcut work
What it looks likeKeyStroke k = KeyStroke.getKeyStroke("ctrl S"); with no further linesWhy it failsA KeyStroke by itself does nothing to the user interface. It only records the combination.
Do this insteadAttach the KeyStroke to the menu item with setAccelerator().
Common mistake Writing the modifiers after the key
What it looks likegetKeyStroke("S ctrl")Why it failsThe source pattern puts modifiers first, then the key, as in ctrl S and ctrl shift Z.
Do this insteadWrite the modifiers first and the key last, such as "ctrl S".
Common mistake Blaming the menu when the string is wrong
Why it failsA wrong text description produces a wrong KeyStroke, which then looks like a broken menu item.
Do this insteadCheck the string against the modifiers-then-key pattern before inspecting the menu item.
Tracing a Shortcut That Should Undo
A developer writes: KeyStroke undoKey = KeyStroke.getKeyStroke("ctrl shift Z"); and then calls setAccelerator(undoKey) on a menu item labeled Undo. Trace the description using the modifiers-then-key pattern. Which words are modifiers, which is the key, and what does the accelerator link the combination to? Then explain what the user should expect when they press that combination, compared with selecting Undo from the menu.
Hints
- Apply the pattern from the ctrl shift Z example: every word before the last one is a modifier.
- Remember that an accelerator is a shortcut to an existing command, not a new action.
- Ask which object records the combination and which object records that it is an accelerator.
Key Takeaways
- A keyboard accelerator is the keyboard equivalent of a menu command, and pressing it produces the same result as selecting that command from the menu.
- The
javax.swing.KeyStrokeclass represents a key press, release, or typed character, possibly with modifier keys such as control, shift, alt, and meta. getKeyStroke(String)builds a KeyStroke from a description with modifiers first and the key last, such as ctrl S or ctrl shift Z.- Creating a KeyStroke and attaching it to a menu item are separate steps;
setAccelerator()on the menu item is the source's described way to associate them. - When an accelerator fails, check the string pattern, then the attachment, then the command, and stop at the first step that does not match what you expected.