Lucas
Lucas

Reputation: 33

JavaFX How can I distinguish between a keyPressed X and a combination of Keys X + Y

I want my application (JavaFx 14) to so something when the key "a" is pressed and I also want the application to do something when a key combination with the same key "a" and "b" is pressed. The goal is to only call one method. the method for pressing a or the method for pressing a +b. I tried doing it with a observable List with keycodes but all methods are called.

    private ObservableList<KeyCode> keys = FXCollections.observableArrayList();

private void keyboardShortcuts(Button[] buttons){

    // collect pressed key
    scene.addEventFilter(KeyEvent.KEY_PRESSED, (e) -> {
        if (!keys.contains(e.getCode())) {
            keys.add(e.getCode());
        }

        e.consume();
    });

    // remove pressed key when it is released   
    scene.setOnKeyReleased((e) -> {
        keys.remove(e.getCode());

        e.consume();
    });

    keys.addListener(new InvalidationListener() {
        @Override
        public void invalidated(Observable observable) {


            if(keys.size() == 1) {

                if(keys.contains(KeyCode.A)) {
                    foo();
                }
                if(keys.contains(KeyCode.B)) {
                    foo2();
                }                  

            }  
            if(keys.size() == 2) {

                if(keys.contains(KeyCode.A) && keys.contains(KeyCode.B)) {
                    foo3();
                }           

            } 
        }
    });
}

Upvotes: 2

Views: 251

Answers (1)

James_D
James_D

Reputation: 209553

When the user "presses two keys", it's impossible for both key presses to happen simultaneously, so one of the key presses will necessarily be detected before the other. Probably the best approach here is to ensure one of the keys (say B) doesn't have it's own functionality. That way the user will learn to always press B first, then A while B is held down (this is the way we type, using modifier keys such as SHIFT and CTRL).

If you really want both keys to have their own functionality, but different functionality when both are pressed "at the same time", then your only option is to implement a delay in handling the press of a single key, waiting to see if the other key is pressed before that delay expires. This, of course, comes at the expense of responsiveness.

Here's an implementation of that, using a short (25ms) PauseTransition for the delay. (I also changed to use an ObservableSet, which cleans up some of the logic.)

private ObservableSet<KeyCode> keys = FXCollections.observableSet();
private PauseTransition keyDelay = new PauseTransition(Duration.millis(25));

private void keyboardShortcuts(Scene scene){

    // collect pressed key
    scene.addEventFilter(KeyEvent.KEY_PRESSED, (e) -> {
        keys.add(e.getCode());
        e.consume();
    });

    // remove pressed key when it is released   
    scene.setOnKeyReleased((e) -> {
        keys.remove(e.getCode());
        e.consume();
    });

    keys.addListener((Change<? extends KeyCode> c) -> {
        if (keys.size() <= 1) {
            keyDelay.playFromStart();
        } else {
            keyDelay.stop();
            checkKeys();
        }
    });
    keyDelay.setOnFinished(e -> checkKeys());

}

private void checkKeys() {

    if(keys.contains(KeyCode.A) && keys.contains(KeyCode.B)) {
        System.out.println("A and B");
    } else if(keys.contains(KeyCode.A)) {
        System.out.println("A only");
    } else if(keys.contains(KeyCode.B)) {
        System.out.println("B only");
    }
}

Upvotes: 2

Related Questions