1 /*
   2  * Copyright (c) 2010, 2017, 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.util.ArrayList;
  29 import java.util.List;
  30 
  31 import javafx.application.Platform;
  32 import javafx.beans.property.BooleanProperty;
  33 import javafx.beans.property.SimpleBooleanProperty;
  34 import javafx.beans.property.StringProperty;
  35 import javafx.beans.property.StringPropertyBase;
  36 import javafx.collections.ListChangeListener.Change;
  37 import javafx.collections.ObservableList;
  38 import javafx.geometry.NodeOrientation;
  39 import javafx.scene.Scene;
  40 import javafx.scene.image.Image;
  41 import javafx.scene.input.KeyCombination;
  42 
  43 import com.sun.javafx.collections.VetoableListDecorator;
  44 import com.sun.javafx.collections.TrackableObservableList;
  45 import com.sun.javafx.scene.SceneHelper;
  46 import com.sun.javafx.stage.StageHelper;
  47 import com.sun.javafx.stage.StagePeerListener;
  48 import com.sun.javafx.tk.TKStage;
  49 import com.sun.javafx.tk.Toolkit;
  50 import static com.sun.javafx.FXPermissions.CREATE_TRANSPARENT_WINDOW_PERMISSION;
  51 import javafx.beans.NamedArg;
  52 import javafx.beans.property.DoubleProperty;
  53 import javafx.beans.property.DoublePropertyBase;
  54 import javafx.beans.property.ObjectProperty;
  55 import javafx.beans.property.ReadOnlyBooleanProperty;
  56 import javafx.beans.property.ReadOnlyBooleanWrapper;
  57 import javafx.beans.property.SimpleObjectProperty;
  58 import javafx.beans.value.ObservableValue;
  59 
  60 /**
  61  * The JavaFX {@code Stage} class is the top level JavaFX container.
  62  * The primary Stage is constructed by the platform. Additional Stage
  63  * objects may be constructed by the application.
  64  *
  65  * <p>
  66  * Stage objects must be constructed and modified on the
  67  * JavaFX Application Thread.
  68  * </p>
  69  * <p>
  70  * The JavaFX Application Thread is created as part of the startup process for
  71  * the JavaFX runtime. See the {@link javafx.application.Application} class and
  72  * the {@link Platform#startup(Runnable)} method for more information.
  73  * </p>
  74  * <p>
  75  * Many of the {@code Stage} properties are read only because they can
  76  * be changed externally by the underlying platform and therefore must
  77  * not be bindable.
  78  * </p>
  79  *
  80  * <p><b>Style</b></p>
  81  * <p>
  82  * A stage has one of the following styles:
  83  * <ul>
  84  * <li>{@link StageStyle#DECORATED} - a stage with a solid white background and
  85  * platform decorations.</li>
  86  * <li>{@link StageStyle#UNDECORATED} - a stage with a solid white background
  87  * and no decorations.</li>
  88  * <li>{@link StageStyle#TRANSPARENT} - a stage with a transparent background
  89  * and no decorations.</li>
  90  * <li>{@link StageStyle#UTILITY} - a stage with a solid white background and
  91  * minimal platform decorations.</li>
  92  * </ul>
  93  * <p>The style must be initialized before the stage is made visible.</p>
  94  * <p>On some platforms decorations might not be available. For example, on
  95  * some mobile or embedded devices. In these cases a request for a DECORATED or
  96  * UTILITY window will be accepted, but no decorations will be shown. </p>
  97  *
  98  * <p><b>Owner</b></p>
  99  * <p>
 100  * A stage can optionally have an owner Window.
 101  * When a window is a stage's owner, it is said to be the parent of that stage.
 102  * <p>
 103  * Owned Stages are tied to the parent Window.
 104  * An owned stage will always be on top of its parent window.
 105  * When a parent window is closed or iconified, then all owned windows will be affected as well.
 106  * Owned Stages cannot be independantly iconified.
 107  * <p>
 108  * The owner must be initialized before the stage is made visible.
 109  *
 110  * <p><b>Modality</b></p>
 111  * <p>
 112  * A stage has one of the following modalities:
 113  * <ul>
 114  * <li>{@link Modality#NONE} - a stage that does not block any other window.</li>
 115  * <li>{@link Modality#WINDOW_MODAL} - a stage that blocks input events from
 116  * being delivered to all windows from its owner (parent) to its root.
 117  * Its root is the closest ancestor window without an owner.</li>
 118  * <li>{@link Modality#APPLICATION_MODAL} - a stage that blocks input events from
 119  * being delivered to all windows from the same application, except for those
 120  * from its child hierarchy.</li>
 121  * </ul>
 122  *
 123  * <p>When a window is blocked by a modal stage its Z-order relative to its ancestors
 124  * is preserved, and it receives no input events and no window activation events,
 125  * but continues to animate and render normally.
 126  * Note that showing a modal stage does not necessarily block the caller. The
 127  * {@link #show} method returns immediately regardless of the modality of the stage.
 128  * Use the {@link #showAndWait} method if you need to block the caller until
 129  * the modal stage is hidden (closed).
 130  * The modality must be initialized before the stage is made visible.</p>
 131  *
 132  * <p><b>Example:</b></p>
 133  *
 134  *
 135 <pre><code>
 136 import javafx.application.Application;
 137 import javafx.scene.Group;
 138 import javafx.scene.Scene;
 139 import javafx.scene.text.Font;
 140 import javafx.scene.text.Text;
 141 import javafx.stage.Stage;
 142 
 143 public class HelloWorld extends Application {
 144 
 145     {@literal @Override} public void start(Stage stage) {
 146         Text text = new Text(10, 40, "Hello World!");
 147         text.setFont(new Font(40));
 148         Scene scene = new Scene(new Group(text));
 149 
 150         stage.setTitle("Welcome to JavaFX!");
 151         stage.setScene(scene);
 152         stage.sizeToScene();
 153         stage.show();
 154     }
 155 
 156     public static void main(String[] args) {
 157         Application.launch(args);
 158     }
 159 }
 160 
 161  * </code></pre>
 162  * <p>produces the following on Windows:</p>
 163  * <p><img src="doc-files/Stage-win.png" alt="A visual rendering
 164      * of a JavaFX Stage on Windows"></p>
 165  *
 166  * <p>produces the following on Mac OSX:</p>
 167  * <p><img src="doc-files/Stage-mac.png" alt="A visual rendering
 168      * of a JavaFX Stage on Mac OSX"></p>
 169  *
 170  * <p>produces the following on Linux:</p>
 171  * <p><img src="doc-files/Stage-linux.png" alt="A visual rendering
 172      * of a JavaFX Stage on Linux"></p>
 173  * @since JavaFX 2.0
 174  */
 175 public class Stage extends Window {
 176 
 177     private boolean inNestedEventLoop = false;
 178 
 179     static {
 180         StageHelper.setStageAccessor(new StageHelper.StageAccessor() {
 181             @Override public void doVisibleChanging(Window window, boolean visible) {
 182                 ((Stage) window).doVisibleChanging(visible);
 183             }
 184 
 185             @Override public void doVisibleChanged(Window window, boolean visible) {
 186                 ((Stage) window).doVisibleChanged(visible);
 187             }
 188 
 189             @Override public void initSecurityDialog(Stage stage, boolean securityDialog) {
 190                 stage.initSecurityDialog(securityDialog);
 191             }
 192 
 193             @Override
 194             public void setPrimary(Stage stage, boolean primary) {
 195                 stage.setPrimary(primary);
 196             }
 197 
 198             @Override
 199             public void setImportant(Stage stage, boolean important) {
 200                 stage.setImportant(important);
 201             }
 202         });
 203     }
 204 
 205     private static final StagePeerListener.StageAccessor STAGE_ACCESSOR = new StagePeerListener.StageAccessor() {
 206 
 207         @Override
 208         public void setIconified(Stage stage, boolean iconified) {
 209             stage.iconifiedPropertyImpl().set(iconified);
 210         }
 211 
 212         @Override
 213         public void setMaximized(Stage stage, boolean maximized) {
 214             stage.maximizedPropertyImpl().set(maximized);
 215         }
 216 
 217         @Override
 218         public void setResizable(Stage stage, boolean resizable) {
 219             ((ResizableProperty)stage.resizableProperty()).setNoInvalidate(resizable);
 220         }
 221 
 222         @Override
 223         public void setFullScreen(Stage stage, boolean fs) {
 224             stage.fullScreenPropertyImpl().set(fs);
 225         }
 226 
 227         @Override
 228         public void setAlwaysOnTop(Stage stage, boolean aot) {
 229             stage.alwaysOnTopPropertyImpl().set(aot);
 230         }
 231     };
 232 
 233     /**
 234      * Creates a new instance of decorated {@code Stage}.
 235      *
 236      * @throws IllegalStateException if this constructor is called on a thread
 237      * other than the JavaFX Application Thread.
 238      */
 239     public Stage() {
 240         this(StageStyle.DECORATED);
 241     }
 242 
 243     /**
 244      * Creates a new instance of {@code Stage}.
 245      *
 246      * @param style The style of the {@code Stage}
 247      *
 248      * @throws IllegalStateException if this constructor is called on a thread
 249      * other than the JavaFX Application Thread.
 250      */
 251     public Stage(@NamedArg(value="style", defaultValue="DECORATED") StageStyle style) {
 252         super();
 253 
 254         Toolkit.getToolkit().checkFxUserThread();
 255 
 256         // Set the style
 257         initStyle(style);
 258         StageHelper.initHelper(this);
 259     }
 260 
 261     /**
 262      * Specify the scene to be used on this stage.
 263      */
 264     @Override final public void setScene(Scene value) {
 265         Toolkit.getToolkit().checkFxUserThread();
 266         super.setScene(value);
 267     }
 268 
 269     /**
 270      * {@inheritDoc}
 271      */
 272     @Override public final void show() {
 273         super.show();
 274     }
 275 
 276     private boolean primary = false;
 277 
 278     ////////////////////////////////////////////////////////////////////
 279 
 280     // Flag indicating that this stage is being used to show a security dialog
 281     private boolean securityDialog = false;
 282 
 283     /**
 284      * Sets a flag indicating that this stage is used for a security dialog and
 285      * must always be on top. If set, this will cause the window to be always
 286      * on top, regardless of the setting of the alwaysOnTop property, and
 287      * whether or not permissions are granted when the dialog is shown.
 288      * NOTE: this flag must be set prior to showing the stage the first time.
 289      *
 290      * @param securityDialog flag indicating that this Stage is being used to
 291      * show a security dialog that should be always-on-top
 292      *
 293      * @throws IllegalStateException if this property is set after the stage
 294      * has ever been made visible.
 295      *
 296      * @defaultValue false
 297      */
 298     final void initSecurityDialog(boolean securityDialog) {
 299         if (hasBeenVisible) {
 300             throw new IllegalStateException("Cannot set securityDialog once stage has been set visible");
 301         }
 302 
 303         this.securityDialog = securityDialog;
 304     }
 305 
 306     /**
 307      * Returns the state of the securityDialog flag.
 308      *
 309      * @return a flag indicating whether or not this is a security dialog
 310      */
 311     final boolean isSecurityDialog() {
 312         return securityDialog;
 313     }
 314 
 315     /*
 316      * sets this stage to be the primary stage.
 317      * When run as an applet, this stage will appear in the broswer
 318      */
 319     void setPrimary(boolean primary) {
 320         this.primary = primary;
 321     }
 322 
 323     /*
 324      * Returns whether this stage is the primary stage.
 325      * When run as an applet, the primary stage will appear in the broswer
 326      *
 327      * @return true if this stage is the primary stage for the application.
 328      */
 329     boolean isPrimary() {
 330         return primary;
 331     }
 332 
 333     private boolean important = true;
 334 
 335     /*
 336      * Sets a flag indicating whether this stage is an "important" window for
 337      * the purpose of determining whether the application is idle and should
 338      * exit. The application is considered finished when the last important
 339      * window is closed.
 340      */
 341     void setImportant(boolean important) {
 342         this.important = important;
 343     }
 344 
 345     private boolean isImportant() {
 346         return important;
 347     }
 348 
 349     /**
 350      * Shows this stage and waits for it to be hidden (closed) before returning
 351      * to the caller. This method temporarily blocks processing of the current
 352      * event, and starts a nested event loop to handle other events.
 353      * This method must be called on the FX Application thread.
 354      * <p>
 355      * A Stage is hidden (closed) by one of the following means:
 356      * <ul>
 357      * <li>the application calls the {@link #hide} or {@link #close} method on
 358      * this stage</li>
 359      * <li>this stage has a non-null owner window, and its owner is closed</li>
 360      * <li>the user closes the window via the window system (for example,
 361      * by pressing the close button in the window decoration)</li>
 362      * </ul>
 363      *
 364      * <p>
 365      * After the Stage is hidden, and the application has returned from the
 366      * event handler to the event loop, the nested event loop terminates
 367      * and this method returns to the caller.
 368      * </p>
 369      * <p>
 370      * For example, consider the following sequence of operations for different
 371      * event handlers, assumed to execute in the order shown below:
 372      * <pre>void evtHander1(...) {
 373      *     stage1.showAndWait();
 374      *     doSomethingAfterStage1Closed(...)
 375      * }
 376      *
 377      * void evtHander2(...) {
 378      *     stage1.hide();
 379      *     doSomethingElseHere(...)
 380      * }</pre>
 381      * evtHandler1 will block at the call to showAndWait. It will resume execution
 382      * after stage1 is hidden and the current event handler, in this case evtHandler2,
 383      * returns to the event loop. This means that doSomethingElseHere will
 384      * execute before doSomethingAfterStage1Closed.
 385      *
 386      * <p>
 387      * More than one stage may be shown with showAndWait. Each call
 388      * will start a new nested event loop. The stages may be hidden in any order,
 389      * but a particular nested event loop (and thus the showAndWait method
 390      * for the associated stage) will only terminate after all inner event loops
 391      * have also terminated.
 392      * </p>
 393      * <p>
 394      * For example, consider the following sequence of operations for different
 395      * event handlers, assumed to execute in the order shown below:
 396      * <pre>void evtHander1() {
 397      *     stage1.showAndWait();
 398      *     doSomethingAfterStage1Closed(...)
 399      * }
 400      *
 401      * void evtHander2() {
 402      *     stage2.showAndWait();
 403      *     doSomethingAfterStage2Closed(...)
 404      * }
 405      *
 406      * void evtHander3() {
 407      *     stage1.hide();
 408      *     doSomethingElseHere(...)
 409      * }
 410      *
 411      * void evtHander4() {
 412      *     stage2.hide();
 413      *     doSomethingElseHereToo(...)
 414      * }</pre>
 415      * evtHandler1 will block at the call to stage1.showAndWait, starting up
 416      * a nested event loop just like in the previous example. evtHandler2 will
 417      * then block at the call to stage2.showAndWait, starting up another (inner)
 418      * nested event loop. The first call to stage1.showAndWait will resume execution
 419      * after stage1 is hidden, but only after the inner nested event loop started
 420      * by stage2.showAndWait has terminated. This means that the call to
 421      * stage1.showAndWait won't return until after evtHandler2 has returned.
 422      * The order of execution is: stage1.showAndWait, stage2.showAndWait,
 423      * stage1.hide, doSomethingElseHere, stage2.hide, doSomethingElseHereToo,
 424      * doSomethingAfterStage2Closed, doSomethingAfterStage1Closed.
 425      *
 426      * <p>
 427      * This method must not be called on the primary stage or on a stage that
 428      * is already visible.
 429      * Additionally, it must either be called from an input event handler or
 430      * from the run method of a Runnable passed to
 431      * {@link javafx.application.Platform#runLater Platform.runLater}.
 432      * It must not be called during animation or layout processing.
 433      * </p>
 434      *
 435      * @throws IllegalStateException if this method is called on a thread
 436      *     other than the JavaFX Application Thread.
 437      * @throws IllegalStateException if this method is called during
 438      *     animation or layout processing.
 439      * @throws IllegalStateException if this method is called on the
 440      *     primary stage.
 441      * @throws IllegalStateException if this stage is already showing.
 442      * @since JavaFX 2.2
 443      */
 444     public void showAndWait() {
 445 
 446         Toolkit.getToolkit().checkFxUserThread();
 447 
 448         if (isPrimary()) {
 449             throw new IllegalStateException("Cannot call this method on primary stage");
 450         }
 451 
 452         if (isShowing()) {
 453             throw new IllegalStateException("Stage already visible");
 454         }
 455 
 456         if (!Toolkit.getToolkit().canStartNestedEventLoop()) {
 457             throw new IllegalStateException("showAndWait is not allowed during animation or layout processing");
 458         }
 459 
 460         // TODO: file a new bug; the following assertion can fail if this
 461         // method is called from an event handler that is listening to a
 462         // WindowEvent.WINDOW_HIDING event.
 463         assert !inNestedEventLoop;
 464 
 465         show();
 466         inNestedEventLoop = true;
 467         Toolkit.getToolkit().enterNestedEventLoop(this);
 468     }
 469 
 470     private StageStyle style; // default is set in constructor
 471 
 472     /**
 473      * Specifies the style for this stage. This must be done prior to making
 474      * the stage visible. The style is one of: StageStyle.DECORATED,
 475      * StageStyle.UNDECORATED, StageStyle.TRANSPARENT, or StageStyle.UTILITY.
 476      *
 477      * @param style the style for this stage.
 478      *
 479      * @throws IllegalStateException if this property is set after the stage
 480      * has ever been made visible.
 481      *
 482      * @defaultValue StageStyle.DECORATED
 483      */
 484     public final void initStyle(StageStyle style) {
 485         if (hasBeenVisible) {
 486             throw new IllegalStateException("Cannot set style once stage has been set visible");
 487         }
 488         this.style = style;
 489     }
 490 
 491     /**
 492      * Retrieves the style attribute for this stage.
 493      *
 494      * @return the stage style.
 495      */
 496     public final StageStyle getStyle() {
 497         return style;
 498     }
 499 
 500     private Modality modality = Modality.NONE;
 501 
 502     /**
 503      * Specifies the modality for this stage. This must be done prior to making
 504      * the stage visible. The modality is one of: Modality.NONE,
 505      * Modality.WINDOW_MODAL, or Modality.APPLICATION_MODAL.
 506      *
 507      * @param modality the modality for this stage.
 508      *
 509      * @throws IllegalStateException if this property is set after the stage
 510      * has ever been made visible.
 511      *
 512      * @throws IllegalStateException if this stage is the primary stage.
 513      *
 514      * @defaultValue Modality.NONE
 515      */
 516     public final void initModality(Modality modality) {
 517         if (hasBeenVisible) {
 518             throw new IllegalStateException("Cannot set modality once stage has been set visible");
 519         }
 520 
 521         if (isPrimary()) {
 522             throw new IllegalStateException("Cannot set modality for the primary stage");
 523         }
 524 
 525         this.modality = modality;
 526     }
 527 
 528     /**
 529      * Retrieves the modality attribute for this stage.
 530      *
 531      * @return the modality.
 532      */
 533     public final Modality getModality() {
 534         return modality;
 535     }
 536 
 537     private Window owner = null;
 538 
 539     /**
 540      * Specifies the owner Window for this stage, or null for a top-level,
 541      * unowned stage. This must be done prior to making the stage visible.
 542      *
 543      * @param owner the owner for this stage.
 544      *
 545      * @throws IllegalStateException if this property is set after the stage
 546      * has ever been made visible.
 547      *
 548      * @throws IllegalStateException if this stage is the primary stage.
 549      *
 550      * @defaultValue null
 551      */
 552     public final void initOwner(Window owner) {
 553         if (hasBeenVisible) {
 554             throw new IllegalStateException("Cannot set owner once stage has been set visible");
 555         }
 556 
 557         if (isPrimary()) {
 558             throw new IllegalStateException("Cannot set owner for the primary stage");
 559         }
 560 
 561         this.owner = owner;
 562 
 563         final Scene sceneValue = getScene();
 564         if (sceneValue != null) {
 565             SceneHelper.parentEffectiveOrientationInvalidated(sceneValue);
 566         }
 567     }
 568 
 569     /**
 570      * Retrieves the owner Window for this stage, or null for an unowned stage.
 571      *
 572      * @return the owner Window.
 573      */
 574     public final Window getOwner() {
 575         return owner;
 576     }
 577 
 578     /**
 579      * Specifies whether this {@code Stage} should be a full-screen,
 580      * undecorated window.
 581      * <p>
 582      * The implementation of full-screen mode is platform and profile-dependent.
 583      * </p>
 584      * <p>
 585      * When set to {@code true}, the {@code Stage} will attempt to enter
 586      * full-screen mode when visible. Set to {@code false} to return {@code Stage}
 587      * to windowed mode.
 588      * An {@link IllegalStateException} is thrown if this property is set
 589      * on a thread other than the JavaFX Application Thread.
 590      * </p>
 591      * <p>
 592      * The full-screen mode will be exited (and the {@code fullScreen} attribute
 593      * will be set to {@code false}) if the full-screen
 594      * {@code Stage} loses focus or if another {@code Stage} enters
 595      * full-screen mode on the same {@link Screen}. Note that a {@code Stage}
 596      * in full-screen mode can become invisible without losing its
 597      * full-screen status and will again enter full-screen mode when the
 598      * {@code Stage} becomes visible.
 599      * </p>
 600      * If the platform supports multiple screens an application can control
 601      * which {@code Screen} the Stage will enter full-screen mode on by
 602      * setting its position to be within the bounds of that {@code Screen}
 603      * prior to entering full-screen mode.
 604      * <p>
 605      * However once in full-screen mode, {@code Stage}'s {@code x}, {@code y},
 606      * {@code width}, and {@code height} variables will continue to represent
 607      * the non-full-screen position and size of the window; the same for
 608      * {@code iconified}, {@code resizable}, {@code style}, and {@code
 609      * opacity}. If changes are made to any of these attributes while in
 610      * full-screen mode, upon exiting full-screen mode the {@code Stage} will
 611      * assume those attributes.
 612      * </p>
 613      * <p>
 614      * In case that more {@code Stage} modes are set simultaneously their order
 615      * of importance is {@code iconified}, fullScreen, {@code maximized} (from
 616      * strongest to weakest).
 617      * </p>
 618      * <p>
 619      * The property is read only because it can be changed externally
 620      * by the underlying platform and therefore must not be bindable.
 621      * </p>
 622      *
 623      * The user can unconditionally exit full-screen mode
 624      * at any time by pressing {@code ESC}.
 625      * <p>
 626      * If a security manager is present, the application must have the
 627      * {@link javafx.util.FXPermission} "unrestrictedFullScreen" in order
 628      * to enter full-screen mode with no restrictions. Applications without
 629      * permission will have the following restrictions:
 630      * </p>
 631      * <ul>
 632      *  <li>Applications can only enter full-screen mode in response
 633      *   to user input. More specifically, entering is allowed from mouse
 634      *   ({@code Node.mousePressed/mouseReleased/mouseClicked}) or keyboard
 635      *   ({@code Node.keyPressed/keyReleased/keyTyped}) event handlers. It is
 636      *   not allowed to enter full-screen mode in response to {@code ESC}
 637      *   key. Attempting to enter full-screen mode from any other context will
 638      *   be ignored.
 639      *   <p>
 640      *   If {@code Stage} was constructed as full-screen but not visible
 641      *   it will enter full-screen mode upon becoming visible, with the same
 642      *   limitations to when this is allowed to happen as when setting
 643      *   {@code fullScreen} to {@code true}.
 644      *   </p>
 645      *  </li>
 646      *  <li> If the application was allowed to enter full-screen mode
 647      *   it will have limited keyboard input. It will only receive KEY_PRESSED
 648      *   and KEY_RELEASED events from the following keys:
 649      *   {@code UP, DOWN, LEFT, RIGHT, SPACE, TAB, PAGE_UP, PAGE_DOWN, HOME, END, ENTER}
 650      *  </li>
 651      * </ul>
 652      * @defaultValue false
 653      */
 654     private ReadOnlyBooleanWrapper fullScreen;
 655 
 656     public final void setFullScreen(boolean value) {
 657         Toolkit.getToolkit().checkFxUserThread();
 658         fullScreenPropertyImpl().set(value);
 659         if (getPeer() != null)
 660             getPeer().setFullScreen(value);
 661     }
 662 
 663     public final boolean isFullScreen() {
 664         return fullScreen == null ? false : fullScreen.get();
 665     }
 666 
 667     public final ReadOnlyBooleanProperty fullScreenProperty() {
 668         return fullScreenPropertyImpl().getReadOnlyProperty();
 669     }
 670 
 671     private ReadOnlyBooleanWrapper fullScreenPropertyImpl () {
 672         if (fullScreen == null) {
 673             fullScreen = new ReadOnlyBooleanWrapper(Stage.this, "fullScreen");
 674         }
 675         return fullScreen;
 676     }
 677 
 678     /**
 679      * Defines the icon images to be used in the window decorations and when
 680      * minimized. The images should be different sizes of the same image and
 681      * the best size will be chosen, eg. 16x16, 32,32.
 682      *
 683      * @defaultValue empty
 684      */
 685     private ObservableList<Image> icons = new VetoableListDecorator<Image>(new TrackableObservableList<Image>() {
 686         @Override protected void onChanged(Change<Image> c) {
 687             List<Object> platformImages = new ArrayList<Object>();
 688             for (Image icon : icons) {
 689                 platformImages.add(Toolkit.getImageAccessor().getPlatformImage(icon));
 690             }
 691             if (getPeer() != null) {
 692                 getPeer().setIcons(platformImages);
 693             }
 694         }
 695     }) {
 696         @Override protected void onProposedChange(
 697                 final List<Image> toBeAddedIcons, int[] indices) {
 698             for (Image icon : toBeAddedIcons) {
 699                 if (icon == null) {
 700                     throw new NullPointerException("icon can not be null.");
 701                 }
 702             }
 703         }
 704     };
 705 
 706     /**
 707      * Gets the icon images to be used in the window decorations and when
 708      * minimized. The images should be different sizes of the same image and
 709      * the best size will be chosen, eg. 16x16, 32,32.
 710      * @return An observable list of icons of this window
 711      */
 712     public final ObservableList<Image> getIcons() {
 713         return icons;
 714     }
 715 
 716     /**
 717      * Defines the title of the {@code Stage}.
 718      *
 719      * @defaultValue empty string
 720      */
 721     private StringProperty title;
 722 
 723     public final void setTitle(String value) {
 724         titleProperty().set(value);
 725     }
 726 
 727     public final String getTitle() {
 728         return title == null ? null : title.get();
 729     }
 730 
 731     public final StringProperty titleProperty() {
 732         if (title == null) {
 733             title = new StringPropertyBase() {
 734 
 735                 @Override
 736                 protected void invalidated() {
 737                     if (getPeer() != null) {
 738                         getPeer().setTitle(get());
 739                     }
 740                 }
 741 
 742                 @Override
 743                 public Object getBean() {
 744                     return Stage.this;
 745                 }
 746 
 747                 @Override
 748                 public String getName() {
 749                     return "title";
 750                 }
 751             };
 752         }
 753         return title;
 754     }
 755 
 756     /**
 757      * Defines whether the {@code Stage} is iconified or not.
 758      * <p>
 759      * In case that more {@code Stage} modes are set simultaneously their order
 760      * of importance is iconified} {@code fullScreen}, {@code maximized} (from
 761      * strongest to weakest).
 762      * </p>
 763      * <p>
 764      * On some mobile and embedded platforms setting this property to true will
 765      * hide the {@code Stage} but not show an icon for it.
 766      * </p>
 767      * <p>
 768      * The property is read only because it can be changed externally
 769      * by the underlying platform and therefore must not be bindable.
 770      * </p>
 771      *
 772      * @defaultValue false
 773      */
 774     private ReadOnlyBooleanWrapper iconified;
 775 
 776     public final void setIconified(boolean value) {
 777         iconifiedPropertyImpl().set(value);
 778         if (getPeer() != null)
 779             getPeer().setIconified(value);
 780     }
 781 
 782     public final boolean isIconified() {
 783         return iconified == null ? false : iconified.get();
 784     }
 785 
 786     public final ReadOnlyBooleanProperty iconifiedProperty() {
 787         return iconifiedPropertyImpl().getReadOnlyProperty();
 788     }
 789 
 790     private final ReadOnlyBooleanWrapper iconifiedPropertyImpl() {
 791         if (iconified == null) {
 792             iconified = new ReadOnlyBooleanWrapper(Stage.this, "iconified");
 793         }
 794         return iconified;
 795     }
 796 
 797     /**
 798      * Defines whether the {@code Stage} is maximized or not.
 799      * <p>
 800      * In case that more {@code Stage} modes are set simultaneously their order
 801      * of importance is {@code iconified}, {@code fullScreen}, maximized (from
 802      * strongest to weakest).
 803      * </p>
 804      * <p>
 805      * The property is read only because it can be changed externally
 806      * by the underlying platform and therefore must not be bindable.
 807      * </p>
 808      *
 809      * @defaultValue false
 810      * @since JavaFX 8.0
 811      */
 812     private ReadOnlyBooleanWrapper maximized;
 813 
 814     public final void setMaximized(boolean value) {
 815         maximizedPropertyImpl().set(value);
 816         if (getPeer() != null) {
 817             getPeer().setMaximized(value);
 818         }
 819     }
 820 
 821     public final boolean isMaximized() {
 822         return maximized == null ? false : maximized.get();
 823     }
 824 
 825     public final ReadOnlyBooleanProperty maximizedProperty() {
 826         return maximizedPropertyImpl().getReadOnlyProperty();
 827     }
 828 
 829     private final ReadOnlyBooleanWrapper maximizedPropertyImpl() {
 830         if (maximized == null) {
 831             maximized = new ReadOnlyBooleanWrapper(Stage.this, "maximized");
 832         }
 833         return maximized;
 834     }
 835 
 836     /**
 837      * Defines whether this {@code Stage} is kept on top of other windows.
 838      * <p>
 839      * If some other window is already always-on-top then the
 840      * relative order between these windows is unspecified (depends on
 841      * platform).
 842      * </p>
 843      * <p>
 844      * If a security manager is present, the application must have the
 845      * {@link javafx.util.FXPermission} "setWindowAlwaysOnTop" in order for
 846      * this property to have any effect. If the application does not have
 847      * permission, attempting to set this property will be ignored
 848      * and the property value will be restored to {@code false}.
 849      * </p>
 850      * <p>
 851      * The property is read only because it can be changed externally
 852      * by the underlying platform and therefore must not be bindable.
 853      * </p>
 854      *
 855      * @defaultValue false
 856      * @since JavaFX 8u20
 857      */
 858     private ReadOnlyBooleanWrapper alwaysOnTop;
 859 
 860     public final void setAlwaysOnTop(boolean value) {
 861         alwaysOnTopPropertyImpl().set(value);
 862         if (getPeer() != null) {
 863             getPeer().setAlwaysOnTop(value);
 864         }
 865     }
 866 
 867     public final boolean isAlwaysOnTop() {
 868         return alwaysOnTop == null ? false : alwaysOnTop.get();
 869     }
 870 
 871     public final ReadOnlyBooleanProperty alwaysOnTopProperty() {
 872         return alwaysOnTopPropertyImpl().getReadOnlyProperty();
 873     }
 874 
 875     private ReadOnlyBooleanWrapper alwaysOnTopPropertyImpl() {
 876         if (alwaysOnTop == null) {
 877             alwaysOnTop = new ReadOnlyBooleanWrapper(Stage.this, "alwaysOnTop");
 878         }
 879         return alwaysOnTop;
 880     }
 881 
 882     /**
 883      * Defines whether the {@code Stage} is resizable or not by the user.
 884      * Programatically you may still change the size of the Stage. This is
 885      * a hint which allows the implementation to optionally make the Stage
 886      * resizable by the user.
 887      * <p>
 888      * <b>Warning:</b> Since 8.0 the property cannot be bound and will throw
 889      * {@code RuntimeException} on an attempt to do so. This is because
 890      * the setting of resizable is asynchronous on some systems or generally
 891      * might be set by the system / window manager.
 892      * <br>
 893      * Bidirectional binds are still allowed, as they don't block setting of the
 894      * property by the system.
 895      *
 896      * @defaultValue true
 897      */
 898     private BooleanProperty resizable;
 899 
 900     public final void setResizable(boolean value) {
 901         resizableProperty().set(value);
 902     }
 903 
 904     public final boolean isResizable() {
 905         return resizable == null ? true : resizable.get();
 906     }
 907 
 908     public final BooleanProperty resizableProperty() {
 909         if (resizable == null) {
 910             resizable = new ResizableProperty();
 911         }
 912         return resizable;
 913     }
 914 
 915     //We cannot return ReadOnlyProperty in resizable, as this would be
 916     // backward incompatible. All we can do is to create this custom property
 917     // implementation that disallows binds
 918     private class ResizableProperty extends SimpleBooleanProperty {
 919         private boolean noInvalidate;
 920 
 921         public ResizableProperty() {
 922             super(Stage.this, "resizable", true);
 923         }
 924 
 925         void setNoInvalidate(boolean value) {
 926             noInvalidate = true;
 927             set(value);
 928             noInvalidate = false;
 929         }
 930 
 931         @Override
 932         protected void invalidated() {
 933             if (noInvalidate) {
 934                 return;
 935             }
 936             if (getPeer() != null) {
 937                 applyBounds();
 938                 getPeer().setResizable(get());
 939             }
 940         }
 941 
 942         @Override
 943         public void bind(ObservableValue<? extends Boolean> rawObservable) {
 944             throw new RuntimeException("Resizable property cannot be bound");
 945         }
 946 
 947     }
 948 
 949     /**
 950      * Defines the minimum width of this {@code Stage}.
 951      *
 952      * @defaultValue 0
 953      * @since JavaFX 2.1
 954      */
 955     private DoubleProperty minWidth;
 956 
 957     public final void setMinWidth(double value) {
 958         minWidthProperty().set(value);
 959     }
 960 
 961     public final double getMinWidth() {
 962         return minWidth == null ? 0 : minWidth.get();
 963     }
 964 
 965     public final DoubleProperty minWidthProperty() {
 966         if (minWidth == null) {
 967             minWidth = new DoublePropertyBase(0) {
 968 
 969                 @Override
 970                 protected void invalidated() {
 971                     if (getPeer() != null) {
 972                         getPeer().setMinimumSize((int) Math.ceil(get()),
 973                                 (int) Math.ceil(getMinHeight()));
 974                     }
 975                     if (getWidth() < getMinWidth()) {
 976                         setWidth(getMinWidth());
 977                     }
 978                 }
 979 
 980                 @Override
 981                 public Object getBean() {
 982                     return Stage.this;
 983                 }
 984 
 985                 @Override
 986                 public String getName() {
 987                     return "minWidth";
 988                 }
 989             };
 990         }
 991         return minWidth;
 992     }
 993 
 994     /**
 995      * Defines the minimum height of this {@code Stage}.
 996      *
 997      * @defaultValue 0
 998      * @since JavaFX 2.1
 999      */
1000     private DoubleProperty minHeight;
1001 
1002     public final void setMinHeight(double value) {
1003         minHeightProperty().set(value);
1004     }
1005 
1006     public final double getMinHeight() {
1007         return minHeight == null ? 0 : minHeight.get();
1008     }
1009 
1010     public final DoubleProperty minHeightProperty() {
1011         if (minHeight == null) {
1012             minHeight = new DoublePropertyBase(0) {
1013 
1014                 @Override
1015                 protected void invalidated() {
1016                     if (getPeer() != null) {
1017                         getPeer().setMinimumSize(
1018                                 (int) Math.ceil(getMinWidth()),
1019                                 (int) Math.ceil(get()));
1020                     }
1021                     if (getHeight() < getMinHeight()) {
1022                         setHeight(getMinHeight());
1023                     }
1024                 }
1025 
1026                 @Override
1027                 public Object getBean() {
1028                     return Stage.this;
1029                 }
1030 
1031                 @Override
1032                 public String getName() {
1033                     return "minHeight";
1034                 }
1035             };
1036         }
1037         return minHeight;
1038     }
1039 
1040     /**
1041      * Defines the maximum width of this {@code Stage}.
1042      *
1043      * @defaultValue Double.MAX_VALUE
1044      * @since JavaFX 2.1
1045      */
1046     private DoubleProperty maxWidth;
1047 
1048     public final void setMaxWidth(double value) {
1049         maxWidthProperty().set(value);
1050     }
1051 
1052     public final double getMaxWidth() {
1053         return maxWidth == null ? Double.MAX_VALUE : maxWidth.get();
1054     }
1055 
1056     public final DoubleProperty maxWidthProperty() {
1057         if (maxWidth == null) {
1058             maxWidth = new DoublePropertyBase(Double.MAX_VALUE) {
1059 
1060                 @Override
1061                 protected void invalidated() {
1062                     if (getPeer() != null) {
1063                         getPeer().setMaximumSize((int) Math.floor(get()),
1064                                 (int) Math.floor(getMaxHeight()));
1065                     }
1066                     if (getWidth() > getMaxWidth()) {
1067                         setWidth(getMaxWidth());
1068                     }
1069                 }
1070 
1071                 @Override
1072                 public Object getBean() {
1073                     return Stage.this;
1074                 }
1075 
1076                 @Override
1077                 public String getName() {
1078                     return "maxWidth";
1079                 }
1080             };
1081         }
1082         return maxWidth;
1083     }
1084 
1085     /**
1086      * Defines the maximum height of this {@code Stage}.
1087      *
1088      * @defaultValue Double.MAX_VALUE
1089      * @since JavaFX 2.1
1090      */
1091     private DoubleProperty maxHeight;
1092 
1093     public final void setMaxHeight(double value) {
1094         maxHeightProperty().set(value);
1095     }
1096 
1097     public final double getMaxHeight() {
1098         return maxHeight == null ? Double.MAX_VALUE : maxHeight.get();
1099     }
1100 
1101     public final DoubleProperty maxHeightProperty() {
1102         if (maxHeight == null) {
1103             maxHeight = new DoublePropertyBase(Double.MAX_VALUE) {
1104 
1105                 @Override
1106                 protected void invalidated() {
1107                     if (getPeer() != null) {
1108                         getPeer().setMaximumSize(
1109                                 (int) Math.floor(getMaxWidth()),
1110                                 (int) Math.floor(get()));
1111                     }
1112                     if (getHeight() > getMaxHeight()) {
1113                         setHeight(getMaxHeight());
1114                     }
1115                 }
1116 
1117                 @Override
1118                 public Object getBean() {
1119                     return Stage.this;
1120                 }
1121 
1122                 @Override
1123                 public String getName() {
1124                     return "maxHeight";
1125                 }
1126             };
1127         }
1128         return maxHeight;
1129     }
1130 
1131     /*
1132      * This can be replaced by listening for the onShowing/onHiding events
1133      * Note: This method MUST only be called via its accessor method.
1134      */
1135     private void doVisibleChanging(boolean value) {
1136         Toolkit toolkit = Toolkit.getToolkit();
1137         if (value && (getPeer() == null)) {
1138             // Setup the peer
1139             Window window = getOwner();
1140             TKStage tkStage = (window == null ? null : window.getPeer());
1141             Scene scene = getScene();
1142             boolean rtl = scene != null && scene.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT;
1143 
1144             StageStyle stageStyle = getStyle();
1145             if (stageStyle == StageStyle.TRANSPARENT) {
1146                 final SecurityManager securityManager =
1147                         System.getSecurityManager();
1148                 if (securityManager != null) {
1149                     try {
1150                         securityManager.checkPermission(CREATE_TRANSPARENT_WINDOW_PERMISSION);
1151                     } catch (final SecurityException e) {
1152                         stageStyle = StageStyle.UNDECORATED;
1153                     }
1154                 }
1155             }
1156             setPeer(toolkit.createTKStage(this, isSecurityDialog(),
1157                     stageStyle, isPrimary(), getModality(), tkStage, rtl, acc));
1158             getPeer().setMinimumSize((int) Math.ceil(getMinWidth()),
1159                     (int) Math.ceil(getMinHeight()));
1160             getPeer().setMaximumSize((int) Math.floor(getMaxWidth()),
1161                     (int) Math.floor(getMaxHeight()));
1162             setPeerListener(new StagePeerListener(this, STAGE_ACCESSOR));
1163         }
1164     }
1165 
1166 
1167     /*
1168      * This can be replaced by listening for the onShown/onHidden events
1169      * Note: This method MUST only be called via its accessor method.
1170      */
1171     private void doVisibleChanged(boolean value) {
1172         if (value) {
1173             // Finish initialization
1174             TKStage peer = getPeer();
1175             peer.setImportant(isImportant());
1176             peer.setResizable(isResizable());
1177             peer.setFullScreen(isFullScreen());
1178             peer.setAlwaysOnTop(isAlwaysOnTop());
1179             peer.setIconified(isIconified());
1180             peer.setMaximized(isMaximized());
1181             peer.setTitle(getTitle());
1182 
1183             List<Object> platformImages = new ArrayList<Object>();
1184             for (Image icon : icons) {
1185                 platformImages.add(Toolkit.getImageAccessor().getPlatformImage(icon));
1186             }
1187             if (peer != null) {
1188                 peer.setIcons(platformImages);
1189             }
1190         }
1191 
1192         if (!value && inNestedEventLoop) {
1193             inNestedEventLoop = false;
1194             Toolkit.getToolkit().exitNestedEventLoop(this, null);
1195         }
1196     }
1197 
1198     /**
1199      * Bring the {@code Window} to the foreground.  If the {@code Window} is
1200      * already in the foreground there is no visible difference.
1201      */
1202     public void toFront() {
1203         if (getPeer() != null) {
1204             getPeer().toFront();
1205         }
1206     }
1207 
1208     /**
1209      * Send the {@code Window} to the background.  If the {@code Window} is
1210      * already in the background there is no visible difference.  This action
1211      * places this {@code Window} at the bottom of the stacking order on
1212      * platforms that support stacking.
1213      */
1214     public void toBack() {
1215         if (getPeer() != null) {
1216             getPeer().toBack();
1217         }
1218     }
1219 
1220     /**
1221      * Closes this {@code Stage}.
1222      * This call is equivalent to {@code hide()}.
1223      */
1224     public void close() {
1225         hide();
1226     }
1227 
1228     @Override
1229     Window getWindowOwner() {
1230         return getOwner();
1231     }
1232 
1233 
1234     private final ObjectProperty<KeyCombination> fullScreenExitCombination =
1235             new SimpleObjectProperty<KeyCombination>(this, "fullScreenExitCombination", null);
1236 
1237     /**
1238      * Specifies the KeyCombination that will allow the user to exit full screen
1239      * mode. A value of KeyCombination.NO_MATCH will not match any KeyEvent and
1240      * will make it so the user is not able to escape from Full Screen mode.
1241      * A value of null indicates that the default platform specific key combination
1242      * should be used.
1243      * <p>
1244      * An internal copy of this value is made when entering full-screen mode and will be
1245      * used to trigger the exit from the mode.
1246      * If a security manager is present, the application must have the
1247      * {@link javafx.util.FXPermission} "unrestrictedFullScreen" to modify the
1248      * exit key combination. If the application does not have permission, the
1249      * value of this property will be ignored, in which case the
1250      * default key combination will be used.
1251      * </p>
1252      * @param keyCombination the key combination to exit on
1253      * @since JavaFX 8.0
1254      */
1255     public final void setFullScreenExitKeyCombination(KeyCombination keyCombination) {
1256         fullScreenExitCombination.set(keyCombination);
1257     }
1258 
1259     /**
1260      * Get the current sequence used to exit Full Screen mode.
1261      * @return the current setting (null for system default)
1262      * @since JavaFX 8.0
1263      */
1264     public final KeyCombination getFullScreenExitKeyCombination() {
1265         return fullScreenExitCombination.get();
1266     }
1267 
1268     /**
1269      * Get the property for the Full Screen exit key combination.
1270      * @return the property.
1271      * @since JavaFX 8.0
1272      */
1273     public final ObjectProperty<KeyCombination> fullScreenExitKeyProperty() {
1274         return fullScreenExitCombination;
1275     }
1276 
1277     private final ObjectProperty<String> fullScreenExitHint =
1278             new SimpleObjectProperty<String>(this, "fullScreenExitHint", null);
1279 
1280     /**
1281      * Specifies the text to show when a user enters full screen mode, usually
1282      * used to indicate the way a user should go about exiting out of full
1283      * screen mode. A value of null will result in the default per-locale
1284      * message being displayed.
1285      * If set to the empty string, then no message will be displayed.
1286      * <p>
1287      * If a security manager is present, the application must have the
1288      * {@link javafx.util.FXPermission} "unrestrictedFullScreen" to modify the
1289      * exit hint. If the application does not have permission, the
1290      * value of this property will be ignored, in which case the
1291      * default message will be displayed.
1292      * </p>
1293      * @param value the string to be displayed.
1294      * @since JavaFX 8.0
1295      */
1296     public final void setFullScreenExitHint(String value) {
1297         fullScreenExitHint.set(value);
1298     }
1299 
1300     public final String getFullScreenExitHint() {
1301         return fullScreenExitHint.get();
1302     }
1303 
1304     public final ObjectProperty<String> fullScreenExitHintProperty() {
1305         return fullScreenExitHint;
1306     }
1307 }