key-bindings and holding down keys
Asked Answered
I

3

3

I have created a key binding for a JTextArea Component. When invoked, it creates a new instance of itself and sets focus to it.

If you hold down the enter (which invokes key binding) my program will start spitting out bunch of JTextArea instances.

Is there a way to force the user to press enter againg to create a new instance?

Do I have I switch to KeyListeners or is there a way with key bindings?

Immoderate answered 31/8, 2012 at 22:18 Comment(0)
K
2

the way to do it with keybindings is to have two actions:

  • an action creating the component is bound to the pressed enter, it disables itself when inserting the component
  • an action enabling the action again is bound to the released enter

Some code:

// the action to create the component
public static class CreateAction extends AbstractAction {

    private Container parent;
    private Action enableAction;

    public CreateAction(Container parent) {
        this.parent = parent;
        enableAction = new EnableAction(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        setEnabled(false);
        Component field = createTextField();
        parent.add(field);
        parent.revalidate();
        field.requestFocus();
    }

    int count;
    private Component createTextField() {
        // just for fun counting the fields we create
        JTextField field = new JTextField("field: " + count++, 20);
        field.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), 
                "createComponent");
        field.getActionMap().put("createComponent", this);
        field.getInputMap().put(KeyStroke.getKeyStroke("released ENTER"), 
                "enableCreation");
        field.getActionMap().put("enableCreation", enableAction);
        return field;
    }

}

// the action that enables another
public static class EnableAction extends AbstractAction {

    Action toEnable;

    public EnableAction(Action toEnable) {
        this.toEnable = toEnable;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        toEnable.setEnabled(true);
    }

}

// usage
final JComponent parent = new JPanel(new MigLayout("wrap"));
// here I'm lazy and let the action create the very first component as well
add.actionPerformed(null);
add.setEnabled(true);

Note that the same instances of the actions are registered to all components, so it doesn't matter which has the focus (and ultimately enables the creation again)

Kenyatta answered 1/9, 2012 at 8:0 Comment(0)
D
4

You specify that a KeyStroke only fire on key release when you're setting up the input map

See KeyStroke getKeyStroke(int keyCode, int modifiers, boolean onKeyRelease)

Deangelis answered 31/8, 2012 at 22:29 Comment(4)
@trashgod opps, was meant to be the Java 7 API, will update, thanksDeangelis
@trashgod ps - I didn't know about it till I read it here on another post ;)Deangelis
That's no good. I need it to fire on key press. However, once it fires, I need it to be disabled until key release. I used keylistener. I created a boolean keypressed, and function keyPressed() sets it to true, and keyReleased() sets it to false. If it's true, code inside keyPressed cannot get executed, so it works really well...Immoderate
@Immoderate The same concept can be applied to Key Bindings and they don't suffer from the same focus issues as KeyListenerDeangelis
K
2

the way to do it with keybindings is to have two actions:

  • an action creating the component is bound to the pressed enter, it disables itself when inserting the component
  • an action enabling the action again is bound to the released enter

Some code:

// the action to create the component
public static class CreateAction extends AbstractAction {

    private Container parent;
    private Action enableAction;

    public CreateAction(Container parent) {
        this.parent = parent;
        enableAction = new EnableAction(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        setEnabled(false);
        Component field = createTextField();
        parent.add(field);
        parent.revalidate();
        field.requestFocus();
    }

    int count;
    private Component createTextField() {
        // just for fun counting the fields we create
        JTextField field = new JTextField("field: " + count++, 20);
        field.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), 
                "createComponent");
        field.getActionMap().put("createComponent", this);
        field.getInputMap().put(KeyStroke.getKeyStroke("released ENTER"), 
                "enableCreation");
        field.getActionMap().put("enableCreation", enableAction);
        return field;
    }

}

// the action that enables another
public static class EnableAction extends AbstractAction {

    Action toEnable;

    public EnableAction(Action toEnable) {
        this.toEnable = toEnable;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        toEnable.setEnabled(true);
    }

}

// usage
final JComponent parent = new JPanel(new MigLayout("wrap"));
// here I'm lazy and let the action create the very first component as well
add.actionPerformed(null);
add.setEnabled(true);

Note that the same instances of the actions are registered to all components, so it doesn't matter which has the focus (and ultimately enables the creation again)

Kenyatta answered 1/9, 2012 at 8:0 Comment(0)
S
0

Here is the code I use, to have an action only run when a key is first pressed down:

private void registerKeyBindings(final JFrame frame) {
    var inputMap = frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
    inputMap.put(KeyStroke.getKeyStroke(KeyCode.G.getInputEventCode(), 0, false), "g_down");
    inputMap.put(KeyStroke.getKeyStroke(KeyCode.G.getInputEventCode(), 0, true), "g_up");
    
    frame.getRootPane().getActionMap().put("g_down", new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        if (gDown) return;
        gDown = true;

        // put your custom key-down-action code here
      }
    });
    frame.getRootPane().getActionMap().put("g_up", new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        gDown = false;
      }
    });
}
Boolean gDown = false;
Splanchnology answered 22/5, 2021 at 14:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.