Anand Dubey
Anand Dubey

Reputation: 5

Enter Key work as Tab Key for every component except JButton

I am developing a Swing application, in which I want enter key work as tab key for all component of JFrame except JButton components and dialog boxes. For that I have set ENTER and TAB as default focus traversal keys.

KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
KeyStroke tab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
Set<KeyStroke> keys = new HashSet<>();
keys.add(enter);
keys.add(tab);
KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, keys);

This is working well,but I want that ENTER Key work as Action on JButton and Dialog Boxes.

Upvotes: 0

Views: 607

Answers (3)

Rjelinek
Rjelinek

Reputation: 26

The approved answer is actually bit wrong. It add Enter as focus travel key, however it DOES NOT modify the basic button behavior of .doClick(); So this code spams .doClick(); second time. I came across this issue when on button action I was writing data from bunch of text Fields to table, and it added the same record two times. solution for this is pretty simple, on instanceof JButton just make return; repaired code upgraded to java 14 instanceof

private static class AWTListener implements AWTEventListener{

    @Override
    public void eventDispatched(AWTEvent event) {
        if (event instanceof KeyEvent key && key.getKeyCode() == KeyEvent.VK_ENTER  && key.getModifiersEx() == 0 && key.getID() == KeyEvent.KEY_PRESSED) {
            if (key.getComponent() instanceof JButton) {
                return;
            }

            key.getComponent().transferFocus();

        }

    }
}

Upvotes: 0

Antony Ng
Antony Ng

Reputation: 797

I think even the solution using AWTEventListener would work, I would suggest avoid using AWTEventListener if another solution is available. It is because it's so powerful that it intercepts all kinds of events globally before they reach their real targets, so if anything went wrong (such as a NullPointerException) in the middle, the whole application would stop working.

My proposed solution makes use of input map & action map which adds handling of Enter key to any focused component in a particular container.

The advantage:

  • Safer because it affects only components in a container instead of all components.

The disadvantage:

  • The same handling code has to apply to all containers that need this behavior, but this could be easily accomplished by a static utility method.

Here is the sample program:

public MainFrame() {
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setSize(500, 500);
    setLayout(new GridLayout(2, 2));

    addAllComponents();
    addEnterKeyAsFocusTraversal();
}

private void addAllComponents() {
    add(new JTextField());
    add(new JTextField());

    add(new JButton("OK"));
    add(new JButton("Cancel"));
}

private void addEnterKeyAsFocusTraversal() {
    final String ENTER_KEY_ACTION = "EnterKeyAction";

    // Here uses the content pane of type Container so a cast is required, 
    // in other case it could be the root container which may already be an instance of JComponent. 
    JComponent contentPane = (JComponent) getContentPane();

    contentPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ENTER_KEY_ACTION);
    contentPane.getActionMap().put(ENTER_KEY_ACTION, createEnterKeyAction());
}

private AbstractAction createEnterKeyAction() {
    return new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
            Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();

            if (focusOwner != null) {
                if (focusOwner instanceof AbstractButton) {
                    ((AbstractButton) focusOwner).doClick();
                } else {
                    focusOwner.transferFocus();
                }
            }
        }
    };
}

Upvotes: 0

Sergiy Medvynskyy
Sergiy Medvynskyy

Reputation: 11327

It's possible, but using another way: global event listener. To register a global event listener you should use the Toolkit class:

Toolkit.getDefaultToolkit().addAWTEventListener(listener, mask);

Here is an example for your case:

import java.awt.AWTEvent;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.AbstractButton;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

public class FocusTransferTest {

    public static void main(String[] args) {
        JFrame frm = new JFrame("Test focus transfer");
        JPanel panel = new JPanel();
        panel.add(new JTextField(10));
        panel.add(new JTextField(10));
        panel.add(new JTextField(10));
        JButton btn = new JButton("Press me");
        btn.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frm, "It's a message", "Info", 
                        JOptionPane.INFORMATION_MESSAGE);
            }
        });
        panel.add(btn);
        Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEnterKeyListener(), 
                AWTEvent.KEY_EVENT_MASK);
        frm.add(panel);
        frm.pack();
        frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frm.setLocationRelativeTo(null);
        frm.setVisible(true);
    }

    private static class AWTEnterKeyListener implements AWTEventListener {

        @Override
        public void eventDispatched(AWTEvent event) {
            if (event instanceof KeyEvent) {
                KeyEvent key = (KeyEvent) event;
                if (key.getKeyCode() == KeyEvent.VK_ENTER  && key.getModifiersEx() == 0 
                        && key.getID() == KeyEvent.KEY_PRESSED) {
                    if (key.getComponent() instanceof AbstractButton) {
                        ((AbstractButton) key.getComponent()).doClick();
                    } else {
                        key.getComponent().transferFocus();
                    }
                }
            }
        }

    }
}

Upvotes: 1

Related Questions