5

Is there a way to use Nimbus LAF (Look And Feel) on OS X while still being able to use the Meta key for cut/copy/paste and select-all operations?

I currently have the following code in my Swing app's main method, which changes up the LAF based on the operating system (default for OS X, Nimbus for all others):

if (!System.getProperty("os.name", "").startsWith("Mac OS X")) {
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(LicenseInspectorUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(LicenseInspectorUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(LicenseInspectorUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(LicenseInspectorUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
}

I do this as a workaround because Nimbus overrides the keyboard shortcuts for cut/copy/paste and select-all on OS X (Meta key versus Ctrl key). I would prefer to use Nimbus all the time, if only the keyboard shortcuts weren't overridden.

xav
  • 5,452
  • 7
  • 48
  • 57
sworisbreathing
  • 670
  • 4
  • 16

2 Answers2

3

Using the getMenuShortcutKeyMask() method works with NimbusLookAndFeel to enable the key, as shown in this example. See also this related answer.

In the particular case of a JTextField, you can use the mask in a key binding that evokes the original action.

int MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
JTextField jtf = new JTextField("Test");
jtf.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_A, MASK), "select-all");
jtf.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, MASK), "copy");
jtf.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_X, MASK), "cut");
jtf.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_V, MASK), "paste");
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • I don’t see how this answers the question. Even if you call getMenuShortcutKeyMask() when registering custom keyboard shortcuts, this does not fix the incorrect Ctrl-C, Ctrl-V, etc. in a default Swing TextField when using NimbusLookAndFeel on OSX. – yonran May 12 '15 at 00:53
  • @yonran: I've elaborated above. – trashgod May 13 '15 at 01:15
  • Seems that the action names differ per component. So to map the "copy" action, you will also need to map DefaultEditorKit.copyAction. – Jouni Aro Aug 22 '17 at 08:00
  • @JouniAro: Not necessarily; the actions are already in the text field's `ActionMap`, placed there (by name) during the UI delegate's initialization; see this [article](https://tips4java.wordpress.com/2008/10/10/key-bindings/). – trashgod Aug 22 '17 at 09:21
  • 1
    Sorry, I mean per component type. So for every Table, the ActionMap contains "copy", whereas for every TextField it contains "copy-to-clipboard" (=DefaultEditorKit.copyAction) – Jouni Aro Aug 22 '17 at 10:22
1

Different components use different keys, so to map all of them, you will have to define different keys. For example (base found from here):

private void addOSXKeyStrokes(InputMap inputMap) {
  inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.META_DOWN_MASK), DefaultEditorKit.copyAction);
  inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, KeyEvent.META_DOWN_MASK), DefaultEditorKit.cutAction);
  inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, KeyEvent.META_DOWN_MASK), DefaultEditorKit.pasteAction);
  inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.META_DOWN_MASK), DefaultEditorKit.selectAllAction);
  inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.META_DOWN_MASK), "copy");
  inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.META_DOWN_MASK), "selectAll");
}

This can then be mapped to different components as follows:

// This must be performed immediately after the LaF has been set
if (System.getProperty("os.name", "").startsWith("Mac OS X")) {
  // Ensure OSX key bindings are used for copy, paste etc
  // Use the Nimbus keys and ensure this occurs before any component creation
  addOSXKeyStrokes((InputMap) UIManager.get("EditorPane.focusInputMap"));
  addOSXKeyStrokes((InputMap) UIManager.get("FormattedTextField.focusInputMap"));
  addOSXKeyStrokes((InputMap) UIManager.get("PasswordField.focusInputMap"));
  addOSXKeyStrokes((InputMap) UIManager.get("TextField.focusInputMap"));
  addOSXKeyStrokes((InputMap) UIManager.get("TextPane.focusInputMap"));
  addOSXKeyStrokes((InputMap) UIManager.get("TextArea.focusInputMap"));
  addOSXKeyStrokes((InputMap) UIManager.get("Table.ancestorInputMap"));
  addOSXKeyStrokes((InputMap) UIManager.get("Tree.focusInputMap"));
}

A full list of Aqua (OS X Look and Feel) action names is here

Jouni Aro
  • 2,099
  • 14
  • 30