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