Rolf
Rolf

Reputation: 662

Java Swing: consuming key events

I have a JFrame (containing various text fields and tables etc.) and want to install a hot key function that applies whenever the frame is open (a bit like a menu accelerator shortcut). The following mostly works, and my action is invoked regardless of which field or control has focus:

String MY_GLOBAL_ACTION_TRIGGER = "hotKey";
InputMap im = getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
KeyStroke ks = KeyStroke.getKeyStroke('`');
im.put(ks, MY_GLOBAL_ACTION_TRIGGER);
ActionMap am = getRootPane().getActionMap();
am.put(MY_ACTION_TRIGGER, new AbstractAction() { public void actionPerformed() ... });

However, the key press isn't consumed and I still get a back quote inserted into the text field. How can I prevent the key press being propagated to text fields once my action has been invoked?

Upvotes: 4

Views: 2017

Answers (3)

Elist
Elist

Reputation: 5533

There are two issues here:

  • The KeyStroke constructed with char argument doesn't seem to actually catch the stroke. Try using KeyStroke(KeyEvent key, int modifiers).
  • The textfields should filter the selected stroke, or rather a listener should consume them.

    Try something like:

    public class KeyStrokeFrame extends JFrame {
        public static void main(String[] args) {
            new KeyStrokeFrame().setVisible(true);
        }
    
        public KeyStrokeFrame() {
            setSize(200, 200);
            JTextField jtf = new JTextField();
            getContentPane().add(jtf);
            String MY_GLOBAL_ACTION_TRIGGER = "hotKey";
            InputMap im = getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
            KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_1, 0);
            ((AbstractDocument)jtf.getDocument()).setDocumentFilter(new DocumentFilter() {
                @Override
                public void insertString(FilterBypass fb, int offset,
                        String string, AttributeSet attr)
                        throws BadLocationException {
                    if (string.equals("1")) return;
                    super.insertString(fb, offset, string, attr);
                }
    
                @Override
                public void replace(FilterBypass fb, int offset, int length,
                        String text, AttributeSet attrs)
                        throws BadLocationException {
                    if (text.equals("1")) return;
                    super.replace(fb, offset, length, text, attrs);
                }
            });
            im.put(ks, MY_GLOBAL_ACTION_TRIGGER);
            ActionMap am = getRootPane().getActionMap();
            am.put(MY_GLOBAL_ACTION_TRIGGER, new AbstractAction() { 
                public void actionPerformed(ActionEvent e) {
                    System.out.println("pressed");} 
                }); 
            }
    }
    

    Upvotes: 1

  • MadProgrammer
    MadProgrammer

    Reputation: 347244

    It is likely the text fields are getting precedence on the key event notification, meaning your key binding isn't getting notified until after the text field has been updated

    Generally speaking, you really don't want to monitor key strokes/events on text components, as it does not take into consideration the use case where the user pastes text into field

    If you want to filter content going into textField you should use a DocumentFilter

    See Implementing a DocumentFilter

    Upvotes: 2

    ControlAltDel
    ControlAltDel

    Reputation: 35041

    Use the KeyboardFocusManager and a KeyEventDispatcher

    private void myListener implements KeyEventDispatcher {
      public boolean dispatchKeyEvent (KeyEvent ke) {
        if (ke.getKeyChar() == '`') {
          MY_GLOBAL_ACTION.actionPerformed(null);
          return true;
        }
        return false;
      }
    }
    

    Upvotes: 2

    Related Questions