In JavaFX (11), I'm trying to show an alert window above the main window, but I don't want the main window to be clickable. To do this, I thought I had to change the initModality of that alert window (https://docs.oracle.com/javafx/2/api/javafx/stage/Stage.html see 'Modality'). Does it not work because the owner of the alert window is null?
Here's my controller class. The main class is just the default JavaFX Main class generated by IntelliJ IDEA and the .fxml file is just an anchorpane with a button for testing purposes.
package sample;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.stage.Modality;
public class Controller {
public Button button;
public void initialize(){
Alert alert = new Alert(Alert.AlertType.ERROR);
System.out.println(alert.getOwner()); //output: null
alert.initModality(Modality.APPLICATION_MODAL);
alert.setHeight(200);
alert.setWidth(300);
alert.show();
button.setOnAction(push -> System.out.println("button pressed")); //button is still pressable
}
}
The button in the main window is still pressable, so the initModality didn't work. I also tried it with Modality.WINDOW_MODAL
and Modality.NONE
, no change.
I'm on Linux with bspwm if that matters.
I believe the issue is the order the windows are shown. Testing this with the following reproduced the issue on Windows 10 (using JavaFX 12.0.1):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
var alert = new Alert(AlertType.INFORMATION);
alert.initModality(Modality.APPLICATION_MODAL);
alert.setHeaderText(null);
alert.setContentText("This is a test of application modality.");
alert.show();
primaryStage.setScene(new Scene(new StackPane(new Label("Hello, World!")), 500, 300));
primaryStage.show();
}
}
The above results in a dialog window that isn't properly modal. However, if you move alert.show()
to after primaryStage.show()
everything works as expected.
Since you are showing your Alert
inside the initialize()
method of an FXML controller, it's likely that code is being executed before you show your primary window. If you want to display the Alert
right at the start of your application there are at least two options:
Call show()
inside a Platform.runLater
call, as you do in your answer.
Add a method to your Controller
class and invoke it after showing your other window.
@Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(/*location*/);
primaryStage.setScene(new Scene(loader.load()));
primaryStage.show();
loader.<Controller>getController().showMyAwesomeAlert();
}
I fixed it by putting the alert code in a Platform.runLater()
method like so:
public void initialize(){
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.initModality(Modality.APPLICATION_MODAL);
alert.setHeight(200);
alert.setWidth(300);
Platform.runLater(alert::show());
button.setOnAction(push -> {
System.out.println("button pressed");
});
}
The main window is now not interactive, just like I wanted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With