Search code examples
modal-dialogjavafx-8key-events

KeyEvent on Dialog not consumed when closing in JavaFX


I have a requirement to navigate a table when the user hits the ENTER key. For this I have created an event filter similar to:

private EventHandler<KeyEvent> keyReleasedFilter = event -> {
    if ((event.getCode() == KeyCode.ENTER || event.getCode() == KeyCode.TAB)) {
        previousPosition = table.getFocusModel().getFocusedCell();
        //do my navigation
    }
}

I have run into an issue where JavaFX Modal Dialogs used during navigation of the table to indicate an error, cause issues with this filter. If the user closed the dialog with the ENTER key, that event is trapped by my event filter on the parent stage. I am not sure how to prevent that. It is causing inconsistent navigation.

Here is a simple application that demonstrates the behavior:

public void start(Stage primaryStage) {
    final Alert d = new Alert(Alert.AlertType.ERROR);
    d.initModality(Modality.WINDOW_MODAL);

    Button btn = new Button();
    btn.setText("Say 'Hello World'");
    btn.addEventFilter(KeyEvent.KEY_RELEASED, event -> {
        if ((event.getCode() == KeyCode.ENTER || event.getCode() == KeyCode.TAB)) {
            d.showAndWait();
        }
    });

    Scene scene = new Scene(new StackPane(btn), 300, 250);

    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(scene);
    primaryStage.show();
}

I have noticed that the dialog is closed with the KEY_PRESSED event, and will not capture the KEY_RELEASED event.

I have tried adding an EVENT FILTER to the dialog (Button/DialogPane/Scene and even Stage) - none intercept the KEY_RELEASED event.

Thanks.


Solution

  • Unless there is another reason to use the KEY_RELEASED event, then I would recommend switching to triggering on KEY_PRESSED for navigation as well and switch your EventFilter to an EventHandler. The example below will allow you to toggle the Alert on and off. When it's on, you'll notice that the button text doesn't change. When it's off, the button text will change. Take a look at how JavaFX constructs Event chains here if you haven't already.

    boolean error = false;
    int i = 0;
    
    @Override
    public void start(Stage primaryStage)
    {
        final Alert d = new Alert(Alert.AlertType.ERROR);
        d.initModality(Modality.WINDOW_MODAL);
    
        Button err = new Button();
        err.setText("Error off");
        err.addEventHandler(ActionEvent.ACTION, t ->
        {
            error = !error;
            if (error)
                err.setText("Error on");
            else
                err.setText("Error off");
        });
    
        Button btn = new Button();
        btn.setText("Say 'Hello World'");
    
        btn.addEventHandler(KeyEvent.KEY_PRESSED, event ->
        {
            if ((event.getCode() == KeyCode.ENTER || event.getCode() == KeyCode.TAB)) {
                if (error)
                {
                    d.showAndWait();
                }
                else
                {
                    i++;
                    btn.setText(String.valueOf(i));
                }
            }
        });
    
    
        Scene scene = new Scene(new VBox(btn, err), 300, 250);
    
        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }