/*
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.scene.control;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Region;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
// Not public API (class is package-protected), so no JavaDoc is required.
class HeavyweightDialog extends FXDialog {
Private fields
/**************************************************************************
*
* Private fields
*
**************************************************************************/
final Stage stage = new Stage() {
@Override public void centerOnScreen() {
Window owner = HeavyweightDialog.this.getOwner();
if (owner != null) {
positionStage();
} else {
if (getWidth() > 0 && getHeight() > 0) {
super.centerOnScreen();
}
}
}
};
private Scene scene;
private final Parent DUMMY_ROOT = new Region();
private final Dialog<?> dialog;
private DialogPane dialogPane;
private double prefX = Double.NaN;
private double prefY = Double.NaN;
Constructors
/**************************************************************************
*
* Constructors
*
**************************************************************************/
HeavyweightDialog(Dialog<?> dialog) {
this.dialog = dialog;
stage.setResizable(false);
stage.setOnCloseRequest(windowEvent -> {
if (requestPermissionToClose(dialog)) {
dialog.close();
} else {
// if we are here, we consume the event to prevent closing the dialog
windowEvent.consume();
}
});
stage.addEventHandler(KeyEvent.KEY_PRESSED, keyEvent -> {
if (keyEvent.getCode() == KeyCode.ESCAPE) {
if (!keyEvent.isConsumed() && requestPermissionToClose(dialog)) {
dialog.close();
keyEvent.consume();
}
}
});
}
Public API
/**************************************************************************
*
* Public API
*
**************************************************************************/
@Override void initStyle(StageStyle style) {
stage.initStyle(style);
}
@Override StageStyle getStyle() {
return stage.getStyle();
}
@Override public void initOwner(Window newOwner) {
updateStageBindings(stage.getOwner(), newOwner);
stage.initOwner(newOwner);
}
@Override public Window getOwner() {
return stage.getOwner();
}
@Override public void initModality(Modality modality) {
stage.initModality(modality == null? Modality.APPLICATION_MODAL : modality);
}
@Override public Modality getModality() {
return stage.getModality();
}
@Override public void setDialogPane(DialogPane dialogPane) {
this.dialogPane = dialogPane;
if (scene == null) {
scene = new Scene(dialogPane);
stage.setScene(scene);
} else {
scene.setRoot(dialogPane);
}
dialogPane.autosize();
stage.sizeToScene();
}
@Override public void show() {
scene.setRoot(dialogPane);
stage.centerOnScreen();
stage.show();
}
@Override public void showAndWait() {
scene.setRoot(dialogPane);
stage.centerOnScreen();
stage.showAndWait();
}
@Override public void close() {
if (stage.isShowing()) {
stage.hide();
}
// Refer to RT-40687 for more context
if (scene != null) {
scene.setRoot(DUMMY_ROOT);
}
}
@Override public ReadOnlyBooleanProperty showingProperty() {
return stage.showingProperty();
}
@Override public Window getWindow() {
return stage;
}
@Override public Node getRoot() {
return stage.getScene().getRoot();
}
// --- x
@Override public double getX() {
return stage.getX();
}
@Override public void setX(double x) {
stage.setX(x);
}
@Override public ReadOnlyDoubleProperty xProperty() {
return stage.xProperty();
}
// --- y
@Override public double getY() {
return stage.getY();
}
@Override public void setY(double y) {
stage.setY(y);
}
@Override public ReadOnlyDoubleProperty yProperty() {
return stage.yProperty();
}
@Override ReadOnlyDoubleProperty heightProperty() {
return stage.heightProperty();
}
@Override void setHeight(double height) {
stage.setHeight(height);
}
@Override double getSceneHeight() {
return scene == null ? 0 : scene.getHeight();
}
@Override ReadOnlyDoubleProperty widthProperty() {
return stage.widthProperty();
}
@Override void setWidth(double width) {
stage.setWidth(width);
}
@Override BooleanProperty resizableProperty() {
return stage.resizableProperty();
}
@Override StringProperty titleProperty() {
return stage.titleProperty();
}
@Override ReadOnlyBooleanProperty focusedProperty() {
return stage.focusedProperty();
}
@Override public void sizeToScene() {
stage.sizeToScene();
}
Private implementation
/**************************************************************************
*
* Private implementation
*
**************************************************************************/
private void positionStage() {
double x = getX();
double y = getY();
// if the user has specified an x/y location, use it
if (!Double.isNaN(x) && !Double.isNaN(y) &&
Double.compare(x, prefX) != 0 && Double.compare(y, prefY) != 0) {
// weird, but if I don't call setX/setY here, the stage
// isn't where I expect it to be (in instances where a single
// dialog is shown and closed multiple times). I expect the
// second showing to be in the place the dialog was when it
// was closed the first time, but on Windows it jumps to the
// top-left of the screen.
setX(x);
setY(y);
return;
}
// Firstly we need to force CSS and layout to happen, as the dialogPane
// may not have been shown yet (so it has no dimensions)
dialogPane.applyCss();
dialogPane.layout();
final Window owner = getOwner();
final Scene ownerScene = owner.getScene();
// scene.getY() seems to represent the y-offset from the top of the titlebar to the
// start point of the scene, so it is the titlebar height
final double titleBarHeight = ownerScene.getY();
// because Stage does not seem to centre itself over its owner, we
// do it here.
// then we can get the dimensions and position the dialog appropriately.
final double dialogWidth = dialogPane.prefWidth(-1);
final double dialogHeight = dialogPane.prefHeight(dialogWidth);
// stage.sizeToScene();
x = owner.getX() + (ownerScene.getWidth() / 2.0) - (dialogWidth / 2.0);
y = owner.getY() + titleBarHeight / 2.0 + (ownerScene.getHeight() / 2.0) - (dialogHeight / 2.0);
prefX = x;
prefY = y;
setX(x);
setY(y);
}
// this method ensures the internal dialog stage is bound to the owner window
// properties as appropriate
private void updateStageBindings(Window oldOwner, Window newOwner) {
final Scene dialogScene = stage.getScene();
if (oldOwner != null && oldOwner instanceof Stage) {
Stage oldStage = (Stage) oldOwner;
Bindings.unbindContent(stage.getIcons(), oldStage.getIcons());
Scene oldScene = oldStage.getScene();
if (scene != null && dialogScene != null) {
Bindings.unbindContent(dialogScene.getStylesheets(), oldScene.getStylesheets());
}
}
// put the icons and stylesheets of the owner window into the dialog
if (newOwner instanceof Stage) {
Stage newStage = (Stage) newOwner;
Bindings.bindContent(stage.getIcons(), newStage.getIcons());
Scene newScene = newStage.getScene();
if (scene != null && dialogScene != null) {
Bindings.bindContent(dialogScene.getStylesheets(), newScene.getStylesheets());
}
}
}
}