Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Listening to key events for a component hierarchy

I have a Swing app that needs to display different sets of controls based on whether the control or alt keys are pressed. I added a KeyListener to the main component, but it is notified only if that component is selected, not if a subcomponent is selected. Is there a way to listen to events for a component and all descendents?

Edit:

I tried using the main component's InputMap, but no event is fired when pressing a modifier key. Specifically, I have the following code:

InputMap inputMap = panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
inputMap.put(KeyStroke.getKeyStroke("pressed CONTROL"), "test1");
inputMap.put(KeyStroke.getKeyStroke("released CONTROL"), "test2");
ActionMap actionMap = panel.getActionMap();
actionMap.put("test1", new AbstractAction() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("pressed");
    }
});
actionMap.put("test2", new AbstractAction() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("released");
    }
});

When pressing and releasing the control key, this will print "released" but not "pressed". Nothing else is registering anything in any InputMap, so it's not as though something else is registered for the same key stroke.

like image 425
Adam Crume Avatar asked Feb 02 '11 22:02

Adam Crume


People also ask

What is a key event?

An event which indicates that a keystroke occurred in a component. This low-level event is generated by a component object (such as a text field) when a key is pressed, released, or typed.

What is a keyboard listener?

Interface KeyListenerA keyboard event is generated when a key is pressed, released, or typed. The relevant method in the listener object is then invoked, and the KeyEvent is passed to it.


3 Answers

Just change your getKeyStroke(...) invocation like so:

InputMap inputMap = panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, KeyEvent.CTRL_DOWN_MASK, false), "test1");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, 0, true), "test2");
ActionMap actionMap = panel.getActionMap();
actionMap.put("test1", new AbstractAction() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("pressed");
    }
});
actionMap.put("test2", new AbstractAction() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("released");
    }
});

I took me a lot of trial and error to come up with the correct incantation.

It is important to use getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT), to be able to listen to child and deeper widgets.

It is especially important to specify KeyEvent.CTRL_DOWN_MASK, in addition to KeyEvent.VK_CONTROL when looking for control key PRESS events. This particular detail was missing from your modified code example.

like image 111
Christopher Bruns Avatar answered Nov 01 '22 15:11

Christopher Bruns


You may want to try to use key binding rather than KeyListeners. Key binding is a higher level construct and can listen for key presses even if the component that "listens" doesn't have the focus -- as opposed to KeyListeners. You can find more on these at the Swing tutorials: How to use key bindings

like image 34
Hovercraft Full Of Eels Avatar answered Nov 01 '22 16:11

Hovercraft Full Of Eels


You may consider using the InputMap and ActionMap of some swing components. You can specify with JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT to indicate you want to respond to the given keystroke when either the component or its subcomponents are in focus.

If you don't want to use the InputMap or ActionMap, you could just add the KeyListener to all the child components:

for (Component child : parent.getComponents())
{
 child.addKeyListener(keyListener);
}
like image 2
Zach L Avatar answered Nov 01 '22 14:11

Zach L