How to set the Java default button to react on ENTER key _released_?
Asked Answered
U

4

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.

Unmask answered 4/9, 2012 at 9:55 Comment(5)
Post some part of code ... I makes easy to get your solution...Mailemailed
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 ... ???,Sabah
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.Unmask
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 ???Sabah
Check this link #13732210Caritta
B
34
JRootPane rootPane = SwingUtilities.getRootPane(/* Your JButton  */); 
rootPane.setDefaultButton(/* Your JButton  */);
Brandt answered 10/12, 2013 at 12:38 Comment(4)
Your code gives me NPE while this works- JRootPane rootPane = SwingUtilities.getRootPane(/* Your JFrame */); Photovoltaic
@Photovoltaic 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.Superannuate
this doesn't work for me. I got away with the NPE (NullPointerException), but the button doesn't focus or anything.Saxena
UPDATE: I had the rootPane.setDefaultButton(btn) method called before .revalidate() and .repaint(). This was the problem. It works now.Saxena
M
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.

Mayhew answered 25/9, 2012 at 15:48 Comment(0)
T
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);
    }
}
Teucer answered 4/9, 2012 at 12:33 Comment(9)
I want to default button to react only on Enter key release. It should not react to Enter key press.Unmask
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)Teucer
"React" means trigger the button. Default button should only trigger on Enter release. Nothing should be happened on Enter Pressed.Unmask
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.Unmask
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.Teucer
Try this by adding another button(non default) to this JFrame. This works fine when there is only one component.Unmask
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...Teucer
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.Unmask
as I commented on your answer, my line was im.put(KeyStroke.getKeyStroke("ENTER"), "pressed");Teucer
S
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.

Submultiple answered 4/9, 2012 at 13:15 Comment(2)
by using built_in Boolean in Swing Action as shown code by @MayhewSabah
@Sabah Thanks! I added a link to that answer.Submultiple

© 2022 - 2024 — McMap. All rights reserved.