Concepts / Associating Keyboard Accelerators with Menu Items

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.

  • Programming

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.

Key point

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.

Code walkthrough
KeyStroke saveKey = KeyStroke.getKeyStroke("ctrl S");
JMenu fileMenu = new JMenu("File");
JMenuItem saveItem = new JMenuItem("Save");
saveItem.setAccelerator(saveKey);
fileMenu.add(saveItem);
  1. 1
    The text ctrl S is handed to getKeyStroke, which returns a KeyStroke that stands for the combination of Control and S.
  2. 2
    The File menu is created as an empty menu object; nothing is connected to it yet.
  3. 3
    The Save menu item is created with the label Save. At this point it has no keyboard equivalent.
  4. 4
    setAccelerator is called on the Save item with the KeyStroke. This is the step that ties the key combination to this specific command.
  5. 5
    The 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
Program codeKeyStrokeCtrl SSave menu itemlabel Save1. getKeyStroke("ctrl S") returns KeyStroke2. create item labeled Save3. setAccelerator(KeyStroke)
In what order do the statements run, and which object receives the KeyStroke at each step?

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

Definition

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.

ctrlmodifiershiftmodifierZthe key
How does getKeyStroke(String) divide the text into modifiers and the key?

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.

ctrl Stext descriptionKeyStrokeCtrl plus SSave menu itemholds the acceleratorSave commandsame result as the menugetKeyStroke(String)setAccelerator()selecting it runs
Which object holds a reference to which, and where does the text description become a KeyStroke?
Key point

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.

  1. 1
    Check whether the string describing the combination follows the pattern of modifiers followed by a key.
  2. 2
    Check whether the KeyStroke returned by getKeyStroke was attached to the correct menu item.
  3. 3
    Check 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?

  • The string pattern check

  • The attachment check, because the KeyStroke was never connected to the Save item

  • The command check, because the Save command has changed

Reveal answer
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 like
    KeyStroke k = KeyStroke.getKeyStroke("ctrl S"); with no further lines
    Why it fails

    A KeyStroke by itself does nothing to the user interface. It only records the combination.

    Do this instead

    Attach the KeyStroke to the menu item with setAccelerator().

  • Common mistake

    Writing the modifiers after the key

    What it looks like
    getKeyStroke("S ctrl")
    Why it fails

    The source pattern puts modifiers first, then the key, as in ctrl S and ctrl shift Z.

    Do this instead

    Write the modifiers first and the key last, such as "ctrl S".

  • Common mistake

    Blaming the menu when the string is wrong

    Why it fails

    A wrong text description produces a wrong KeyStroke, which then looks like a broken menu item.

    Do this instead

    Check the string against the modifiers-then-key pattern before inspecting the menu item.

Tracing a Shortcut That Should Undo

MEDIUM

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.KeyStroke class 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.