Search code examples
javaswingnimbus

How to modify Nimbus L&F to activate the default button when pressing the Enter key?


In the Nimbus L&F when one presses the Enter key, if a button has focus, that button is clicked whether or not another button has been set as default as in:

getRootPane().setDefaultButton(myButton);

Also, using Key Binding does not work:

Action clickDefault = new AbstractAction("clickDefault") {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Got Here");
        getRootPane().getDefaultButton().doClick();
    }
};
InputMap im = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
KeyStroke enter = KeyStroke.getKeyStroke("ENTER");
im.put(enter, "defaultButton");
getRootPane().getActionMap().put("defaultButton", clickDefault);

I never even see the "Got Here" message, though if I bind to another KeyStroke, such as "P" it functions as expected. So it would seem that the Enter key is being captured before it gets to this event handler.

I also tried modifying the UIDefaults:

    im = (InputMap) UIManager.getDefaults().get("Button.focusInputMap");
    im.put(enter, null);
    im.put(enterRelease, null);

That failed as well. Anyone have any ideas how to accomplish this?

--- Update ---

Further investigation revealed that the InputMap for JButton contains {"pressed Enter": "pressed", "released ENTER": "released"} (as well as bindings for SPACE). The Key Binding of the Component in question has higher precedence than the RootPane's. See my answer below for code that resolves the problem.


Solution

  • Ok, finally figured out how to make this work as I wanted. Here's the code I'm using:

    public class Main {
        public static void main(String[] args) {
    
            EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
    
                // Use Nimbus if it's available and we're not on Mac OSX
                if (!System.getProperty("os.name").equals("Mac OS X")) {
                    try {
                        for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                            if ("Nimbus".equals(info.getName())) {
                                UIManager.setLookAndFeel(info.getClassName());
    
                                ((InputMap) UIManager.get("Button.focusInputMap"))
                                        .put(KeyStroke.getKeyStroke("pressed ENTER"), null);
                                ((InputMap) UIManager.get("Button.focusInputMap"))
                                        .put(KeyStroke.getKeyStroke("released ENTER"), null);
    
                                break;
                            }
                        }
                    } catch (Exception e) {
                        // Default Look and Feel will be used
                    }
                }
    
                MainWindow mainWindow = new MainWindow();
                mainWindow.setVisible(true);
            }
        });
    }
    

    } // end class Main

    One important thing I figured out was that the updates to the InputMap(s) must be made AFTER setting the Look and Feel. I wasn't aware that was required when messing with these things, but then again, I'm new at this whole Look and Feel business.