1 /*
   2  * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  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.AccessControlContext;
  29 import java.security.AccessController;
  30 import java.util.HashMap;
  31 
  32 import javafx.beans.property.DoubleProperty;
  33 import javafx.beans.property.DoublePropertyBase;
  34 import javafx.beans.property.ObjectProperty;
  35 import javafx.beans.property.ObjectPropertyBase;
  36 import javafx.beans.property.ReadOnlyBooleanProperty;
  37 import javafx.beans.property.ReadOnlyBooleanWrapper;
  38 import javafx.beans.property.ReadOnlyObjectProperty;
  39 import javafx.beans.property.ReadOnlyObjectWrapper;
  40 import javafx.beans.property.ReadOnlyDoubleProperty;
  41 import javafx.beans.property.ReadOnlyDoubleWrapper;
  42 import javafx.beans.property.SimpleObjectProperty;
  43 import javafx.beans.property.SimpleDoubleProperty;
  44 import javafx.collections.FXCollections;
  45 import javafx.collections.ObservableList;
  46 import javafx.collections.ObservableMap;
  47 import javafx.event.Event;
  48 import javafx.event.EventDispatchChain;
  49 import javafx.event.EventDispatcher;
  50 import javafx.event.EventHandler;
  51 import javafx.event.EventTarget;
  52 import javafx.event.EventType;
  53 import javafx.geometry.Rectangle2D;
  54 import javafx.scene.Scene;
  55 
  56 import com.sun.javafx.util.Utils;
  57 import com.sun.javafx.css.StyleManager;
  58 import com.sun.javafx.stage.WindowEventDispatcher;
  59 import com.sun.javafx.stage.WindowHelper;
  60 import com.sun.javafx.stage.WindowPeerListener;
  61 import com.sun.javafx.tk.TKPulseListener;
  62 import com.sun.javafx.tk.TKScene;
  63 import com.sun.javafx.tk.TKStage;
  64 import com.sun.javafx.tk.Toolkit;
  65 import javafx.beans.property.BooleanProperty;
  66 import javafx.beans.property.SimpleBooleanProperty;
  67 
  68 import static com.sun.javafx.FXPermissions.ACCESS_WINDOW_LIST_PERMISSION;
  69 import com.sun.javafx.scene.NodeHelper;
  70 import com.sun.javafx.scene.SceneHelper;
  71 
  72 
  73 /**
  74  * <p>
  75  *     A top level window within which a scene is hosted, and with which the user
  76  *     interacts. A Window might be a {@link Stage}, {@link PopupWindow}, or other
  77  *     such top level. A Window is used also for browser plug-in based deployments.
  78  * </p>
  79  *
  80  * @since JavaFX 2.0
  81  */
  82 public class Window implements EventTarget {
  83 
  84     /**
  85      * A list of all the currently _showing_ windows. This is publicly accessible via the unmodifiableWindows wrapper.
  86      */
  87     private static ObservableList<Window> windows = FXCollections.observableArrayList();
  88     private static ObservableList<Window> unmodifiableWindows = FXCollections.unmodifiableObservableList(windows);
  89 
  90     /*
  91      * Store the singleton instance of the WindowHelper subclass corresponding
  92      * to the subclass of this instance of Window
  93      */
  94     private WindowHelper windowHelper = null;
  95 
  96     static {
  97         WindowHelper.setWindowAccessor(
  98                 new WindowHelper.WindowAccessor() {
  99                     @Override
 100                     public WindowHelper getHelper(Window window) {
 101                         return window.windowHelper;
 102                     }
 103 
 104                     @Override
 105                     public void setHelper(Window window, WindowHelper windowHelper) {
 106                         window.windowHelper = windowHelper;
 107                     }
 108 
 109                     @Override
 110                     public String doGetMXWindowType(Window window) {
 111                         return window.doGetMXWindowType();
 112                     }
 113 
 114                     @Override
 115                     public void doVisibleChanging(Window window, boolean visible) {
 116                         window.doVisibleChanging(visible);
 117                     }
 118 
 119                     @Override
 120                     public void doVisibleChanged(Window window, boolean visible) {
 121                         window.doVisibleChanged(visible);
 122                     }
 123 
 124                     @Override
 125                     public TKStage getPeer(Window window) {
 126                         return window.getPeer();
 127                     }
 128 
 129                     @Override
 130                     public void setPeer(Window window, TKStage peer) {
 131                         window.setPeer(peer);
 132                     }
 133 
 134                     @Override
 135                     public WindowPeerListener getPeerListener(Window window) {
 136                         return window.getPeerListener();
 137                     }
 138 
 139                     @Override
 140                     public void  setPeerListener(Window window, WindowPeerListener peerListener) {
 141                         window.setPeerListener(peerListener);
 142                     }
 143 
 144                     @Override
 145                     public void setFocused(Window window, boolean value) {
 146                         window.setFocused(value);
 147                     }
 148 
 149                     /*
 150                      * Allow window peer listeners to directly change reported
 151                      * window location and size without changing the xExplicit,
 152                      * yExplicit, widthExplicit and heightExplicit values.
 153                      */
 154                     @Override
 155                     public void notifyLocationChanged(
 156                             Window window, double x, double y) {
 157                         window.notifyLocationChanged(x, y);
 158                     }
 159 
 160                     @Override
 161                     public void notifySizeChanged(Window window,
 162                                                   double width,
 163                                                   double height) {
 164                         window.notifySizeChanged(width, height);
 165                     }
 166 
 167                     @Override
 168                     public void notifyScaleChanged(Window window,
 169                                                    double newOutputScaleX,
 170                                                    double newOutputScaleY) {
 171                         window.updateOutputScales(newOutputScaleX, newOutputScaleY);
 172                     }
 173 
 174                     @Override
 175                     public void notifyScreenChanged(Window window,
 176                                                   Object from,
 177                                                   Object to) {
 178                         window.notifyScreenChanged(from, to);
 179                     }
 180 
 181                     @Override
 182                     public float getPlatformScaleX(Window window) {
 183                         TKStage peer = window.getPeer();
 184                         return peer == null ? 1.0f : peer.getPlatformScaleX();
 185                     }
 186 
 187                     @Override
 188                     public float getPlatformScaleY(Window window) {
 189                         TKStage peer = window.getPeer();
 190                         return peer == null ? 1.0f : peer.getPlatformScaleY();
 191                     }
 192 
 193                     @Override
 194                     public ReadOnlyObjectProperty<Screen> screenProperty(Window window) {
 195                         return window.screenProperty();
 196                     }
 197 
 198                     @Override
 199                     public AccessControlContext getAccessControlContext(Window window) {
 200                         return window.acc;
 201                     }
 202                 });
 203     }
 204 
 205     /**
 206      * Returns a list containing a reference to the currently showing JavaFX windows. The list is unmodifiable -
 207      * attempting to modify this list will result in an {@link UnsupportedOperationException} being thrown at runtime.
 208      *
 209      * @return A list containing all windows that are currently showing.
 210      * @since 9
 211      */
 212     public static ObservableList<Window> getWindows() {
 213         final SecurityManager securityManager = System.getSecurityManager();
 214         if (securityManager != null) {
 215             securityManager.checkPermission(ACCESS_WINDOW_LIST_PERMISSION);
 216         }
 217 
 218         return unmodifiableWindows;
 219     }
 220 
 221     final AccessControlContext acc = AccessController.getContext();
 222 
 223     protected Window() {
 224         // necessary for WindowCloseRequestHandler
 225         initializeInternalEventDispatcher();
 226         WindowHelper.initHelper(this);
 227     }
 228 
 229     /*
 230      * The listener that gets called by peer. It's also responsible for
 231      * window size/location synchronization with the window peer, which
 232      * occurs on every pulse.
 233      */
 234     private WindowPeerListener peerListener;
 235 
 236     WindowPeerListener getPeerListener() {
 237         return peerListener;
 238     }
 239 
 240     void setPeerListener(WindowPeerListener peerListener) {
 241         this.peerListener = peerListener;
 242     }
 243 
 244     /*
 245      * The peer of this Stage. All external access should be
 246      * made though getPeer(). Implementors note: Please ensure that this
 247      * variable is defined *after* style and *before* the other variables so
 248      * that style has been initialized prior to this call, and so that
 249      * peer is initialized prior to subsequent initialization.
 250      */
 251     private TKStage peer;
 252 
 253     private TKBoundsConfigurator peerBoundsConfigurator =
 254             new TKBoundsConfigurator();
 255 
 256     /*
 257      * Get Stage's peer
 258      */
 259     TKStage getPeer() {
 260         return peer;
 261     }
 262 
 263     void setPeer(TKStage peer) {
 264         this.peer = peer;
 265     }
 266 
 267     /*
 268      * Note: This method MUST only be called via its accessor method.
 269      */
 270     private String doGetMXWindowType() {
 271         return getClass().getSimpleName();
 272     }
 273 
 274     /**
 275      * Indicates if a user requested the window to be sized to match the scene
 276      * size.
 277      */
 278     private boolean sizeToScene = false;
 279     /**
 280      * Set the width and height of this Window to match the size of the content
 281      * of this Window's Scene.
 282      */
 283     public void sizeToScene() {
 284         if (getScene() != null && peer != null) {
 285             SceneHelper.preferredSize(getScene());
 286             adjustSize(false);
 287         } else {
 288             // Remember the request to reapply it later if needed
 289             sizeToScene = true;
 290         }
 291     }
 292 
 293     private void adjustSize(boolean selfSizePriority) {
 294         if (getScene() == null) {
 295             return;
 296         }
 297         if (peer != null) {
 298             double sceneWidth = getScene().getWidth();
 299             double cw = (sceneWidth > 0) ? sceneWidth : -1;
 300             double w = -1;
 301             if (selfSizePriority && widthExplicit) {
 302                 w = getWidth();
 303             } else if (cw <= 0) {
 304                 w = widthExplicit ? getWidth() : -1;
 305             } else {
 306                 widthExplicit = false;
 307             }
 308             double sceneHeight = getScene().getHeight();
 309             double ch = (sceneHeight > 0) ? sceneHeight : -1;
 310             double h = -1;
 311             if (selfSizePriority && heightExplicit) {
 312                 h = getHeight();
 313             } else if (ch <= 0) {
 314                 h = heightExplicit ? getHeight() : -1;
 315             } else {
 316                 heightExplicit = false;
 317             }
 318 
 319             peerBoundsConfigurator.setSize(w, h, cw, ch);
 320             applyBounds();
 321         }
 322     }
 323 
 324     private static final float CENTER_ON_SCREEN_X_FRACTION = 1.0f / 2;
 325     private static final float CENTER_ON_SCREEN_Y_FRACTION = 1.0f / 3;
 326 
 327     /**
 328      * Sets x and y properties on this Window so that it is centered on the
 329      * curent screen.
 330      * The current screen is determined from the intersection of current window bounds and
 331      * visual bounds of all screens.
 332      */
 333     public void centerOnScreen() {
 334         xExplicit = false;
 335         yExplicit = false;
 336         if (peer != null) {
 337             Rectangle2D bounds = getWindowScreen().getVisualBounds();
 338             double centerX =
 339                     bounds.getMinX() + (bounds.getWidth() - getWidth())
 340                                            * CENTER_ON_SCREEN_X_FRACTION;
 341             double centerY =
 342                     bounds.getMinY() + (bounds.getHeight() - getHeight())
 343                                            * CENTER_ON_SCREEN_Y_FRACTION;
 344 
 345             x.set(centerX);
 346             y.set(centerY);
 347             peerBoundsConfigurator.setLocation(centerX, centerY,
 348                                                CENTER_ON_SCREEN_X_FRACTION,
 349                                                CENTER_ON_SCREEN_Y_FRACTION);
 350             applyBounds();
 351         }
 352     }
 353 
 354     private void updateOutputScales(double sx, double sy) {
 355         // We call updateRenderScales() before updating the property
 356         // values so that an application can listen to the properties
 357         // and set their own values overriding the default values we set.
 358         updateRenderScales(sx, sy);
 359         // Now set the properties and trigger any potential listeners.
 360         outputScaleX.set(sx);
 361         outputScaleY.set(sy);
 362     }
 363 
 364     void updateRenderScales(double sx, double sy) {
 365         boolean forceInt = forceIntegerRenderScale.get();
 366         if (!renderScaleX.isBound()) {
 367             renderScaleX.set(forceInt ? Math.ceil(sx) : sx);
 368         }
 369         if (!renderScaleY.isBound()) {
 370             renderScaleY.set(forceInt ? Math.ceil(sy) : sy);
 371         }
 372     }
 373 
 374     /**
 375      * The scale that the {@code Window} will apply to horizontal scene
 376      * coordinates in all stages of rendering and compositing the output
 377      * to the screen or other destination device.
 378      * This property is updated asynchronously by the system at various
 379      * times including:
 380      * <ul>
 381      * <li>Window creation
 382      * <li>At some point during moving a window to a new {@code Screen}
 383      * which may be before or after the {@link Screen} property is updated.
 384      * <li>In response to a change in user preferences for output scaling.
 385      * </ul>
 386      *
 387      * @see #renderScaleXProperty()
 388      * @since 9
 389      */
 390     private ReadOnlyDoubleWrapper outputScaleX =
 391         new ReadOnlyDoubleWrapper(this, "outputScaleX", 1.0);
 392     public final double getOutputScaleX() {
 393         return outputScaleX.get();
 394     }
 395     public final ReadOnlyDoubleProperty outputScaleXProperty() {
 396         return outputScaleX.getReadOnlyProperty();
 397     }
 398 
 399     /**
 400      * The scale that the {@code Window} will apply to vertical scene
 401      * coordinates in all stages of rendering and compositing the output
 402      * to the screen or other destination device.
 403      * This property is updated asynchronously by the system at various
 404      * times including:
 405      * <ul>
 406      * <li>Window creation
 407      * <li>At some point during moving a window to a new {@code Screen}
 408      * which may be before or after the {@link Screen} property is updated.
 409      * <li>In response to a change in user preferences for output scaling.
 410      * </ul>
 411      *
 412      * @see #renderScaleYProperty()
 413      * @since 9
 414      */
 415     private ReadOnlyDoubleWrapper outputScaleY =
 416         new ReadOnlyDoubleWrapper(this, "outputScaleY", 1.0);
 417     public final double getOutputScaleY() {
 418         return outputScaleY.get();
 419     }
 420     public final ReadOnlyDoubleProperty outputScaleYProperty() {
 421         return outputScaleY.getReadOnlyProperty();
 422     }
 423 
 424     /**
 425      * Boolean property that controls whether only integer render scales
 426      * are set by default by the system when there is a change in the
 427      * associated output scale.
 428      * The {@code renderScale} properties will be updated directly and
 429      * simultaneously with any changes in the associated {@code outputScale}
 430      * properties, but the values can be overridden by subsequent calls to
 431      * the {@code setRenderScale} setters or through appropriate use of
 432      * binding.
 433      * This property will not prevent setting non-integer scales
 434      * directly using the {@code renderScale} property object or the
 435      * convenience setter method.
 436      *
 437      * @defaultValue false
 438      * @see #renderScaleXProperty()
 439      * @see #renderScaleYProperty()
 440      * @since 9
 441      */
 442     private BooleanProperty forceIntegerRenderScale =
 443         new SimpleBooleanProperty(this, "forceIntegerRenderScale", false) {
 444             @Override
 445             protected void invalidated() {
 446                 updateRenderScales(getOutputScaleX(),
 447                                    getOutputScaleY());
 448             }
 449         };
 450     public final void setForceIntegerRenderScale(boolean forced) {
 451         forceIntegerRenderScale.set(forced);
 452     }
 453     public final boolean isForceIntegerRenderScale() {
 454         return forceIntegerRenderScale.get();
 455     }
 456     public final BooleanProperty forceIntegerRenderScaleProperty() {
 457         return forceIntegerRenderScale;
 458     }
 459 
 460     /**
 461      * The horizontal scale that the {@code Window} will use when rendering
 462      * its {@code Scene} to the rendering buffer.
 463      * This property is automatically updated whenever there is a change in
 464      * the {@link outputScaleX} property and can be overridden either by
 465      * calling {@code setRenderScaleX()} in response to a listener on the
 466      * {@code outputScaleX} property or by binding it appropriately.
 467      *
 468      * @defaultValue outputScaleX
 469      * @see #outputScaleXProperty()
 470      * @see #forceIntegerRenderScaleProperty()
 471      * @since 9
 472      */
 473     private DoubleProperty renderScaleX =
 474         new SimpleDoubleProperty(this, "renderScaleX", 1.0) {
 475             @Override
 476             protected void invalidated() {
 477                 peerBoundsConfigurator.setRenderScaleX(get());
 478             }
 479         };
 480     public final void setRenderScaleX(double scale) {
 481         renderScaleX.set(scale);
 482     }
 483     public final double getRenderScaleX() {
 484         return renderScaleX.get();
 485     }
 486     public final DoubleProperty renderScaleXProperty() {
 487         return renderScaleX;
 488     }
 489 
 490     /**
 491      * The vertical scale that the {@code Window} will use when rendering
 492      * its {@code Scene} to the rendering buffer.
 493      * This property is automatically updated whenever there is a change in
 494      * the {@link outputScaleY} property and can be overridden either by
 495      * calling {@code setRenderScaleY()} in response to a listener on the
 496      * {@code outputScaleY} property or by binding it appropriately.
 497      *
 498      * @defaultValue outputScaleY
 499      * @see #outputScaleYProperty()
 500      * @see #forceIntegerRenderScaleProperty()
 501      * @since 9
 502      */
 503     private DoubleProperty renderScaleY =
 504         new SimpleDoubleProperty(this, "renderScaleY", 1.0) {
 505             @Override
 506             protected void invalidated() {
 507                 peerBoundsConfigurator.setRenderScaleY(get());
 508             }
 509         };
 510     public final void setRenderScaleY(double scale) {
 511         renderScaleY.set(scale);
 512     }
 513     public final double getRenderScaleY() {
 514         return renderScaleY.get();
 515     }
 516     public final DoubleProperty renderScaleYProperty() {
 517         return renderScaleY;
 518     }
 519 
 520     private boolean xExplicit = false;
 521     /**
 522      * The horizontal location of this {@code Stage} on the screen. Changing
 523      * this attribute will move the {@code Stage} horizontally. Changing this
 524      * attribute will not visually affect a {@code Stage} while
 525      * {@code fullScreen} is true, but will be honored by the {@code Stage} once
 526      * {@code fullScreen} becomes false.
 527      */
 528     private ReadOnlyDoubleWrapper x =
 529             new ReadOnlyDoubleWrapper(this, "x", Double.NaN);
 530 
 531     public final void setX(double value) {
 532         setXInternal(value);
 533     }
 534     public final double getX() { return x.get(); }
 535     public final ReadOnlyDoubleProperty xProperty() { return x.getReadOnlyProperty(); }
 536 
 537     void setXInternal(double value) {
 538         x.set(value);
 539         peerBoundsConfigurator.setX(value, 0);
 540         xExplicit = true;
 541     }
 542 
 543     private boolean yExplicit = false;
 544     /**
 545      * The vertical location of this {@code Stage} on the screen. Changing this
 546      * attribute will move the {@code Stage} vertically. Changing this
 547      * attribute will not visually affect a {@code Stage} while
 548      * {@code fullScreen} is true, but will be honored by the {@code Stage} once
 549      * {@code fullScreen} becomes false.
 550      */
 551     private ReadOnlyDoubleWrapper y =
 552             new ReadOnlyDoubleWrapper(this, "y", Double.NaN);
 553 
 554     public final void setY(double value) {
 555         setYInternal(value);
 556     }
 557     public final double getY() { return y.get(); }
 558     public final ReadOnlyDoubleProperty yProperty() { return y.getReadOnlyProperty(); }
 559 
 560     void setYInternal(double value) {
 561         y.set(value);
 562         peerBoundsConfigurator.setY(value, 0);
 563         yExplicit = true;
 564     }
 565 
 566     /**
 567      * Notification from the windowing system that the window's position has
 568      * changed.
 569      *
 570      * @param newX the new window x position
 571      * @param newY the new window y position
 572      */
 573     void notifyLocationChanged(double newX, double newY) {
 574         x.set(newX);
 575         y.set(newY);
 576     }
 577 
 578     private boolean widthExplicit = false;
 579 
 580     /**
 581      * The width of this {@code Window}. Changing this attribute will narrow or
 582      * widen the width of the {@code Window}. Changing this
 583      * attribute will not visually affect a {@code Window} while
 584      * {@code fullScreen} is true, but will be honored by the {@code Window} once
 585      * {@code fullScreen} becomes false. This value includes any and all
 586      * decorations which may be added by the Operating System such as resizable
 587      * frame handles. Typical applications will set the {@link javafx.scene.Scene}
 588      * width instead. This window will take its width from the scene if it has
 589      * never been set by the application. Resizing the window by end user does
 590      * not count as a setting the width by the application.
 591      * <p>
 592      * The property is read only because it can be changed externally
 593      * by the underlying platform and therefore must not be bindable.
 594      * </p>
 595      */
 596     private ReadOnlyDoubleWrapper width =
 597             new ReadOnlyDoubleWrapper(this, "width", Double.NaN);
 598 
 599     public final void setWidth(double value) {
 600         width.set(value);
 601         peerBoundsConfigurator.setWindowWidth(value);
 602         widthExplicit = true;
 603     }
 604     public final double getWidth() { return width.get(); }
 605     public final ReadOnlyDoubleProperty widthProperty() { return width.getReadOnlyProperty(); }
 606 
 607     private boolean heightExplicit = false;
 608     /**
 609      * The height of this {@code Window}. Changing this attribute will shrink
 610      * or heighten the height of the {@code Window}. Changing this
 611      * attribute will not visually affect a {@code Window} while
 612      * {@code fullScreen} is true, but will be honored by the {@code Window} once
 613      * {@code fullScreen} becomes false. This value includes any and all
 614      * decorations which may be added by the Operating System such as the title
 615      * bar. Typical applications will set the {@link javafx.scene.Scene} height
 616      * instead. This window will take its height from the scene if it has never
 617      * been set by the application. Resizing this window by end user does not
 618      * count as a setting the height by the application.
 619      * <p>
 620      * The property is read only because it can be changed externally
 621      * by the underlying platform and therefore must not be bindable.
 622      * </p>
 623      */
 624     private ReadOnlyDoubleWrapper height =
 625             new ReadOnlyDoubleWrapper(this, "height", Double.NaN);
 626 
 627     public final void setHeight(double value) {
 628         height.set(value);
 629         peerBoundsConfigurator.setWindowHeight(value);
 630         heightExplicit = true;
 631     }
 632     public final double getHeight() { return height.get(); }
 633     public final ReadOnlyDoubleProperty heightProperty() { return height.getReadOnlyProperty(); }
 634 
 635     /**
 636      * Notification from the windowing system that the window's size has
 637      * changed.
 638      *
 639      * @param newWidth the new window width
 640      * @param newHeight the new window height
 641      */
 642     void notifySizeChanged(double newWidth, double newHeight) {
 643         width.set(newWidth);
 644         height.set(newHeight);
 645     }
 646 
 647     /**
 648      * Whether or not this {@code Window} has the keyboard or input focus.
 649      * <p>
 650      * The property is read only because it can be changed externally
 651      * by the underlying platform and therefore must not be bindable.
 652      * </p>
 653      *
 654      * @profile common
 655      */
 656     private ReadOnlyBooleanWrapper focused = new ReadOnlyBooleanWrapper() {
 657         @Override protected void invalidated() {
 658             focusChanged(get());
 659         }
 660 
 661         @Override
 662         public Object getBean() {
 663             return Window.this;
 664         }
 665 
 666         @Override
 667         public String getName() {
 668             return "focused";
 669         }
 670     };
 671 
 672     final void setFocused(boolean value) { focused.set(value); }
 673 
 674     /**
 675      * Requests that this {@code Window} get the input focus.
 676      */
 677     public final void requestFocus() {
 678         if (peer != null) {
 679             peer.requestFocus();
 680         }
 681     }
 682     public final boolean isFocused() { return focused.get(); }
 683     public final ReadOnlyBooleanProperty focusedProperty() { return focused.getReadOnlyProperty(); }
 684 
 685     /*************************************************************************
 686     *                                                                        *
 687     *                                                                        *
 688     *                                                                        *
 689     *************************************************************************/
 690 
 691     private static final Object USER_DATA_KEY = new Object();
 692     // A map containing a set of properties for this window
 693     private ObservableMap<Object, Object> properties;
 694 
 695     /**
 696       * Returns an observable map of properties on this node for use primarily
 697       * by application developers.
 698       *
 699       * @return an observable map of properties on this node for use primarily
 700       * by application developers
 701       *
 702       * @since JavaFX 8u40
 703       */
 704      public final ObservableMap<Object, Object> getProperties() {
 705         if (properties == null) {
 706             properties = FXCollections.observableMap(new HashMap<Object, Object>());
 707         }
 708         return properties;
 709     }
 710 
 711     /**
 712      * Tests if Window has properties.
 713      * @return true if node has properties.
 714      *
 715      * @since JavaFX 8u40
 716      */
 717      public boolean hasProperties() {
 718         return properties != null && !properties.isEmpty();
 719     }
 720 
 721     /**
 722      * Convenience method for setting a single Object property that can be
 723      * retrieved at a later date. This is functionally equivalent to calling
 724      * the getProperties().put(Object key, Object value) method. This can later
 725      * be retrieved by calling {@link Window#getUserData()}.
 726      *
 727      * @param value The value to be stored - this can later be retrieved by calling
 728      *          {@link Window#getUserData()}.
 729      *
 730      * @since JavaFX 8u40
 731      */
 732     public void setUserData(Object value) {
 733         getProperties().put(USER_DATA_KEY, value);
 734     }
 735 
 736     /**
 737      * Returns a previously set Object property, or null if no such property
 738      * has been set using the {@link Window#setUserData(java.lang.Object)} method.
 739      *
 740      * @return The Object that was previously set, or null if no property
 741      *          has been set or if null was set.
 742      *
 743      * @since JavaFX 8u40
 744      */
 745     public Object getUserData() {
 746         return getProperties().get(USER_DATA_KEY);
 747     }
 748 
 749     /**
 750      * The {@code Scene} to be rendered on this {@code Window}. There can only
 751      * be one {@code Scene} on the {@code Window} at a time, and a {@code Scene}
 752      * can only be on one {@code Window} at a time. Setting a {@code Scene} on
 753      * a different {@code Window} will cause the old {@code Window} to lose the
 754      * reference before the new one gains it. You may swap {@code Scene}s on
 755      * a {@code Window} at any time, even while in full-screen exclusive mode.
 756      * If the width or height of this {@code Window} have never been set by the
 757      * application, setting the scene will cause this {@code Window} to take its
 758      * width or height from that scene.  Resizing this window by end user does
 759      * not count as setting the width or height by the application.
 760      *
 761      * An {@link IllegalStateException} is thrown if this property is set
 762      * on a thread other than the JavaFX Application Thread.
 763      *
 764      * @defaultValue null
 765      */
 766     private SceneModel scene = new SceneModel();
 767     protected void setScene(Scene value) { scene.set(value); }
 768     public final Scene getScene() { return scene.get(); }
 769     public final ReadOnlyObjectProperty<Scene> sceneProperty() { return scene.getReadOnlyProperty(); }
 770 
 771     private final class SceneModel extends ReadOnlyObjectWrapper<Scene> {
 772         private Scene oldScene;
 773 
 774         @Override protected void invalidated() {
 775             final Scene newScene = get();
 776             if (oldScene == newScene) {
 777                 return;
 778             }
 779             if (peer != null) {
 780                 Toolkit.getToolkit().checkFxUserThread();
 781             }
 782             // First, detach scene peer from this window
 783             updatePeerScene(null);
 784             // Second, dispose scene peer
 785             if (oldScene != null) {
 786                 SceneHelper.setWindow(oldScene, null);
 787                 StyleManager.getInstance().forget(oldScene);
 788             }
 789             if (newScene != null) {
 790                 final Window oldWindow = newScene.getWindow();
 791                 if (oldWindow != null) {
 792                     // if the new scene was previously set to a window
 793                     // we need to remove it from that window
 794                     // NOTE: can this "scene" property be bound?
 795                     oldWindow.setScene(null);
 796                 }
 797 
 798                 // Set the "window" on the new scene. This will also trigger
 799                 // scene's peer creation.
 800                 SceneHelper.setWindow(newScene, Window.this);
 801                 // Set scene impl on stage impl
 802                 updatePeerScene(SceneHelper.getPeer(newScene));
 803 
 804                 // Fix for RT-15432: we should update new Scene's stylesheets, if the
 805                 // window is already showing. For not yet shown windows, the update is
 806                 // performed in doVisibleChanging()
 807                 if (isShowing()) {
 808                     NodeHelper.reapplyCSS(newScene.getRoot());
 809 
 810                     if (!widthExplicit || !heightExplicit) {
 811                         SceneHelper.preferredSize(getScene());
 812                         adjustSize(true);
 813                     }
 814                 }
 815             }
 816 
 817             oldScene = newScene;
 818         }
 819 
 820         @Override
 821         public Object getBean() {
 822             return Window.this;
 823         }
 824 
 825         @Override
 826         public String getName() {
 827             return "scene";
 828         }
 829 
 830         private void updatePeerScene(final TKScene tkScene) {
 831             if (peer != null) {
 832                 // Set scene impl on stage impl
 833                 peer.setScene(tkScene);
 834             }
 835         }
 836     }
 837 
 838     /**
 839      * Defines the opacity of the {@code Stage} as a value between 0.0 and 1.0.
 840      * The opacity is reflected across the {@code Stage}, its {@code Decoration}
 841      * and its {@code Scene} content. On a JavaFX runtime platform that does not
 842      * support opacity, assigning a value to this variable will have no
 843      * visible difference. A {@code Stage} with 0% opacity is fully translucent.
 844      * Typically, a {@code Stage} with 0% opacity will not receive any mouse
 845      * events.
 846      *
 847      * @defaultValue 1.0
 848      */
 849     private DoubleProperty opacity;
 850 
 851     public final void setOpacity(double value) {
 852         opacityProperty().set(value);
 853     }
 854 
 855     public final double getOpacity() {
 856         return opacity == null ? 1.0 : opacity.get();
 857     }
 858 
 859     public final DoubleProperty opacityProperty() {
 860         if (opacity == null) {
 861             opacity = new DoublePropertyBase(1.0) {
 862 
 863                 @Override
 864                 protected void invalidated() {
 865                     if (peer != null) {
 866                         peer.setOpacity((float) get());
 867                     }
 868                 }
 869 
 870                 @Override
 871                 public Object getBean() {
 872                     return Window.this;
 873                 }
 874 
 875                 @Override
 876                 public String getName() {
 877                     return "opacity";
 878                 }
 879             };
 880         }
 881         return opacity;
 882     }
 883 
 884     /**
 885      * Called when there is an external request to close this {@code Window}.
 886      * The installed event handler can prevent window closing by consuming the
 887      * received event.
 888      */
 889     private ObjectProperty<EventHandler<WindowEvent>> onCloseRequest;
 890     public final void setOnCloseRequest(EventHandler<WindowEvent> value) {
 891         onCloseRequestProperty().set(value);
 892     }
 893     public final EventHandler<WindowEvent> getOnCloseRequest() {
 894         return (onCloseRequest != null) ? onCloseRequest.get() : null;
 895     }
 896     public final ObjectProperty<EventHandler<WindowEvent>>
 897             onCloseRequestProperty() {
 898         if (onCloseRequest == null) {
 899             onCloseRequest = new ObjectPropertyBase<EventHandler<WindowEvent>>() {
 900                 @Override protected void invalidated() {
 901                     setEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, get());
 902                 }
 903 
 904                 @Override
 905                 public Object getBean() {
 906                     return Window.this;
 907                 }
 908 
 909                 @Override
 910                 public String getName() {
 911                     return "onCloseRequest";
 912                 }
 913             };
 914         }
 915         return onCloseRequest;
 916     }
 917 
 918     /**
 919      * Called just prior to the Window being shown.
 920      */
 921     private ObjectProperty<EventHandler<WindowEvent>> onShowing;
 922     public final void setOnShowing(EventHandler<WindowEvent> value) { onShowingProperty().set(value); }
 923     public final EventHandler<WindowEvent> getOnShowing() {
 924         return onShowing == null ? null : onShowing.get();
 925     }
 926     public final ObjectProperty<EventHandler<WindowEvent>> onShowingProperty() {
 927         if (onShowing == null) {
 928             onShowing = new ObjectPropertyBase<EventHandler<WindowEvent>>() {
 929                 @Override protected void invalidated() {
 930                     setEventHandler(WindowEvent.WINDOW_SHOWING, get());
 931                 }
 932 
 933                 @Override
 934                 public Object getBean() {
 935                     return Window.this;
 936                 }
 937 
 938                 @Override
 939                 public String getName() {
 940                     return "onShowing";
 941                 }
 942             };
 943         }
 944         return onShowing;
 945     }
 946 
 947     /**
 948      * Called just after the Window is shown.
 949      */
 950     private ObjectProperty<EventHandler<WindowEvent>> onShown;
 951     public final void setOnShown(EventHandler<WindowEvent> value) { onShownProperty().set(value); }
 952     public final EventHandler<WindowEvent> getOnShown() {
 953         return onShown == null ? null : onShown.get();
 954     }
 955     public final ObjectProperty<EventHandler<WindowEvent>> onShownProperty() {
 956         if (onShown == null) {
 957             onShown = new ObjectPropertyBase<EventHandler<WindowEvent>>() {
 958                 @Override protected void invalidated() {
 959                     setEventHandler(WindowEvent.WINDOW_SHOWN, get());
 960                 }
 961 
 962                 @Override
 963                 public Object getBean() {
 964                     return Window.this;
 965                 }
 966 
 967                 @Override
 968                 public String getName() {
 969                     return "onShown";
 970                 }
 971             };
 972         }
 973         return onShown;
 974     }
 975 
 976     /**
 977      * Called just prior to the Window being hidden.
 978      */
 979     private ObjectProperty<EventHandler<WindowEvent>> onHiding;
 980     public final void setOnHiding(EventHandler<WindowEvent> value) { onHidingProperty().set(value); }
 981     public final EventHandler<WindowEvent> getOnHiding() {
 982         return onHiding == null ? null : onHiding.get();
 983     }
 984     public final ObjectProperty<EventHandler<WindowEvent>> onHidingProperty() {
 985         if (onHiding == null) {
 986             onHiding = new ObjectPropertyBase<EventHandler<WindowEvent>>() {
 987                 @Override protected void invalidated() {
 988                     setEventHandler(WindowEvent.WINDOW_HIDING, get());
 989                 }
 990 
 991                 @Override
 992                 public Object getBean() {
 993                     return Window.this;
 994                 }
 995 
 996                 @Override
 997                 public String getName() {
 998                     return "onHiding";
 999                 }
1000             };
1001         }
1002         return onHiding;
1003     }
1004 
1005     /**
1006      * Called just after the Window has been hidden.
1007      * When the {@code Window} is hidden, this event handler is invoked allowing
1008      * the developer to clean up resources or perform other tasks when the
1009      * {@link Window} is closed.
1010      */
1011     private ObjectProperty<EventHandler<WindowEvent>> onHidden;
1012     public final void setOnHidden(EventHandler<WindowEvent> value) { onHiddenProperty().set(value); }
1013     public final EventHandler<WindowEvent> getOnHidden() {
1014         return onHidden == null ? null : onHidden.get();
1015     }
1016     public final ObjectProperty<EventHandler<WindowEvent>> onHiddenProperty() {
1017         if (onHidden == null) {
1018             onHidden = new ObjectPropertyBase<EventHandler<WindowEvent>>() {
1019                 @Override protected void invalidated() {
1020                     setEventHandler(WindowEvent.WINDOW_HIDDEN, get());
1021                 }
1022 
1023                 @Override
1024                 public Object getBean() {
1025                     return Window.this;
1026                 }
1027 
1028                 @Override
1029                 public String getName() {
1030                     return "onHidden";
1031                 }
1032             };
1033         }
1034         return onHidden;
1035     }
1036 
1037     /**
1038      * Whether or not this {@code Stage} is showing (that is, open on the
1039      * user's system). The Stage might be "showing", yet the user might not
1040      * be able to see it due to the Stage being rendered behind another window
1041      * or due to the Stage being positioned off the monitor.
1042      *
1043      * @defaultValue false
1044      */
1045     private ReadOnlyBooleanWrapper showing = new ReadOnlyBooleanWrapper() {
1046         private boolean oldVisible;
1047 
1048         @Override protected void invalidated() {
1049             final boolean newVisible = get();
1050             if (oldVisible == newVisible) {
1051                 return;
1052             }
1053 
1054             if (!oldVisible && newVisible) {
1055                 fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_SHOWING));
1056             } else {
1057                 fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_HIDING));
1058             }
1059 
1060             oldVisible = newVisible;
1061             WindowHelper.visibleChanging(Window.this, newVisible);
1062             if (newVisible) {
1063                 hasBeenVisible = true;
1064                 windows.add(Window.this);
1065             } else {
1066                 windows.remove(Window.this);
1067             }
1068             Toolkit tk = Toolkit.getToolkit();
1069             if (peer != null) {
1070                 if (newVisible) {
1071                     if (peerListener == null) {
1072                         peerListener = new WindowPeerListener(Window.this);
1073                     }
1074 
1075                     // Setup listener for changes coming back from peer
1076                     peer.setTKStageListener(peerListener);
1077                     // Register pulse listener
1078                     tk.addStageTkPulseListener(peerBoundsConfigurator);
1079 
1080                     if (getScene() != null) {
1081                         SceneHelper.initPeer(getScene());
1082                         peer.setScene(SceneHelper.getPeer(getScene()));
1083                         SceneHelper.preferredSize(getScene());
1084                     }
1085 
1086                     updateOutputScales(peer.getOutputScaleX(), peer.getOutputScaleY());
1087                     // updateOutputScales may cause an update to the render
1088                     // scales in many cases, but if the scale has not changed
1089                     // then the lazy render scale properties might think
1090                     // they do not need to send down the new values.  In some
1091                     // cases we have been show()n with a brand new peer, so
1092                     // it is better to force the render scales into the PBC.
1093                     // This may usually be a NOP, but it is similar to the
1094                     // forced setSize and setLocation down below.
1095                     peerBoundsConfigurator.setRenderScaleX(getRenderScaleX());
1096                     peerBoundsConfigurator.setRenderScaleY(getRenderScaleY());
1097 
1098                     // Set peer bounds
1099                     if ((getScene() != null) && (!widthExplicit || !heightExplicit)) {
1100                         adjustSize(true);
1101                     } else {
1102                         peerBoundsConfigurator.setSize(
1103                                 getWidth(), getHeight(), -1, -1);
1104                     }
1105 
1106                     if (!xExplicit && !yExplicit) {
1107                         centerOnScreen();
1108                     } else {
1109                         peerBoundsConfigurator.setLocation(getX(), getY(),
1110                                                            0, 0);
1111                     }
1112 
1113                     // set peer bounds before the window is shown
1114                     applyBounds();
1115 
1116                     peer.setOpacity((float)getOpacity());
1117 
1118                     peer.setVisible(true);
1119                     fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_SHOWN));
1120                 } else {
1121                     peer.setVisible(false);
1122 
1123                     // Call listener
1124                     fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_HIDDEN));
1125 
1126                     if (getScene() != null) {
1127                         peer.setScene(null);
1128                         SceneHelper.disposePeer(getScene());
1129                         StyleManager.getInstance().forget(getScene());
1130                     }
1131 
1132                     // Remove toolkit pulse listener
1133                     tk.removeStageTkPulseListener(peerBoundsConfigurator);
1134                     // Remove listener for changes coming back from peer
1135                     peer.setTKStageListener(null);
1136 
1137                     // Notify peer
1138                     peer.close();
1139                 }
1140             }
1141             if (newVisible) {
1142                 tk.requestNextPulse();
1143             }
1144             WindowHelper.visibleChanged(Window.this, newVisible);
1145 
1146             if (sizeToScene) {
1147                 if (newVisible) {
1148                     // Now that the visibleChanged has completed, the insets of the window
1149                     // might have changed (e.g. due to setResizable(false)). Reapply the
1150                     // sizeToScene() request if needed to account for the new insets.
1151                     sizeToScene();
1152                 }
1153 
1154                 // Reset the flag unconditionally upon visibility changes
1155                 sizeToScene = false;
1156             }
1157         }
1158 
1159         @Override
1160         public Object getBean() {
1161             return Window.this;
1162         }
1163 
1164         @Override
1165         public String getName() {
1166             return "showing";
1167         }
1168     };
1169     private void setShowing(boolean value) {
1170         Toolkit.getToolkit().checkFxUserThread();
1171         showing.set(value);
1172     }
1173     public final boolean isShowing() { return showing.get(); }
1174     public final ReadOnlyBooleanProperty showingProperty() { return showing.getReadOnlyProperty(); }
1175 
1176     // flag indicating whether this window has ever been made visible.
1177     boolean hasBeenVisible = false;
1178 
1179     /**
1180      * Attempts to show this Window by setting visibility to true
1181      *
1182      * @throws IllegalStateException if this method is called on a thread
1183      * other than the JavaFX Application Thread.
1184      */
1185     protected void show() {
1186         setShowing(true);
1187     }
1188 
1189     /**
1190      * Attempts to hide this Window by setting the visibility to false.
1191      *
1192      * @throws IllegalStateException if this method is called on a thread
1193      * other than the JavaFX Application Thread.
1194      */
1195     public void hide() {
1196         setShowing(false);
1197     }
1198 
1199     /*
1200      * This can be replaced by listening for the onShowing/onHiding events
1201      * Note: This method MUST only be called via its accessor method.
1202      */
1203     private void doVisibleChanging(boolean visible) {
1204         if (visible && (getScene() != null)) {
1205             NodeHelper.reapplyCSS(getScene().getRoot());
1206         }
1207     }
1208 
1209     /*
1210      * This can be replaced by listening for the onShown/onHidden events
1211      * Note: This method MUST only be called via its accessor method.
1212      */
1213     private void doVisibleChanged(boolean visible) {
1214         assert peer != null;
1215         if (!visible) {
1216             peerListener = null;
1217             peer = null;
1218         }
1219     }
1220 
1221     // PENDING_DOC_REVIEW
1222     /**
1223      * Specifies the event dispatcher for this node. The default event
1224      * dispatcher sends the received events to the registered event handlers and
1225      * filters. When replacing the value with a new {@code EventDispatcher},
1226      * the new dispatcher should forward events to the replaced dispatcher
1227      * to maintain the node's default event handling behavior.
1228      */
1229     private ObjectProperty<EventDispatcher> eventDispatcher;
1230 
1231     public final void setEventDispatcher(EventDispatcher value) {
1232         eventDispatcherProperty().set(value);
1233     }
1234 
1235     public final EventDispatcher getEventDispatcher() {
1236         return eventDispatcherProperty().get();
1237     }
1238 
1239     public final ObjectProperty<EventDispatcher> eventDispatcherProperty() {
1240         initializeInternalEventDispatcher();
1241         return eventDispatcher;
1242     }
1243 
1244     private WindowEventDispatcher internalEventDispatcher;
1245 
1246     // PENDING_DOC_REVIEW
1247     /**
1248      * Registers an event handler to this node. The handler is called when the
1249      * node receives an {@code Event} of the specified type during the bubbling
1250      * phase of event delivery.
1251      *
1252      * @param <T> the specific event class of the handler
1253      * @param eventType the type of the events to receive by the handler
1254      * @param eventHandler the handler to register
1255      * @throws NullPointerException if the event type or handler is null
1256      */
1257     public final <T extends Event> void addEventHandler(
1258             final EventType<T> eventType,
1259             final EventHandler<? super T> eventHandler) {
1260         getInternalEventDispatcher().getEventHandlerManager()
1261                                     .addEventHandler(eventType, eventHandler);
1262     }
1263 
1264     // PENDING_DOC_REVIEW
1265     /**
1266      * Unregisters a previously registered event handler from this node. One
1267      * handler might have been registered for different event types, so the
1268      * caller needs to specify the particular event type from which to
1269      * unregister the handler.
1270      *
1271      * @param <T> the specific event class of the handler
1272      * @param eventType the event type from which to unregister
1273      * @param eventHandler the handler to unregister
1274      * @throws NullPointerException if the event type or handler is null
1275      */
1276     public final <T extends Event> void removeEventHandler(
1277             final EventType<T> eventType,
1278             final EventHandler<? super T> eventHandler) {
1279         getInternalEventDispatcher().getEventHandlerManager()
1280                                     .removeEventHandler(eventType,
1281                                                         eventHandler);
1282     }
1283 
1284     // PENDING_DOC_REVIEW
1285     /**
1286      * Registers an event filter to this node. The filter is called when the
1287      * node receives an {@code Event} of the specified type during the capturing
1288      * phase of event delivery.
1289      *
1290      * @param <T> the specific event class of the filter
1291      * @param eventType the type of the events to receive by the filter
1292      * @param eventFilter the filter to register
1293      * @throws NullPointerException if the event type or filter is null
1294      */
1295     public final <T extends Event> void addEventFilter(
1296             final EventType<T> eventType,
1297             final EventHandler<? super T> eventFilter) {
1298         getInternalEventDispatcher().getEventHandlerManager()
1299                                     .addEventFilter(eventType, eventFilter);
1300     }
1301 
1302     // PENDING_DOC_REVIEW
1303     /**
1304      * Unregisters a previously registered event filter from this node. One
1305      * filter might have been registered for different event types, so the
1306      * caller needs to specify the particular event type from which to
1307      * unregister the filter.
1308      *
1309      * @param <T> the specific event class of the filter
1310      * @param eventType the event type from which to unregister
1311      * @param eventFilter the filter to unregister
1312      * @throws NullPointerException if the event type or filter is null
1313      */
1314     public final <T extends Event> void removeEventFilter(
1315             final EventType<T> eventType,
1316             final EventHandler<? super T> eventFilter) {
1317         getInternalEventDispatcher().getEventHandlerManager()
1318                                     .removeEventFilter(eventType, eventFilter);
1319     }
1320 
1321     /**
1322      * Sets the handler to use for this event type. There can only be one such handler
1323      * specified at a time. This handler is guaranteed to be called first. This is
1324      * used for registering the user-defined onFoo event handlers.
1325      *
1326      * @param <T> the specific event class of the handler
1327      * @param eventType the event type to associate with the given eventHandler
1328      * @param eventHandler the handler to register, or null to unregister
1329      * @throws NullPointerException if the event type is null
1330      */
1331     protected final <T extends Event> void setEventHandler(
1332             final EventType<T> eventType,
1333             final EventHandler<? super T> eventHandler) {
1334         getInternalEventDispatcher().getEventHandlerManager()
1335                                     .setEventHandler(eventType, eventHandler);
1336     }
1337 
1338     WindowEventDispatcher getInternalEventDispatcher() {
1339         initializeInternalEventDispatcher();
1340         return internalEventDispatcher;
1341     }
1342 
1343     private void initializeInternalEventDispatcher() {
1344         if (internalEventDispatcher == null) {
1345             internalEventDispatcher = createInternalEventDispatcher();
1346             eventDispatcher = new SimpleObjectProperty<EventDispatcher>(
1347                                           this,
1348                                           "eventDispatcher",
1349                                           internalEventDispatcher);
1350         }
1351     }
1352 
1353     WindowEventDispatcher createInternalEventDispatcher() {
1354         return new WindowEventDispatcher(this);
1355     }
1356 
1357     /**
1358      * Fires the specified event.
1359      * <p>
1360      * This method must be called on the FX user thread.
1361      *
1362      * @param event the event to fire
1363      */
1364     public final void fireEvent(Event event) {
1365         Event.fireEvent(this, event);
1366     }
1367 
1368     // PENDING_DOC_REVIEW
1369     /**
1370      * Construct an event dispatch chain for this window.
1371      *
1372      * @param tail the initial chain to build from
1373      * @return the resulting event dispatch chain for this window
1374      */
1375     @Override
1376     public EventDispatchChain buildEventDispatchChain(
1377             EventDispatchChain tail) {
1378         if (eventDispatcher != null) {
1379             final EventDispatcher eventDispatcherValue = eventDispatcher.get();
1380             if (eventDispatcherValue != null) {
1381                 tail = tail.prepend(eventDispatcherValue);
1382             }
1383         }
1384 
1385         return tail;
1386     }
1387 
1388     private int focusGrabCounter;
1389 
1390     void increaseFocusGrabCounter() {
1391         if ((++focusGrabCounter == 1) && (peer != null) && isFocused()) {
1392             peer.grabFocus();
1393         }
1394     }
1395 
1396     void decreaseFocusGrabCounter() {
1397         if ((--focusGrabCounter == 0) && (peer != null)) {
1398             peer.ungrabFocus();
1399         }
1400     }
1401 
1402     private void focusChanged(final boolean newIsFocused) {
1403         if ((focusGrabCounter > 0) && (peer != null) && newIsFocused) {
1404             peer.grabFocus();
1405         }
1406     }
1407 
1408     final void applyBounds() {
1409         peerBoundsConfigurator.apply();
1410     }
1411 
1412     Window getWindowOwner() {
1413         return null;
1414     }
1415 
1416     private Screen getWindowScreen() {
1417         Window window = this;
1418         do {
1419             if (!Double.isNaN(window.getX())
1420                     && !Double.isNaN(window.getY())
1421                     && !Double.isNaN(window.getWidth())
1422                     && !Double.isNaN(window.getHeight())) {
1423                 return Utils.getScreenForRectangle(
1424                                      new Rectangle2D(window.getX(),
1425                                                      window.getY(),
1426                                                      window.getWidth(),
1427                                                      window.getHeight()));
1428             }
1429 
1430             window = window.getWindowOwner();
1431         } while (window != null);
1432 
1433         return Screen.getPrimary();
1434     }
1435 
1436     private final ReadOnlyObjectWrapper<Screen> screen = new ReadOnlyObjectWrapper<>(Screen.getPrimary());
1437     private ReadOnlyObjectProperty<Screen> screenProperty() { return screen.getReadOnlyProperty(); }
1438 
1439     private void notifyScreenChanged(Object from, Object to) {
1440         screen.set(Screen.getScreenForNative(to));
1441     }
1442 
1443     /**
1444      * Caches all requested bounds settings and applies them at once during
1445      * the next pulse.
1446      */
1447     private final class TKBoundsConfigurator implements TKPulseListener {
1448         private double renderScaleX;
1449         private double renderScaleY;
1450         private double x;
1451         private double y;
1452         private float xGravity;
1453         private float yGravity;
1454         private double windowWidth;
1455         private double windowHeight;
1456         private double clientWidth;
1457         private double clientHeight;
1458 
1459         private boolean dirty;
1460 
1461         public TKBoundsConfigurator() {
1462             reset();
1463         }
1464 
1465         public void setRenderScaleX(final double renderScaleX) {
1466             this.renderScaleX = renderScaleX;
1467             setDirty();
1468         }
1469 
1470         public void setRenderScaleY(final double renderScaleY) {
1471             this.renderScaleY = renderScaleY;
1472             setDirty();
1473         }
1474 
1475         public void setX(final double x, final float xGravity) {
1476             this.x = x;
1477             this.xGravity = xGravity;
1478             setDirty();
1479         }
1480 
1481         public void setY(final double y, final float yGravity) {
1482             this.y = y;
1483             this.yGravity = yGravity;
1484             setDirty();
1485         }
1486 
1487         public void setWindowWidth(final double windowWidth) {
1488             this.windowWidth = windowWidth;
1489             setDirty();
1490         }
1491 
1492         public void setWindowHeight(final double windowHeight) {
1493             this.windowHeight = windowHeight;
1494             setDirty();
1495         }
1496 
1497         public void setClientWidth(final double clientWidth) {
1498             this.clientWidth = clientWidth;
1499             setDirty();
1500         }
1501 
1502         public void setClientHeight(final double clientHeight) {
1503             this.clientHeight = clientHeight;
1504             setDirty();
1505         }
1506 
1507         public void setLocation(final double x,
1508                                 final double y,
1509                                 final float xGravity,
1510                                 final float yGravity) {
1511             this.x = x;
1512             this.y = y;
1513             this.xGravity = xGravity;
1514             this.yGravity = yGravity;
1515             setDirty();
1516         }
1517 
1518         public void setSize(final double windowWidth,
1519                             final double windowHeight,
1520                             final double clientWidth,
1521                             final double clientHeight) {
1522             this.windowWidth = windowWidth;
1523             this.windowHeight = windowHeight;
1524             this.clientWidth = clientWidth;
1525             this.clientHeight = clientHeight;
1526             setDirty();
1527         }
1528 
1529         public void apply() {
1530             if (dirty) {
1531                 // Snapshot values and then reset() before we call down
1532                 // as we may end up with recursive calls back up with
1533                 // new values that must be recorded as dirty.
1534                 boolean xSet = !Double.isNaN(x);
1535                 float newX = xSet ? (float) x : 0f;
1536                 boolean ySet = !Double.isNaN(y);
1537                 float newY = ySet ? (float) y : 0f;
1538                 float newWW = (float) windowWidth;
1539                 float newWH = (float) windowHeight;
1540                 float newCW = (float) clientWidth;
1541                 float newCH = (float) clientHeight;
1542                 float newXG = xGravity;
1543                 float newYG = yGravity;
1544                 float newRX = (float) renderScaleX;
1545                 float newRY = (float) renderScaleY;
1546                 reset();
1547                 peer.setBounds(newX, newY, xSet, ySet,
1548                                     newWW, newWH, newCW, newCH,
1549                                     newXG, newYG,
1550                                     newRX, newRY);
1551             }
1552         }
1553 
1554         @Override
1555         public void pulse() {
1556             apply();
1557         }
1558 
1559         private void reset() {
1560             renderScaleX = 0.0;
1561             renderScaleY = 0.0;
1562             x = Double.NaN;
1563             y = Double.NaN;
1564             xGravity = 0;
1565             yGravity = 0;
1566             windowWidth = -1;
1567             windowHeight = -1;
1568             clientWidth = -1;
1569             clientHeight = -1;
1570             dirty = false;
1571         }
1572 
1573         private void setDirty() {
1574             if (!dirty) {
1575                 Toolkit.getToolkit().requestNextPulse();
1576                 dirty = true;
1577             }
1578         }
1579     }
1580 }