How to fix accelerators (keyboard shortcuts) not working with Swing actions

2014-06-15

Let’s start with a piece of code that seems to be correct at the first glance…

JMenuItem mntmTest = new JMenuItem("Test");
mntmTest.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_MASK));
mntmTest.setAction(action);	

This snippet was generated by Eclipse WindowBuilder.

Looks good to you? Well… It’s not. The menu item will not have an accelerator key combination.

Always check autogenerated code if you can.

The problem with this code is that Swing actions have their own accelerator key property and when you set the action for a menu item, that menu’s accelerator gets replaced with the action’s accelerator. In other words, the snippet above is equivalent to this:

JMenuItem mntmTest = new JMenuItem("Test");
mntmTest.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_MASK));
mntmTest.setAction(action);
mntmTest.setAccelerator(null);

The obvious solution would be to put setAccelerator after setAction, like this:

JMenuItem mntmTest = new JMenuItem("Test");
mntmTest.setAction(action);
mntmTest.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_MASK));

But that is by far not the best way to do this! A better way to do this would be to set the action’s accelerator key stroke.

Your action’s constructor might look like this:

public SwingAction() {
	this.putValue(NAME, "SwingAction");
	this.putValue(SHORT_DESCRIPTION, "Some short description");
}

Let’s add the accelerator key:

public SwingAction() {
	this.putValue(NAME, "SwingAction");
	this.putValue(SHORT_DESCRIPTION, "Some short description");
	this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_MASK));	
}

Now, we don’t need to call setAccelerator on mntmTest because it will be called automatically in setAction.

Final code

public SwingAction() {
	this.putValue(NAME, "SwingAction");
	this.putValue(SHORT_DESCRIPTION, "Some short description");
	this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_MASK));	
}
JMenuItem mntmTest = new JMenuItem("Test");
mntmTest.setAction(action);