Alerts in Javafx do not close when "x" button is pressed - javafx

Hi for a different javafx application I've been testing alerts and the only thing which doesn't work in pressing the "X" button for the alert box.
I have added a code below but if you don't have time to run it here is a GIF of explaining what issue I have with the alertbox:
https://giant.gfycat.com/GeneralUntimelyBluewhale.webm
I am not quite sure how to upload gifs to the actual post so sorry for that.
Is there any way of fixing this issue?
Thanks
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Playground extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(100);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
Button button = new Button("Alert");
button.setOnAction(event -> {
ButtonType goodButton = new ButtonType("Good");
ButtonType badButton = new ButtonType("Bad");
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "", goodButton, badButton);
alert.showAndWait();
if (alert.getResult().equals(goodButton)) {
System.out.println("Good");
} else if (alert.getResult().equals(badButton)) {
System.out.println("Bad");
}
});
// Add the buttons to the layout
root.getChildren().addAll(button);
// Show the Stage
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}

As per the "Dialog Closing Rules" in the Dialog API documentation, the default "X" button works normally only if atleast one of the buttons is of type "CANCEL". So changing any one of your button to ButtonType.CANCEL should close your dialog on click of "X".
If you are not interested in using built in buttons, then you have to explicitly handle the close request of dialog as per your requirement.
ButtonType goodButton = new ButtonType("Good");
ButtonType badButton = new ButtonType("Bad");
Alert alert = new Alert(Alert.AlertType.ERROR,"",goodButton,badButton);
Window window = alert.getDialogPane().getScene().getWindow();
window.setOnCloseRequest(e -> alert.hide());
Optional<ButtonType> result = alert.showAndWait();
result.ifPresent(res->{
if (res.equals(goodButton)) {
System.out.println("Good");
} else if (res.equals(badButton)) {
System.out.println("Bad");
}
});

To add to Sai Dandem's answer, here is the relevant javadoc from Dialog:
...
Dialog Closing Rules
It is important to understand what happens when a Dialog is closed, and also how a Dialog can be closed, especially in abnormal closing situations (such as when the 'X' button is clicked in a dialogs title bar, or when operating system specific keyboard shortcuts (such as alt-F4 on Windows) are entered). Fortunately, the outcome is well-defined in these situations, and can be best summarised in the following bullet points:
JavaFX dialogs can only be closed 'abnormally' (as defined above) in two situations:
When the dialog only has one button, or
When the dialog has multiple buttons, as long as one of them meets one of the following requirements:
The button has a ButtonType whose ButtonBar.ButtonData is of type ButtonBar.ButtonData.CANCEL_CLOSE.
The button has a ButtonType whose ButtonBar.ButtonData returns true when ButtonBar.ButtonData.isCancelButton() is called.
In all other situations, the dialog will refuse to respond to all close requests, remaining open until the user clicks on one of the available buttons in the DialogPane area of the dialog.
If a dialog is closed abnormally, and if the dialog contains a button which meets one of the two criteria above, the dialog will attempt to set the result property to whatever value is returned from calling the result converter with the first matching ButtonType.
If for any reason the result converter returns null, or if the dialog is closed when only one non-cancel button is present, the result property will be null, and the showAndWait() method will return Optional.empty(). This later point means that, if you use either of option 2 or option 3 (as presented earlier in this class documentation), the Optional.ifPresent(java.util.function.Consumer) lambda will never be called, and code will continue executing as if the dialog had not returned any value at all.
Normally, when using AlertType.CONFIRMATION, there would already be a cancel button. However, you're declaring your own buttons in the constructor of your Alert which overrides the default buttons.
Javadoc of Alert(AlertType,String,ButtonType...):
...
By passing in a variable number of ButtonType arguments, the developer is directly overriding the default buttons that will be displayed in the dialog, replacing the pre-defined buttons with whatever is specified in the varargs array.
...
And none of your buttons are a cancel button. Since you don't specify a ButtonData they all have ButtonBar.ButtonData.OTHER.

Alternatively, if you want the close "X" button of the alert window to default to one of the alert buttons you can set its ButtonData type:
ButtonType goodButton = new ButtonType("Good");
ButtonType badButton = new ButtonType("Bad", ButtonBar.ButtonData.CANCEL_CLOSE);
Closing the alert will then be equivalent to pressing the "Bad" button.

Related

ComboBox not close the popup after select value from list [duplicate]

I am encountering some strange issues after upgrading to latest JavaFX version (19).
If I set the last option as the value in ComboBox, then on first opening, the dropdown will not hide if I select any option. After that, the dropdown works as usual. The strange part is this only happens if I set the last option as value. When I set any other option apart from last option, it is working well.
In the above example, you can notice that:
If "Four" is set: the dropdown does not hide even when I select different options (only for first time).
If "Three" is set: the dropdown works as usual.
When I tried to investigate the root cause, I am almost concluded that it is because of the new feature "focusWithIn". Because that is where it is causing the issue to happen.
But on further investigation, I also noticed that the focus got messed up. In the above gif, you can also notice that the "focused" style is not applied on ComboBox if I set last option as value (again only for first time till I moved focus to another node and when I am back, it works as usual). Whereas the focused style works well if I set a different value.
When I tried to put some logs on focused property, below is the output when I focus on ComboBox (with last option as value):
ComboBox focused : true
ComboBox focused : false
The focus is immediately turned off!!
The only thing that confuses me is "Why only with last option??". I know issues/bugs will occur in general. But this particual relation with last option, I couldn't get it :)
Anyway, I tried different ways to make the dropdown hiding and focus to work, but none worked. Do any of you have any suggestions(workaround) to let this fix.
Below is the working demo:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ComboBoxDemo extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll("One", "Two", "Three", "Four");
comboBox.getSelectionModel().select("Four"); // Change value to any other options, it works
comboBox.focusedProperty().addListener((obs, old, focused) -> {
System.out.println("ComboBox focused : " + focused+", showing: "+comboBox.isShowing());
// ATTEMPT #2 : Requesting the focus by conditional checking (DIDN'T WORKED)
if(comboBox.isShowing() && !focused){
comboBox.requestFocus();
}
});
// ATTEMPT #1 : Requesting the focus after the dropdown is shown (DIDN'T WORKED)
comboBox.setOnShown(e -> {
System.out.println("ComboBox shown...");
comboBox.requestFocus();
});
VBox root = new VBox(new CheckBox("Just for focus grabbing"), comboBox);
root.setAlignment(Pos.CENTER);
root.setSpacing(20);
Scene scene = new Scene(root, 300, 200);
primaryStage.setTitle("ComboBox FX " + System.getProperty("javafx.runtime.version"));
primaryStage.setScene(scene);
primaryStage.show();
}
}
Definitely a bug in JavaFX 19.
I tested a bunch of things, and it seems that in order to set an initial selection for a ComboBox at the time of generation, it requires two ticks:
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll("One", "Two", "Three", "Four");
initializeComboBox(comboBox, 3);
private void initializeComboBox(ComboBox comboBox, int intialIndex) {
Platform.runLater(()->{
Platform.runLater(()->{
comboBox.getSelectionModel().select(intialIndex);
});
});
}
I cannot answer why it is required for the last item only. That will be a job for the developers. However, this will work as a workaround in the meantime for this particular use case in this version.

Issue with ComboBox focus in FX 19

I am encountering some strange issues after upgrading to latest JavaFX version (19).
If I set the last option as the value in ComboBox, then on first opening, the dropdown will not hide if I select any option. After that, the dropdown works as usual. The strange part is this only happens if I set the last option as value. When I set any other option apart from last option, it is working well.
In the above example, you can notice that:
If "Four" is set: the dropdown does not hide even when I select different options (only for first time).
If "Three" is set: the dropdown works as usual.
When I tried to investigate the root cause, I am almost concluded that it is because of the new feature "focusWithIn". Because that is where it is causing the issue to happen.
But on further investigation, I also noticed that the focus got messed up. In the above gif, you can also notice that the "focused" style is not applied on ComboBox if I set last option as value (again only for first time till I moved focus to another node and when I am back, it works as usual). Whereas the focused style works well if I set a different value.
When I tried to put some logs on focused property, below is the output when I focus on ComboBox (with last option as value):
ComboBox focused : true
ComboBox focused : false
The focus is immediately turned off!!
The only thing that confuses me is "Why only with last option??". I know issues/bugs will occur in general. But this particual relation with last option, I couldn't get it :)
Anyway, I tried different ways to make the dropdown hiding and focus to work, but none worked. Do any of you have any suggestions(workaround) to let this fix.
Below is the working demo:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ComboBoxDemo extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll("One", "Two", "Three", "Four");
comboBox.getSelectionModel().select("Four"); // Change value to any other options, it works
comboBox.focusedProperty().addListener((obs, old, focused) -> {
System.out.println("ComboBox focused : " + focused+", showing: "+comboBox.isShowing());
// ATTEMPT #2 : Requesting the focus by conditional checking (DIDN'T WORKED)
if(comboBox.isShowing() && !focused){
comboBox.requestFocus();
}
});
// ATTEMPT #1 : Requesting the focus after the dropdown is shown (DIDN'T WORKED)
comboBox.setOnShown(e -> {
System.out.println("ComboBox shown...");
comboBox.requestFocus();
});
VBox root = new VBox(new CheckBox("Just for focus grabbing"), comboBox);
root.setAlignment(Pos.CENTER);
root.setSpacing(20);
Scene scene = new Scene(root, 300, 200);
primaryStage.setTitle("ComboBox FX " + System.getProperty("javafx.runtime.version"));
primaryStage.setScene(scene);
primaryStage.show();
}
}
Definitely a bug in JavaFX 19.
I tested a bunch of things, and it seems that in order to set an initial selection for a ComboBox at the time of generation, it requires two ticks:
ComboBox<String> comboBox = new ComboBox<>();
comboBox.getItems().addAll("One", "Two", "Three", "Four");
initializeComboBox(comboBox, 3);
private void initializeComboBox(ComboBox comboBox, int intialIndex) {
Platform.runLater(()->{
Platform.runLater(()->{
comboBox.getSelectionModel().select(intialIndex);
});
});
}
I cannot answer why it is required for the last item only. That will be a job for the developers. However, this will work as a workaround in the meantime for this particular use case in this version.

How do I create a JavaFX Alert with a check box for "Do not ask again"?

I would like to use the standard JavaFX Alert class for a confirmation dialog that includes a check box for "Do not ask again". Is this possible, or do I have to create a custom Dialog from scratch?
I tried using the DialogPane.setExpandableContent() method, but that's not really what I want - this adds a Hide/Show button in the button bar, and the check box appears in the main body of the dialog, whereas I want the check box to appear in the button bar.
Yes, it is possible, with a little bit of work. You can override DialogPane.createDetailsButton() to return any node you want in place of the Hide/Show button. The trick is that you need to reconstruct the Alert after that, because you will have got rid of the standard contents created by the Alert. You also need to fool the DialogPane into thinking there is expanded content so that it shows your checkbox. Here's an example of a factory method to create an Alert with an opt-out check box. The text and action of the check box are customizable.
public static Alert createAlertWithOptOut(AlertType type, String title, String headerText,
String message, String optOutMessage, Consumer<Boolean> optOutAction,
ButtonType... buttonTypes) {
Alert alert = new Alert(type);
// Need to force the alert to layout in order to grab the graphic,
// as we are replacing the dialog pane with a custom pane
alert.getDialogPane().applyCss();
Node graphic = alert.getDialogPane().getGraphic();
// Create a new dialog pane that has a checkbox instead of the hide/show details button
// Use the supplied callback for the action of the checkbox
alert.setDialogPane(new DialogPane() {
#Override
protected Node createDetailsButton() {
CheckBox optOut = new CheckBox();
optOut.setText(optOutMessage);
optOut.setOnAction(e -> optOutAction.accept(optOut.isSelected()));
return optOut;
}
});
alert.getDialogPane().getButtonTypes().addAll(buttonTypes);
alert.getDialogPane().setContentText(message);
// Fool the dialog into thinking there is some expandable content
// a Group won't take up any space if it has no children
alert.getDialogPane().setExpandableContent(new Group());
alert.getDialogPane().setExpanded(true);
// Reset the dialog graphic using the default style
alert.getDialogPane().setGraphic(graphic);
alert.setTitle(title);
alert.setHeaderText(headerText);
return alert;
}
And here is an example of the factory method being used, where prefs is some preference store that saves the user's choice
Alert alert = createAlertWithOptOut(AlertType.CONFIRMATION, "Exit", null,
"Are you sure you wish to exit?", "Do not ask again",
param -> prefs.put(KEY_AUTO_EXIT, param ? "Always" : "Never"), ButtonType.YES, ButtonType.NO);
if (alert.showAndWait().filter(t -> t == ButtonType.YES).isPresent()) {
System.exit();
}
And here's what the dialog looks like:

Create Dialogs with Default Images in JavaFX (Info/Warning/Error)

I am creating a JavaFX Dialog and want to use the default icons for Info/Warning/Error.
In Swing, I can get the Information icon this way:
UIManager.getIcon("OptionPane.informationIcon")
How can I do the same in JavaFX?
I asked for this some days ago. I use this code to make labels with default icons:
Label img = new Label();
img.getStyleClass().addAll("alert", "error", "dialog-pane");
dialog.setGraphic(img);
If you get a copy of the images you can use these ideas.
Alert example:
#FXML void handleHelpButton(ActionEvent event){
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Help");
alert.setHeaderText("Help");
alert.setGraphic(new ImageView(this.getClass().getResource("img/help.png").toString()));
alert.setContentText("Place the cursor over a button for hint.");
Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
stage.getIcons().add(new Image(this.getClass().getResource("img/help.png").toString()));
alert.showAndWait();
}
Dialog example:
ChoiceDialog<String> dialog = new ChoiceDialog<>(currentFullscreenSetting, choices);
dialog.setTitle("Settings");
dialog.setHeaderText("Settings");
dialog.setContentText("Fullscreen on startup: ");
dialog.setGraphic(new ImageView(this.getClass().getResource("img/settings.png").toString()));
Stage stage2 = (Stage) dialog.getDialogPane().getScene().getWindow();
stage2.getIcons().add(new Image(this.getClass().getResource("img/settings.png").toString()));
// Traditional way to get the response value.
Optional<String> result = dialog.showAndWait();
This part of both examples is tricky. I noticed that this would not work unless I had the image in the same folder as the .fxml and controller.java files or in a folder that is in the same folder has the files mentioned. You might have to play with you file location. In my example it appears that my setGraphic and getIcons images are in the same folder, but they are not.
stage.getIcons().add(new
Image(this.getClass().getResource("img/help.png").toString()));
My file structure looks like:
    PlanningChart
        css
        img
        planningchart
            img
The second img folder holds the images for stage.getIcons.add(). The images could also be probably use to for setGraphic. I did not try it.
You do not need to create custom JavaFX dialogs for Info/Warning/Error, since JavaFX already have created Alerts for you.
The Alert class subclasses the Dialog class, and provides support for a number of pre-built dialog types that can be easily shown to users to prompt for a response.
You can create different types of Alerts, depending on the AlertType, it will embed the necessary image.
For Information alert use :
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Information Dialog");
alert.setHeaderText("An Information Dialog");
alert.setContentText("Information Message");
alert.showAndWait();
Similarly, for Warning alert, you can use
AlertType.WARNING
and for Error alert, you can use :
AlertType.ERROR

Refreshing tableView in JavaFx after closing dialog

I have two layouts in JavaFX app. The first one contains table view whereas the second one is a simple dialog to input data. The problem is that I want to refresh data after closing the dialog. Now I have a button on the first layout which refreshes data:
data.removeAll(data);
loadDataToTable();
But I don't want to invoke methods shown above with the button but automatically right after closing the dialog. I don't know how to make this, let's say, connection between those controllers.
thanks in advance
The new Dialog, if I am not wrong must be a new Stage ! Let us consider the new Stage to be modifyStage. We can call the onSetCloseRequest of the new Stage and put your code in it.
modifyStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent paramT) {
data.removeAll(data);
loadDataToTable();
}
});

Resources