Search code examples
javaswingbuttonactionlistenerkey-bindings

Java Swing: Show pressed button when using corresponding keyboard button


I'm making a program in Java, using Swing, with a GUI that contains arrow keys. The arrow keys correspond to the arrow keys on the keyboard.

When I press the up arrow key on the keyboard, I'd like the up arrow key on the GUI to show up as being pressed. Until I release the arrow key, it should show it is still being pressed, and when released it should also release.

A snippet of my code so far (only for the Up button), which I think is totally wrong in the show being pressed category:

...
if (e.getKeyCode() == KeyEvent.VK_UP) {
                    actionArrowUp();
                    JButton buttonUp = (JButton) mainTab.getComponent(4);
                    buttonUp.setSelected(true);
                }
...
@Override
public void keyReleased(KeyEvent e) { 
            if (e.getKeyCode() == KeyEvent.VK_UP)
                actionArrowUpRelease();
                buttonUp.setSelected(true);

Solution

  • Using keyBindings (as @trashgod already mentioned) is the way to go. To get the exact same visual behaviour as if activating the button by space/enter (when it were focused)

    • implement actions that delegate to the button's default actions registered for pressed/released
    • needs binding to both pressed and released of the key to simulate
    • install the binding to the buttons's parent in its inputMap of type WHEN_ANCESTOR

    In code:

    // the delegating  action
    public static class SimulateButtonAction extends AbstractAction {
    
        AbstractButton button;
    
        public SimulateButtonAction(AbstractButton model, String fire) {
            super(fire);
            this.button = model;
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
            Action delegate = button.getActionMap().get(getName());
            delegate.actionPerformed(new ActionEvent(button, 
                    ActionEvent.ACTION_PERFORMED, getName()));
        }
    
        public String getName() {
            return (String) getValue(Action.NAME);
        }
    
    }
    
    // example usage
    JComponent content = new JPanel(new GridLayout(0, 5));
    Action log = new AbstractAction() {
    
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("triggered: " + ((AbstractButton) e.getSource()).getText());
        }
    
    };
    String pressed = "pressed";
    String released = "released";
    ActionMap actionMap = content.getActionMap();
    InputMap inputMap = content.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    String[] arrows = {"UP", "DOWN", "LEFT", "RIGHT"};
    for (int i = 0; i < arrows.length; i++) {
        JButton button = new JButton(log);
        button.setAction(log);
        button.setText(arrows[i]);
        content.add(button);
        // simulate pressed
        String pressedKey = pressed + arrows[i];
        inputMap.put(KeyStroke.getKeyStroke(arrows[i]), pressedKey);
        actionMap.put(pressedKey, new SimulateButtonAction(button, pressed));
        String releasedKey = released + arrows[i];
        inputMap.put(KeyStroke.getKeyStroke(released + " " +arrows[i]), releasedKey);
        actionMap.put(releasedKey, new SimulateButtonAction(button, released));
    }