Search code examples
javaswingjtextareakey-bindingskeystrokes

key-bindings and holding down keys


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?


Solution

  • 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)