66

GUI development with Swing.

I have a custom dialog for choosing a file to be opened in my application; its class extends javax.swing.JDialog and contains, among other components, a JFileChooser, which can be toggled to be shown or hidden.

The JFileChooser component already handles the ESC key by itself: when the file chooser is shown (embedded in my dialog) and I press ESC, the file chooser hides itself.

Now I would like my dialog to do the same: when I press ESC, I want the dialog to close. Mind you, when the embedded file chooser is shown, the ESC key should only hide it.

Any ideas ?

Leonel
  • 28,541
  • 26
  • 76
  • 103

5 Answers5

74

You can use the following snippet. This is better because the rootPane will get events from any component in the dialog. You can replace setVisible(false) with dispose() if you want.

public static void addEscapeListener(final JDialog dialog) {
    ActionListener escListener = new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            dialog.setVisible(false);
        }
    };

    dialog.getRootPane().registerKeyboardAction(escListener,
            KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
            JComponent.WHEN_IN_FOCUSED_WINDOW);

}
Ayman
  • 11,265
  • 16
  • 66
  • 92
  • Sounds nice. I'd just replace dialog.setVisible(false) with some code to actually cancel the dialog. I'll give it a try. – Leonel Mar 19 '09 at 13:29
66

Use InputMap and ActionMap for dealing with key actions in Swing. To close the dialog cleanly, send a window closing event to it.

From my now defunct weblog:

private static final KeyStroke escapeStroke = 
    KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); 
public static final String dispatchWindowClosingActionMapKey = 
    "com.spodding.tackline.dispatch:WINDOW_CLOSING"; 
public static void installEscapeCloseOperation(final JDialog dialog) { 
    Action dispatchClosing = new AbstractAction() { 
        public void actionPerformed(ActionEvent event) { 
            dialog.dispatchEvent(new WindowEvent( 
                dialog, WindowEvent.WINDOW_CLOSING 
            )); 
        } 
    }; 
    JRootPane root = dialog.getRootPane(); 
    root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( 
        escapeStroke, dispatchWindowClosingActionMapKey 
    ); 
    root.getActionMap().put( dispatchWindowClosingActionMapKey, dispatchClosing 
    ); 
}
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • 3
    @Tom hello, thanks for the great post, can you also tell me the best practice in case I've any text base component in the dialog. Since it doesn't work if the focus is on them. – pratikabu Dec 11 '13 at 10:04
17

If your looking for a technique using new features of Java 8 , try a lambda expression:

dialog.getRootPane().registerKeyboardAction(e -> {
    window.dispose();
}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);

or

KeyStroke k = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
int w = JComponent.WHEN_IN_FOCUSED_WINDOW;
dialog.getRootPane().registerKeyboardAction(e -> window.dispose(), k, w);
Java42
  • 7,628
  • 1
  • 32
  • 50
4

I had problems implementing both of the top answers. Here's a rather compact version using AbstractAction to auto-implement most of Action's methods, which works within text-based fields (per @pratikabu's request):

final AbstractAction escapeAction = new AbstractAction() {
    private static final long serialVersionUID = 1L;

    @Override
    public void actionPerformed(ActionEvent ae) {
        dispose();
    }
};

getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
        .put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "ESCAPE_KEY");
getRootPane().getActionMap().put("ESCAPE_KEY", escapeAction);

References

nikodaemus
  • 1,918
  • 3
  • 21
  • 32
  • 1
    Small note to others. I found out that if you don't make `escapeAction`, `final`, then you will get an exception. It didn't show up in my IDE that it had to be `final`. Exception showed up during runtime. – kevto Nov 11 '16 at 13:48
2

Here's mine, I add CtrlW as closing shorcut aswell

    Action closeAction = new AbstractAction(){
        public void actionPerformed(ActionEvent e){
            dispose();
        }
    };

    KeyStroke esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0);
    getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(esc, "closex");
    getRootPane().getActionMap().put("closex", closeAction);

    KeyStroke ctrlW = KeyStroke.getKeyStroke("control W");
    getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ctrlW, "close");
    getRootPane().getActionMap().put("close", closeAction); 
Wesos de Queso
  • 1,526
  • 1
  • 21
  • 23