Reputation: 662
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
Reputation: 5533
There are two issues here:
KeyStroke
constructed with char
argument doesn't seem to actually catch the stroke. Try using KeyStroke(KeyEvent key, int modifiers)
.
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
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
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