Reputation: 4209
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.
Upvotes: 1
Views: 760
Reputation: 1938
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();
}
Upvotes: 1
Reputation: 4209
So I have a hack that works - I really don't like it, so I am hoping that there is a better solution (a clean one) to solve this issue....
Basically, right before I open my dialog, I set a boolean so I know it's open. Then in my event filter, kick out if that is set to true.
public class EventTester extends Application{
public static void main(String[] args){
launch(args);
}
private boolean modalWasShowing = false;
@Override
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(modalWasShowing){
modalWasShowing=false;
return;
}
if ((event.getCode() == KeyCode.ENTER || event.getCode() == KeyCode.TAB)) {
modalWasShowing = true;
d.showAndWait();
}
});
Scene scene = new Scene(new StackPane(btn), 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
Please let me know if you know of a better way to handle this.
Upvotes: 0