Question

In my application I use a Default button. I want it to react when ENTER Key is released. Not when ENTER Key is pressed.

I removed the KeyStroke from InputMap of the button. But it didn't work for me. How should i do that?


import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class ButtonTest {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                buildFrame();
            }
        });
    }

    private static void buildFrame() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JButton button = new JButton(new AbstractAction("Button") {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("ButtonTest::actionPerformed: CALLED");
            }
        });

        JButton button2 = new JButton("Button 2");
        InputMap im = button.getInputMap();
        im.put(KeyStroke.getKeyStroke("ENTER"), "none");
        im.put(KeyStroke.getKeyStroke("released ENTER"), "released");

        f.setLayout(new GridBagLayout());
        f.add(button);
        f.add(button2);
        f.getRootPane().setDefaultButton(button);

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

This is the sample code.

Was it helpful?

Solution

JRootPane rootPane = SwingUtilities.getRootPane(/* Your JButton  */); 
rootPane.setDefaultButton(/* Your JButton  */);

OTHER TIPS

the keys for the default button (that is, the button that is triggered on enter, no matter which component is focusOwner) are bound in the rootPane's componentInputMap, that is its inputMap of type WHEN_IN_FOCUSED_WINDOW. So technically, that's the place to tweak:

JComponent content = new JPanel();
content.add(new JTextField("some focusable"));
content.add(new JTextField("something else"));

JXFrame frame = wrapInFrame(content, "default button on released");
Action action = new AbstractAction("do something") {

    @Override
    public void actionPerformed(ActionEvent e) {
        LOG.info("clicked");
    }
};
JButton button = new JButton(action);
content.add(button);
frame.getRootPane().setDefaultButton(button);
// remove the binding for pressed
frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
    .put(KeyStroke.getKeyStroke("ENTER"), "none");
// retarget the binding for released
frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
    .put(KeyStroke.getKeyStroke("released ENTER"), "press");

Beware: this is still different from the pressed/released behaviour of a non-default button because the rootPane action simply call's button.doClick without going through the moves of arming/pressing the buttonModel to indirectly trigger the action.

    InputMap im = button.getInputMap();
    im.put(KeyStroke.getKeyStroke("ENTER"), "pressed");
    im.put(KeyStroke.getKeyStroke("released ENTER"), "released");

The effect of this is that the button executes its actionPerformed only once, when the ENTER is released (if I understand correctly, this is what you want).

EDIT: the running following code demonstrates that the actionPerformed is executed only at ENTER release (although visually the button appears pressed already on ENTER press)

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class ButtonTest {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                buildFrame();
            }
        });
    }

    private static void buildFrame() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JButton button = new JButton(new AbstractAction("Button") {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("ButtonTest::actionPerformed: CALLED");
            }
        });

        InputMap im = button.getInputMap();
        im.put(KeyStroke.getKeyStroke("ENTER"), "pressed");
        im.put(KeyStroke.getKeyStroke("released ENTER"), "released");

        f.add(button);
        f.getRootPane().setDefaultButton(button);

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }
}

You can use the version of getKeyStroke() that lets you set onKeyRelease to true, like in this answer

Edit: You can stop repeats, like in this answer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top