modules/graphics/src/main/java/javafx/stage/Window.java

Print this page




  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.stage;
  27 
  28 import java.security.AllPermission;
  29 import java.security.AccessControlContext;
  30 import java.security.AccessController;
  31 import java.util.ArrayList;
  32 import java.util.HashMap;
  33 import java.util.Iterator;
  34 import java.util.List;
  35 
  36 import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection;
  37 import javafx.beans.property.DoubleProperty;
  38 import javafx.beans.property.DoublePropertyBase;
  39 import javafx.beans.property.ObjectProperty;
  40 import javafx.beans.property.ObjectPropertyBase;
  41 import javafx.beans.property.ReadOnlyBooleanProperty;
  42 import javafx.beans.property.ReadOnlyBooleanWrapper;
  43 import javafx.beans.property.ReadOnlyObjectProperty;
  44 import javafx.beans.property.ReadOnlyObjectWrapper;
  45 import javafx.beans.property.ReadOnlyDoubleProperty;
  46 import javafx.beans.property.ReadOnlyDoubleWrapper;
  47 import javafx.beans.property.SimpleObjectProperty;

  48 import javafx.collections.FXCollections;
  49 import javafx.collections.ObservableList;
  50 import javafx.collections.ObservableMap;
  51 import javafx.event.Event;
  52 import javafx.event.EventDispatchChain;
  53 import javafx.event.EventDispatcher;
  54 import javafx.event.EventHandler;
  55 import javafx.event.EventTarget;
  56 import javafx.event.EventType;
  57 import javafx.geometry.Rectangle2D;
  58 import javafx.scene.Scene;
  59 
  60 import com.sun.javafx.util.Utils;
  61 import com.sun.javafx.util.WeakReferenceQueue;
  62 import com.sun.javafx.css.StyleManager;
  63 import com.sun.javafx.stage.WindowEventDispatcher;
  64 import com.sun.javafx.stage.WindowHelper;
  65 import com.sun.javafx.stage.WindowPeerListener;
  66 import com.sun.javafx.tk.TKPulseListener;
  67 import com.sun.javafx.tk.TKScene;
  68 import com.sun.javafx.tk.TKStage;
  69 import com.sun.javafx.tk.Toolkit;


  70 
  71 
  72 /**
  73  * <p>
  74  *     A top level window within which a scene is hosted, and with which the user
  75  *     interacts. A Window might be a {@link Stage}, {@link PopupWindow}, or other
  76  *     such top level. A Window is used also for browser plug-in based deployments.
  77  * </p>
  78  *
  79  * @since JavaFX 2.0
  80  */
  81 public class Window implements EventTarget {
  82 
  83     /**
  84      * A list of all the currently _showing_ windows. This is publicly accessible via the unmodifiableWindows wrapper.
  85      */
  86     private static ObservableList<Window> windows = FXCollections.observableArrayList();
  87     private static ObservableList<Window> unmodifiableWindows = FXCollections.unmodifiableObservableList(windows);
  88 
  89     static {


  91                 new WindowHelper.WindowAccessor() {
  92                     /**
  93                      * Allow window peer listeners to directly change reported
  94                      * window location and size without changing the xExplicit,
  95                      * yExplicit, widthExplicit and heightExplicit values.
  96                      */
  97                     @Override
  98                     public void notifyLocationChanged(
  99                             Window window, double x, double y) {
 100                         window.notifyLocationChanged(x, y);
 101                     }
 102 
 103                     @Override
 104                     public void notifySizeChanged(Window window,
 105                                                   double width,
 106                                                   double height) {
 107                         window.notifySizeChanged(width, height);
 108                     }
 109 
 110                     @Override







 111                     public void notifyScreenChanged(Window window,
 112                                                   Object from,
 113                                                   Object to) {
 114                         window.notifyScreenChanged(from, to);
 115                     }
 116 
 117                     @Override
 118                     public float getUIScale(Window window) {
 119                         TKStage peer = window.impl_peer;
 120                         return peer == null ? 1.0f : peer.getUIScale();
 121                     }
 122 
 123                     @Override
 124                     public float getRenderScale(Window window) {
 125                         TKStage peer = window.impl_peer;
 126                         return peer == null ? 1.0f : peer.getRenderScale();
 127                     }
 128 
 129                     @Override
 130                     public ReadOnlyObjectProperty<Screen> screenProperty(Window window) {
 131                         return window.screenProperty();
 132                     }
 133 
 134                     @Override
 135                     public AccessControlContext getAccessControlContext(Window window) {
 136                         return window.acc;
 137                     }
 138                 });
 139     }
 140 
 141     /**
 142      * Returns a list containing a reference to the currently showing JavaFX windows. The list is unmodifiable -
 143      * attempting to modify this list will result in an {@link UnsupportedOperationException} being thrown at runtime.
 144      *
 145      * @return A list containing all windows that are currently showing.
 146      * @since 9


 272         xExplicit = false;
 273         yExplicit = false;
 274         if (impl_peer != null) {
 275             Rectangle2D bounds = getWindowScreen().getVisualBounds();
 276             double centerX =
 277                     bounds.getMinX() + (bounds.getWidth() - getWidth())
 278                                            * CENTER_ON_SCREEN_X_FRACTION;
 279             double centerY =
 280                     bounds.getMinY() + (bounds.getHeight() - getHeight())
 281                                            * CENTER_ON_SCREEN_Y_FRACTION;
 282 
 283             x.set(centerX);
 284             y.set(centerY);
 285             peerBoundsConfigurator.setLocation(centerX, centerY,
 286                                                CENTER_ON_SCREEN_X_FRACTION,
 287                                                CENTER_ON_SCREEN_Y_FRACTION);
 288             applyBounds();
 289         }
 290     }
 291 



















































































































































 292     private boolean xExplicit = false;
 293     /**
 294      * The horizontal location of this {@code Stage} on the screen. Changing
 295      * this attribute will move the {@code Stage} horizontally. Changing this
 296      * attribute will not visually affect a {@code Stage} while
 297      * {@code fullScreen} is true, but will be honored by the {@code Stage} once
 298      * {@code fullScreen} becomes false.
 299      */
 300     private ReadOnlyDoubleWrapper x =
 301             new ReadOnlyDoubleWrapper(this, "x", Double.NaN);
 302 
 303     public final void setX(double value) {
 304         setXInternal(value);
 305     }
 306     public final double getX() { return x.get(); }
 307     public final ReadOnlyDoubleProperty xProperty() { return x.getReadOnlyProperty(); }
 308 
 309     void setXInternal(double value) {
 310         x.set(value);
 311         peerBoundsConfigurator.setX(value, 0);


 825                 fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_HIDING));
 826             }
 827 
 828             oldVisible = newVisible;
 829             impl_visibleChanging(newVisible);
 830             if (newVisible) {
 831                 hasBeenVisible = true;
 832                 windows.add(Window.this);
 833             } else {
 834                 windows.remove(Window.this);
 835             }
 836             Toolkit tk = Toolkit.getToolkit();
 837             if (impl_peer != null) {
 838                 if (newVisible) {
 839                     if (peerListener == null) {
 840                         peerListener = new WindowPeerListener(Window.this);
 841                     }
 842 
 843                     // Setup listener for changes coming back from peer
 844                     impl_peer.setTKStageListener(peerListener);

 845                     // Register pulse listener
 846                     tk.addStageTkPulseListener(peerBoundsConfigurator);
 847 
 848                     if (getScene() != null) {
 849                         getScene().impl_initPeer();
 850                         impl_peer.setScene(getScene().impl_getPeer());
 851                         getScene().impl_preferredSize();
 852                     }
 853 
 854                     // Set peer bounds
 855                     if ((getScene() != null) && (!widthExplicit || !heightExplicit)) {
 856                         adjustSize(true);
 857                     } else {
 858                         peerBoundsConfigurator.setSize(
 859                                 getWidth(), getHeight(), -1, -1);
 860                     }
 861 
 862                     if (!xExplicit && !yExplicit) {
 863                         centerOnScreen();
 864                     } else {


1188             }
1189 
1190             window = window.getWindowOwner();
1191         } while (window != null);
1192 
1193         return Screen.getPrimary();
1194     }
1195 
1196     private final ReadOnlyObjectWrapper<Screen> screen = new ReadOnlyObjectWrapper<>(Screen.getPrimary());
1197     private ReadOnlyObjectProperty<Screen> screenProperty() { return screen.getReadOnlyProperty(); }
1198 
1199     private void notifyScreenChanged(Object from, Object to) {
1200         screen.set(Screen.getScreenForNative(to));
1201     }
1202 
1203     /**
1204      * Caches all requested bounds settings and applies them at once during
1205      * the next pulse.
1206      */
1207     private final class TKBoundsConfigurator implements TKPulseListener {


1208         private double x;
1209         private double y;
1210         private float xGravity;
1211         private float yGravity;
1212         private double windowWidth;
1213         private double windowHeight;
1214         private double clientWidth;
1215         private double clientHeight;
1216 
1217         private boolean dirty;
1218 
1219         public TKBoundsConfigurator() {
1220             reset();
1221         }
1222 










1223         public void setX(final double x, final float xGravity) {
1224             this.x = x;
1225             this.xGravity = xGravity;
1226             setDirty();
1227         }
1228 
1229         public void setY(final double y, final float yGravity) {
1230             this.y = y;
1231             this.yGravity = yGravity;
1232             setDirty();
1233         }
1234 
1235         public void setWindowWidth(final double windowWidth) {
1236             this.windowWidth = windowWidth;
1237             setDirty();
1238         }
1239 
1240         public void setWindowHeight(final double windowHeight) {
1241             this.windowHeight = windowHeight;
1242             setDirty();


1259             this.x = x;
1260             this.y = y;
1261             this.xGravity = xGravity;
1262             this.yGravity = yGravity;
1263             setDirty();
1264         }
1265 
1266         public void setSize(final double windowWidth,
1267                             final double windowHeight,
1268                             final double clientWidth,
1269                             final double clientHeight) {
1270             this.windowWidth = windowWidth;
1271             this.windowHeight = windowHeight;
1272             this.clientWidth = clientWidth;
1273             this.clientHeight = clientHeight;
1274             setDirty();
1275         }
1276 
1277         public void apply() {
1278             if (dirty) {
1279                 impl_peer.setBounds((float) (Double.isNaN(x) ? 0 : x),
1280                                     (float) (Double.isNaN(y) ? 0 : y),
1281                                     !Double.isNaN(x),
1282                                     !Double.isNaN(y),
1283                                     (float) windowWidth,
1284                                     (float) windowHeight,
1285                                     (float) clientWidth,
1286                                     (float) clientHeight,
1287                                     xGravity, yGravity);
1288 





1289                 reset();




1290             }
1291         }
1292 
1293         @Override
1294         public void pulse() {
1295             apply();
1296         }
1297 
1298         private void reset() {


1299             x = Double.NaN;
1300             y = Double.NaN;
1301             xGravity = 0;
1302             yGravity = 0;
1303             windowWidth = -1;
1304             windowHeight = -1;
1305             clientWidth = -1;
1306             clientHeight = -1;
1307             dirty = false;
1308         }
1309 
1310         private void setDirty() {
1311             if (!dirty) {
1312                 Toolkit.getToolkit().requestNextPulse();
1313                 dirty = true;
1314             }
1315         }
1316     }
1317 }


  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.stage;
  27 
  28 import java.security.AllPermission;
  29 import java.security.AccessControlContext;
  30 import java.security.AccessController;

  31 import java.util.HashMap;


  32 
  33 import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection;
  34 import javafx.beans.property.DoubleProperty;
  35 import javafx.beans.property.DoublePropertyBase;
  36 import javafx.beans.property.ObjectProperty;
  37 import javafx.beans.property.ObjectPropertyBase;
  38 import javafx.beans.property.ReadOnlyBooleanProperty;
  39 import javafx.beans.property.ReadOnlyBooleanWrapper;
  40 import javafx.beans.property.ReadOnlyObjectProperty;
  41 import javafx.beans.property.ReadOnlyObjectWrapper;
  42 import javafx.beans.property.ReadOnlyDoubleProperty;
  43 import javafx.beans.property.ReadOnlyDoubleWrapper;
  44 import javafx.beans.property.SimpleObjectProperty;
  45 import javafx.beans.property.SimpleDoubleProperty;
  46 import javafx.collections.FXCollections;
  47 import javafx.collections.ObservableList;
  48 import javafx.collections.ObservableMap;
  49 import javafx.event.Event;
  50 import javafx.event.EventDispatchChain;
  51 import javafx.event.EventDispatcher;
  52 import javafx.event.EventHandler;
  53 import javafx.event.EventTarget;
  54 import javafx.event.EventType;
  55 import javafx.geometry.Rectangle2D;
  56 import javafx.scene.Scene;
  57 
  58 import com.sun.javafx.util.Utils;

  59 import com.sun.javafx.css.StyleManager;
  60 import com.sun.javafx.stage.WindowEventDispatcher;
  61 import com.sun.javafx.stage.WindowHelper;
  62 import com.sun.javafx.stage.WindowPeerListener;
  63 import com.sun.javafx.tk.TKPulseListener;
  64 import com.sun.javafx.tk.TKScene;
  65 import com.sun.javafx.tk.TKStage;
  66 import com.sun.javafx.tk.Toolkit;
  67 import javafx.beans.property.BooleanProperty;
  68 import javafx.beans.property.SimpleBooleanProperty;
  69 
  70 
  71 /**
  72  * <p>
  73  *     A top level window within which a scene is hosted, and with which the user
  74  *     interacts. A Window might be a {@link Stage}, {@link PopupWindow}, or other
  75  *     such top level. A Window is used also for browser plug-in based deployments.
  76  * </p>
  77  *
  78  * @since JavaFX 2.0
  79  */
  80 public class Window implements EventTarget {
  81 
  82     /**
  83      * A list of all the currently _showing_ windows. This is publicly accessible via the unmodifiableWindows wrapper.
  84      */
  85     private static ObservableList<Window> windows = FXCollections.observableArrayList();
  86     private static ObservableList<Window> unmodifiableWindows = FXCollections.unmodifiableObservableList(windows);
  87 
  88     static {


  90                 new WindowHelper.WindowAccessor() {
  91                     /**
  92                      * Allow window peer listeners to directly change reported
  93                      * window location and size without changing the xExplicit,
  94                      * yExplicit, widthExplicit and heightExplicit values.
  95                      */
  96                     @Override
  97                     public void notifyLocationChanged(
  98                             Window window, double x, double y) {
  99                         window.notifyLocationChanged(x, y);
 100                     }
 101 
 102                     @Override
 103                     public void notifySizeChanged(Window window,
 104                                                   double width,
 105                                                   double height) {
 106                         window.notifySizeChanged(width, height);
 107                     }
 108 
 109                     @Override
 110                     public void notifyScaleChanged(Window window,
 111                                                    double newOutputScaleX,
 112                                                    double newOutputScaleY) {
 113                         window.updateOutputScales(newOutputScaleX, newOutputScaleY);
 114                     }
 115 
 116                     @Override
 117                     public void notifyScreenChanged(Window window,
 118                                                   Object from,
 119                                                   Object to) {
 120                         window.notifyScreenChanged(from, to);
 121                     }
 122 
 123                     @Override
 124                     public float getPlatformScaleX(Window window) {
 125                         TKStage peer = window.impl_peer;
 126                         return peer == null ? 1.0f : peer.getPlatformScaleX();
 127                     }
 128 
 129                     @Override
 130                     public float getPlatformScaleY(Window window) {
 131                         TKStage peer = window.impl_peer;
 132                         return peer == null ? 1.0f : peer.getPlatformScaleY();
 133                     }
 134 
 135                     @Override
 136                     public ReadOnlyObjectProperty<Screen> screenProperty(Window window) {
 137                         return window.screenProperty();
 138                     }
 139 
 140                     @Override
 141                     public AccessControlContext getAccessControlContext(Window window) {
 142                         return window.acc;
 143                     }
 144                 });
 145     }
 146 
 147     /**
 148      * Returns a list containing a reference to the currently showing JavaFX windows. The list is unmodifiable -
 149      * attempting to modify this list will result in an {@link UnsupportedOperationException} being thrown at runtime.
 150      *
 151      * @return A list containing all windows that are currently showing.
 152      * @since 9


 278         xExplicit = false;
 279         yExplicit = false;
 280         if (impl_peer != null) {
 281             Rectangle2D bounds = getWindowScreen().getVisualBounds();
 282             double centerX =
 283                     bounds.getMinX() + (bounds.getWidth() - getWidth())
 284                                            * CENTER_ON_SCREEN_X_FRACTION;
 285             double centerY =
 286                     bounds.getMinY() + (bounds.getHeight() - getHeight())
 287                                            * CENTER_ON_SCREEN_Y_FRACTION;
 288 
 289             x.set(centerX);
 290             y.set(centerY);
 291             peerBoundsConfigurator.setLocation(centerX, centerY,
 292                                                CENTER_ON_SCREEN_X_FRACTION,
 293                                                CENTER_ON_SCREEN_Y_FRACTION);
 294             applyBounds();
 295         }
 296     }
 297 
 298     private void updateOutputScales(double sx, double sy) {
 299         // We call updateRenderScales() before updating the property
 300         // values so that an application can listen to the properties
 301         // and set their own values overriding the default values we set.
 302         updateRenderScales(sx, sy);
 303         // Now set the properties and trigger any potential listeners.
 304         outputScaleX.set(sx);
 305         outputScaleY.set(sy);
 306     }
 307 
 308     void updateRenderScales(double sx, double sy) {
 309         boolean forceInt = forceIntegerRenderScale.get();
 310         if (!renderScaleX.isBound()) {
 311             renderScaleX.set(forceInt ? Math.ceil(sx) : sx);
 312         }
 313         if (!renderScaleY.isBound()) {
 314             renderScaleY.set(forceInt ? Math.ceil(sy) : sy);
 315         }
 316     }
 317 
 318     /**
 319      * The scale that the {@code Window} will apply to horizontal scene
 320      * coordinates in all stages of rendering and compositing the output
 321      * to the screen or other destination device.
 322      * This property is updated asynchronously by the system at various
 323      * times including:
 324      * <ul>
 325      * <li>Window creation
 326      * <li>At some point during moving a window to a new {@code Screen}
 327      * which may be before or after the {@link Screen} property is updated.
 328      * <li>In response to a change in user preferences for output scaling.
 329      * </ul>
 330      * @see outputScaleThreshold
 331      */
 332     private ReadOnlyDoubleWrapper outputScaleX =
 333         new ReadOnlyDoubleWrapper(this, "outputScaleX", 1.0);
 334     public final double getOutputScaleX() {
 335         return outputScaleX.get();
 336     }
 337     public final ReadOnlyDoubleProperty outputScaleXProperty() {
 338         return outputScaleX.getReadOnlyProperty();
 339     }
 340 
 341     /**
 342      * The scale that the {@code Window} will apply to vertical scene
 343      * coordinates in all stages of rendering and compositing the output
 344      * to the screen or other destination device.
 345      * This property is updated asynchronously by the system at various
 346      * times including:
 347      * <ul>
 348      * <li>Window creation
 349      * <li>At some point during moving a window to a new {@code Screen}
 350      * which may be before or after the {@link Screen} property is updated.
 351      * <li>In response to a change in user preferences for output scaling.
 352      * </ul>
 353      * @see outputScaleThreshold
 354      */
 355     private ReadOnlyDoubleWrapper outputScaleY =
 356         new ReadOnlyDoubleWrapper(this, "outputScaleY", 1.0);
 357     public final double getOutputScaleY() {
 358         return outputScaleY.get();
 359     }
 360     public final ReadOnlyDoubleProperty outputScaleYProperty() {
 361         return outputScaleY.getReadOnlyProperty();
 362     }
 363 
 364     /**
 365      * Boolean property that controls whether only integer render scales
 366      * are set by default by the system when there is a change in the
 367      * associated output scale.
 368      * The {@code renderScale} properties will be updated directly and
 369      * simultaneously with any changes in the associated {@code outputScale}
 370      * properties, but the values can be overridden by subsequent calls to
 371      * the {@code setRenderScale} setters or through appropriate use of
 372      * binding.
 373      * This property will not prevent setting non-integer scales
 374      * directly using the {@code renderScale} property object or the
 375      * convenience setter method.
 376      */
 377     private BooleanProperty forceIntegerRenderScale =
 378         new SimpleBooleanProperty(this, "forceIntegerRenderScale", false) {
 379             @Override
 380             protected void invalidated() {
 381                 updateRenderScales(getOutputScaleX(),
 382                                    getOutputScaleY());
 383             }
 384         };
 385     public final void setForceIntegerRenderScale(boolean forced) {
 386         forceIntegerRenderScale.set(forced);
 387     }
 388     public final boolean isForceIntegerRenderScale() {
 389         return forceIntegerRenderScale.get();
 390     }
 391     public final BooleanProperty forceIntegerRenderScaleProperty() {
 392         return forceIntegerRenderScale;
 393     }
 394 
 395     /**
 396      * The horizontal scale that the {@code Window} will use when rendering
 397      * its {@code Scene} to the rendering buffer.
 398      * This property is automatically updated whenever there is a change in
 399      * the {@link outputScaleX} property and can be overridden either by
 400      * calling {@code setRenderScaleX()} in response to a listener on the
 401      * {@code outputScaleX} property or by binding it appropriately.
 402      */
 403     private DoubleProperty renderScaleX =
 404         new SimpleDoubleProperty(this, "renderScaleX", 1.0) {
 405             @Override
 406             protected void invalidated() {
 407                 peerBoundsConfigurator.setRenderScaleX(get());
 408             }
 409         };
 410     public final void setRenderScaleX(double scale) {
 411         renderScaleX.set(scale);
 412     }
 413     public final double getRenderScaleX() {
 414         return renderScaleX.get();
 415     }
 416     public final DoubleProperty renderScaleXProperty() {
 417         return renderScaleX;
 418     }
 419 
 420     /**
 421      * The vertical scale that the {@code Window} will use when rendering
 422      * its {@code Scene} to the rendering buffer.
 423      * This property is automatically updated whenever there is a change in
 424      * the {@link outputScaleY} property and can be overridden either by
 425      * calling {@code setRenderScaleY()} in response to a listener on the
 426      * {@code outputScaleY} property or by binding it appropriately.
 427      */
 428     private DoubleProperty renderScaleY =
 429         new SimpleDoubleProperty(this, "renderScaleY", 1.0) {
 430             @Override
 431             protected void invalidated() {
 432                 peerBoundsConfigurator.setRenderScaleY(get());
 433             }
 434         };
 435     public final void setRenderScaleY(double scale) {
 436         renderScaleY.set(scale);
 437     }
 438     public final double getRenderScaleY() {
 439         return renderScaleY.get();
 440     }
 441     public final DoubleProperty renderScaleYProperty() {
 442         return renderScaleY;
 443     }
 444 
 445     private boolean xExplicit = false;
 446     /**
 447      * The horizontal location of this {@code Stage} on the screen. Changing
 448      * this attribute will move the {@code Stage} horizontally. Changing this
 449      * attribute will not visually affect a {@code Stage} while
 450      * {@code fullScreen} is true, but will be honored by the {@code Stage} once
 451      * {@code fullScreen} becomes false.
 452      */
 453     private ReadOnlyDoubleWrapper x =
 454             new ReadOnlyDoubleWrapper(this, "x", Double.NaN);
 455 
 456     public final void setX(double value) {
 457         setXInternal(value);
 458     }
 459     public final double getX() { return x.get(); }
 460     public final ReadOnlyDoubleProperty xProperty() { return x.getReadOnlyProperty(); }
 461 
 462     void setXInternal(double value) {
 463         x.set(value);
 464         peerBoundsConfigurator.setX(value, 0);


 978                 fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_HIDING));
 979             }
 980 
 981             oldVisible = newVisible;
 982             impl_visibleChanging(newVisible);
 983             if (newVisible) {
 984                 hasBeenVisible = true;
 985                 windows.add(Window.this);
 986             } else {
 987                 windows.remove(Window.this);
 988             }
 989             Toolkit tk = Toolkit.getToolkit();
 990             if (impl_peer != null) {
 991                 if (newVisible) {
 992                     if (peerListener == null) {
 993                         peerListener = new WindowPeerListener(Window.this);
 994                     }
 995 
 996                     // Setup listener for changes coming back from peer
 997                     impl_peer.setTKStageListener(peerListener);
 998                     updateOutputScales(impl_peer.getOutputScaleX(), impl_peer.getOutputScaleY());
 999                     // Register pulse listener
1000                     tk.addStageTkPulseListener(peerBoundsConfigurator);
1001 
1002                     if (getScene() != null) {
1003                         getScene().impl_initPeer();
1004                         impl_peer.setScene(getScene().impl_getPeer());
1005                         getScene().impl_preferredSize();
1006                     }
1007 
1008                     // Set peer bounds
1009                     if ((getScene() != null) && (!widthExplicit || !heightExplicit)) {
1010                         adjustSize(true);
1011                     } else {
1012                         peerBoundsConfigurator.setSize(
1013                                 getWidth(), getHeight(), -1, -1);
1014                     }
1015 
1016                     if (!xExplicit && !yExplicit) {
1017                         centerOnScreen();
1018                     } else {


1342             }
1343 
1344             window = window.getWindowOwner();
1345         } while (window != null);
1346 
1347         return Screen.getPrimary();
1348     }
1349 
1350     private final ReadOnlyObjectWrapper<Screen> screen = new ReadOnlyObjectWrapper<>(Screen.getPrimary());
1351     private ReadOnlyObjectProperty<Screen> screenProperty() { return screen.getReadOnlyProperty(); }
1352 
1353     private void notifyScreenChanged(Object from, Object to) {
1354         screen.set(Screen.getScreenForNative(to));
1355     }
1356 
1357     /**
1358      * Caches all requested bounds settings and applies them at once during
1359      * the next pulse.
1360      */
1361     private final class TKBoundsConfigurator implements TKPulseListener {
1362         private double renderScaleX;
1363         private double renderScaleY;
1364         private double x;
1365         private double y;
1366         private float xGravity;
1367         private float yGravity;
1368         private double windowWidth;
1369         private double windowHeight;
1370         private double clientWidth;
1371         private double clientHeight;
1372 
1373         private boolean dirty;
1374 
1375         public TKBoundsConfigurator() {
1376             reset();
1377         }
1378 
1379         public void setRenderScaleX(final double renderScaleX) {
1380             this.renderScaleX = renderScaleX;
1381             setDirty();
1382         }
1383 
1384         public void setRenderScaleY(final double renderScaleY) {
1385             this.renderScaleY = renderScaleY;
1386             setDirty();
1387         }
1388 
1389         public void setX(final double x, final float xGravity) {
1390             this.x = x;
1391             this.xGravity = xGravity;
1392             setDirty();
1393         }
1394 
1395         public void setY(final double y, final float yGravity) {
1396             this.y = y;
1397             this.yGravity = yGravity;
1398             setDirty();
1399         }
1400 
1401         public void setWindowWidth(final double windowWidth) {
1402             this.windowWidth = windowWidth;
1403             setDirty();
1404         }
1405 
1406         public void setWindowHeight(final double windowHeight) {
1407             this.windowHeight = windowHeight;
1408             setDirty();


1425             this.x = x;
1426             this.y = y;
1427             this.xGravity = xGravity;
1428             this.yGravity = yGravity;
1429             setDirty();
1430         }
1431 
1432         public void setSize(final double windowWidth,
1433                             final double windowHeight,
1434                             final double clientWidth,
1435                             final double clientHeight) {
1436             this.windowWidth = windowWidth;
1437             this.windowHeight = windowHeight;
1438             this.clientWidth = clientWidth;
1439             this.clientHeight = clientHeight;
1440             setDirty();
1441         }
1442 
1443         public void apply() {
1444             if (dirty) {
1445                 // Snapshot values and then reset() before we call down
1446                 // as we may end up with recursive calls back up with
1447                 // new values that must be recorded as dirty.
1448                 boolean xSet = !Double.isNaN(x);
1449                 float newX = xSet ? (float) x : 0f;
1450                 boolean ySet = !Double.isNaN(y);
1451                 float newY = ySet ? (float) y : 0f;
1452                 float newWW = (float) windowWidth;
1453                 float newWH = (float) windowHeight;
1454                 float newCW = (float) clientWidth;
1455                 float newCH = (float) clientHeight;
1456                 float newXG = xGravity;
1457                 float newYG = yGravity;
1458                 float newRX = (float) renderScaleX;
1459                 float newRY = (float) renderScaleY;
1460                 reset();
1461                 impl_peer.setBounds(newX, newY, xSet, ySet,
1462                                     newWW, newWH, newCW, newCH,
1463                                     newXG, newYG,
1464                                     newRX, newRY);
1465             }
1466         }
1467 
1468         @Override
1469         public void pulse() {
1470             apply();
1471         }
1472 
1473         private void reset() {
1474             renderScaleX = 0.0;
1475             renderScaleY = 0.0;
1476             x = Double.NaN;
1477             y = Double.NaN;
1478             xGravity = 0;
1479             yGravity = 0;
1480             windowWidth = -1;
1481             windowHeight = -1;
1482             clientWidth = -1;
1483             clientHeight = -1;
1484             dirty = false;
1485         }
1486 
1487         private void setDirty() {
1488             if (!dirty) {
1489                 Toolkit.getToolkit().requestNextPulse();
1490                 dirty = true;
1491             }
1492         }
1493     }
1494 }