JDK-8154851 : Update : WINDOW_MODAL dialog does not popup when restoring iconified owner window, which causes application freeze
  • Type: Bug
  • Component: javafx
  • Sub-Component: window-toolkit
  • Affected Version: 8u73,9
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: other
  • CPU: x86
  • Submitted: 2016-04-14
  • Updated: 2016-04-22
  • Resolved: 2016-04-22
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [version 10.0.10586]

A DESCRIPTION OF THE PROBLEM :
When you create a WINDOW_MODAL dialog while it's owner-window is minimized, and you then restore the owner window (click on task bar icon), the owner window pops up, but the dialog remains invisible : 

Now I know what happens : it's actually rendered off-screen, as the reported coordinates of minimized owner window are something like x = -32765, y = -32765

I have designed a fix using a listener on owner windows's x property and another on its iconified property to recenter the dialog when its deiconified based on its actual coordinates

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a scene containing at least two focusable controls (such as a TextFields for instance)
2. Set it as root for a new stage (Application's primaryStage for instance)
3.a. Add a handler for WindowEvent.ON_CLOSE_REQUEST on that stage
3.b. In this handler, make an Alert, set it's modality to WINDOW_MODAL and its owner as the stage, call its showAndWait() method
4. Launch application, minimize the main window, right-click its taskbar icon, and select "Close Window"
5. Restore the main window by clicking its icon on task bar : its frozen. You can't get focus into the TextField, can't minimize, maximize or close it
6. Press ESCAPE: window unfrozen (the alert has been closed)

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The dialog should pop up in front of the owner window when it's restored, so you could read and close it and get back control over your application
ACTUAL -
The dialog is invisible, and the owner window is frozen: you can't get focus into the TextField, can't minimize, maximize or close it

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
package application;
	
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;


public class Main extends Application {
	@Override
	public void start(Stage primaryStage) {
		try {
			BorderPane root = new BorderPane();
			TextField tf1 = new TextField();
			tf1.setPrefColumnCount(10);
			TextField tf2 = new TextField();
			tf2.setPrefColumnCount(10);
			VBox vb = new VBox(4, tf1, tf2);
			vb.setFillWidth(false);
			vb.setAlignment(Pos.CENTER);
			root.setCenter(vb);
			Scene scene = new Scene(root,400,400);
			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
			primaryStage.setScene(scene);
			
			primaryStage.addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, (event) -> {
				Alert alert = new Alert(AlertType.INFORMATION, "Close window ?", ButtonType.YES, ButtonType.NO);
				alert.initModality(Modality.WINDOW_MODAL);
				alert.initOwner(primaryStage);
				alert.showAndWait().ifPresent((result) -> {
					if (result == ButtonType.NO)
						event.consume();
				});
			});
			
			primaryStage.show();
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		launch(args);
	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
public T safeShowAndWait(Dialog<T> dialog) {
		Stage nfstage = null;
		if (dialog.getOwner() instanceof Stage)
			nfstage = (Stage) dialog.getOwner();
		Stage ostage = nfstage;
		ChangeListener<? super Boolean> listener = (observable, oldValue, newValue) -> {
			System.err.println(observable + ": " + oldValue + " -> " + newValue);
			if (dialog == null || dialog.getDialogPane() == null || dialog.getDialogPane().getScene() == null)
				return;
			if (!newValue) { // Owner window is being de-iconified
				Window w = dialog.getDialogPane().getScene().getWindow();
				if (w instanceof Stage && dialog.getOwner() instanceof Stage) {
					Stage stage = (Stage) dialog.getOwner();
					Stage wStage = (Stage) w;
					//Get all screens displaying part of the dialog
					List<Screen> scrs = Screen.getScreensForRectangle(wStage.getX(), wStage.getY(), wStage.getWidth(), wStage.getHeight());
					if (scrs.size() == 0) { //No screen found : dialog is off-screen
						//Center dialog on owner window
						double x = stage.getX() + (stage.getWidth() - wStage.getWidth()) / 2.0;
						double y = stage.getY() + (stage.getHeight() - wStage.getHeight()) / 2.0;
						//Apply coordinates
						wStage.setX(x);
						wStage.setY(y);
					}
				}
			}
		};
		ChangeListener<? super Number> xListener = (observable, oldValue, newValue) -> {
			//Detect de-iconification when window is maximized using value of stage.xProperty();
			if (oldValue == null || newValue == null)
				return;
			if (ostage.isMaximized()) {
				if (newValue.doubleValue() <= -32000 && oldValue.doubleValue() > -32000)
					Platform.runLater(() -> ostage.setIconified(true));
				else if (newValue.doubleValue() > -32000 && oldValue.doubleValue() <= -32000)
					Platform.runLater(() -> ostage.setIconified(false));
			}
		};
		
		try {
			if (ostage != null) {
				ostage.iconifiedProperty().addListener(listener);
				ostage.xProperty().addListener(xListener);
			}
			
			Optional<T> bType = dialog.showAndWait();
			
			if (bType.isPresent())
				return bType.get();
			return null;
			
		} finally {
			if (ostage != null) {
				ostage.iconifiedProperty().removeListener(listener);
				ostage.xProperty().removeListener(xListener);
			}
		}
	}


Comments
Duplicate of JDK-8151170. I added a comment to that bug with a pointer to the additional information in this one.
22-04-2016