20

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.

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
Hirantha
  • 337
  • 1
  • 4
  • 14
  • Post some part of code ... I makes easy to get your solution... – Vinesh Sep 04 '12 at 10:01
  • what difference is between KeyReleased and KeyPressed, because this JButton must received once time ENTER KeyEvent from keyboard, do you meaning repeatly firing the same action until ENTER ... ???, – mKorbel Sep 04 '12 at 10:12
  • in My Application Dialog will be displayed after I pressed the Enter on Textfiled which triggers a "Search" button. If I hold the ENTER key for longer time shown dialog is automatically gets closed. Since it has default button. – Hirantha Sep 04 '12 at 10:45
  • do you want to protect against multiplay event came from `ENTER` (in your case) ??? action from `ENTER` key should be fired only once time ???, and new action should be fired only on next keyPressed ??? – mKorbel Sep 04 '12 at 11:13
  • Check this link http://stackoverflow.com/questions/13731710/allowing-the-enter-key-to-press-the-submit-button-as-opposed-to-only-using-mo – Akash Namdev Apr 19 '16 at 09:58

4 Answers4

34
JRootPane rootPane = SwingUtilities.getRootPane(/* Your JButton  */); 
rootPane.setDefaultButton(/* Your JButton  */);
DNP
  • 501
  • 4
  • 9
  • 2
    Your code gives me NPE while this works- `JRootPane rootPane = SwingUtilities.getRootPane(/* Your JFrame */); ` – raghavsood33 Feb 26 '17 at 15:10
  • 2
    @raghavsood33 `SwingUtilities.getRootPane(yourJButton);` will work without NPE only after calling something to the effect of `yourJFrame.add(yourJButton)`. Prior to adding the button to the frame (or a container which has been added to the frame), the button clearly won't have a root pane. – AJNeufeld Nov 28 '17 at 16:37
  • this doesn't work for me. I got away with the NPE (NullPointerException), but the button doesn't focus or anything. – ArchLinuxTux May 03 '18 at 13:35
  • 1
    _UPDATE:_ I had the `rootPane.setDefaultButton(btn)` method called before .revalidate() and .repaint(). This was the problem. It **works** now. – ArchLinuxTux May 03 '18 at 13:43
8

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.

kleopatra
  • 51,061
  • 28
  • 99
  • 211
2
    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);
    }
}
lbalazscs
  • 17,474
  • 7
  • 42
  • 50
  • I want to default button to react only on Enter key release. It should not react to Enter key press. – Hirantha Sep 05 '12 at 03:54
  • What do you mean by "react"? I added a full running example where you can see that the actionPerformed is executed only at ENTER release (although visually the button appears pressed already on ENTER press) – lbalazscs Sep 05 '12 at 08:38
  • "React" means trigger the button. Default button should only trigger on Enter release. Nothing should be happened on Enter Pressed. – Hirantha Sep 06 '12 at 02:50
  • In you example default button triggers to the Enter Key Pressed.... I checked it by holding the Enter key for some time, then it continuously print the Sys out. – Hirantha Sep 06 '12 at 03:07
  • Holding the Enter prints nothing to the system out for me, only when I release... I have unfortunately no idea why it does not work for you. – lbalazscs Sep 06 '12 at 08:48
  • Try this by adding another button(non default) to this JFrame. This works fine when there is only one component. – Hirantha Sep 06 '12 at 09:59
  • So you didn't check with *my* running example, but you put it somehow in your program... My example runs fine even with two buttons... – lbalazscs Sep 06 '12 at 10:25
  • I tried ur program by adding two buttons, it didnt work for me. Still, default button gets triggered to Enter Key Pressed. I want it to fire on Enter Key Released only. – Hirantha Sep 06 '12 at 11:23
  • as I commented on your answer, my line was im.put(KeyStroke.getKeyStroke("ENTER"), "pressed"); – lbalazscs Sep 06 '12 at 12:51
1

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.

Community
  • 1
  • 1
Catalina Island
  • 7,027
  • 2
  • 23
  • 42