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 }