Java tutorial
/* * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javafx.stage; import java.util.ArrayList; import java.util.List; import javafx.application.Platform; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.StringProperty; import javafx.beans.property.StringPropertyBase; import javafx.collections.ListChangeListener.Change; import javafx.collections.ObservableList; import javafx.geometry.NodeOrientation; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.scene.input.KeyCombination; import com.sun.javafx.collections.VetoableListDecorator; import com.sun.javafx.collections.TrackableObservableList; import com.sun.javafx.scene.SceneHelper; import com.sun.javafx.stage.StageHelper; import com.sun.javafx.stage.StagePeerListener; import com.sun.javafx.tk.TKStage; import com.sun.javafx.tk.Toolkit; import static com.sun.javafx.FXPermissions.CREATE_TRANSPARENT_WINDOW_PERMISSION; import javafx.beans.NamedArg; import javafx.beans.property.DoubleProperty; import javafx.beans.property.DoublePropertyBase; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; /** * The JavaFX {@code Stage} class is the top level JavaFX container. * The primary Stage is constructed by the platform. Additional Stage * objects may be constructed by the application. * * <p> * Stage objects must be constructed and modified on the * JavaFX Application Thread. * </p> * <p> * The JavaFX Application Thread is created as part of the startup process for * the JavaFX runtime. See the {@link javafx.application.Application} class and * the {@link Platform#startup(Runnable)} method for more information. * </p> * <p> * Many of the {@code Stage} properties are read only because they can * be changed externally by the underlying platform and therefore must * not be bindable. * </p> * * <p><b>Style</b></p> * <p> * A stage has one of the following styles: * <ul> * <li>{@link StageStyle#DECORATED} - a stage with a solid white background and * platform decorations.</li> * <li>{@link StageStyle#UNDECORATED} - a stage with a solid white background * and no decorations.</li> * <li>{@link StageStyle#TRANSPARENT} - a stage with a transparent background * and no decorations.</li> * <li>{@link StageStyle#UTILITY} - a stage with a solid white background and * minimal platform decorations.</li> * </ul> * <p>The style must be initialized before the stage is made visible.</p> * <p>On some platforms decorations might not be available. For example, on * some mobile or embedded devices. In these cases a request for a DECORATED or * UTILITY window will be accepted, but no decorations will be shown. </p> * * <p><b>Owner</b></p> * <p> * A stage can optionally have an owner Window. * When a window is a stage's owner, it is said to be the parent of that stage. * <p> * Owned Stages are tied to the parent Window. * An owned stage will always be on top of its parent window. * When a parent window is closed or iconified, then all owned windows will be affected as well. * Owned Stages cannot be independantly iconified. * <p> * The owner must be initialized before the stage is made visible. * * <p><b>Modality</b></p> * <p> * A stage has one of the following modalities: * <ul> * <li>{@link Modality#NONE} - a stage that does not block any other window.</li> * <li>{@link Modality#WINDOW_MODAL} - a stage that blocks input events from * being delivered to all windows from its owner (parent) to its root. * Its root is the closest ancestor window without an owner.</li> * <li>{@link Modality#APPLICATION_MODAL} - a stage that blocks input events from * being delivered to all windows from the same application, except for those * from its child hierarchy.</li> * </ul> * * <p>When a window is blocked by a modal stage its Z-order relative to its ancestors * is preserved, and it receives no input events and no window activation events, * but continues to animate and render normally. * Note that showing a modal stage does not necessarily block the caller. The * {@link #show} method returns immediately regardless of the modality of the stage. * Use the {@link #showAndWait} method if you need to block the caller until * the modal stage is hidden (closed). * The modality must be initialized before the stage is made visible.</p> * * <p><b>Example:</b></p> * * <pre><code> import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.text.Font; import javafx.scene.text.Text; import javafx.stage.Stage; public class HelloWorld extends Application { {@literal @Override} public void start(Stage stage) { Text text = new Text(10, 40, "Hello World!"); text.setFont(new Font(40)); Scene scene = new Scene(new Group(text)); stage.setTitle("Welcome to JavaFX!"); stage.setScene(scene); stage.sizeToScene(); stage.show(); } public static void main(String[] args) { Application.launch(args); } } * </code></pre> * <p>produces the following on Windows:</p> * <p><img src="doc-files/Stage-win.png" alt="A visual rendering * of a JavaFX Stage on Windows"></p> * * <p>produces the following on Mac OSX:</p> * <p><img src="doc-files/Stage-mac.png" alt="A visual rendering * of a JavaFX Stage on Mac OSX"></p> * * <p>produces the following on Linux:</p> * <p><img src="doc-files/Stage-linux.png" alt="A visual rendering * of a JavaFX Stage on Linux"></p> * @since JavaFX 2.0 */ public class Stage extends Window { private boolean inNestedEventLoop = false; static { StageHelper.setStageAccessor(new StageHelper.StageAccessor() { @Override public void doVisibleChanging(Window window, boolean visible) { ((Stage) window).doVisibleChanging(visible); } @Override public void doVisibleChanged(Window window, boolean visible) { ((Stage) window).doVisibleChanged(visible); } @Override public void initSecurityDialog(Stage stage, boolean securityDialog) { stage.initSecurityDialog(securityDialog); } @Override public void setPrimary(Stage stage, boolean primary) { stage.setPrimary(primary); } @Override public void setImportant(Stage stage, boolean important) { stage.setImportant(important); } }); } private static final StagePeerListener.StageAccessor STAGE_ACCESSOR = new StagePeerListener.StageAccessor() { @Override public void setIconified(Stage stage, boolean iconified) { stage.iconifiedPropertyImpl().set(iconified); } @Override public void setMaximized(Stage stage, boolean maximized) { stage.maximizedPropertyImpl().set(maximized); } @Override public void setResizable(Stage stage, boolean resizable) { ((ResizableProperty) stage.resizableProperty()).setNoInvalidate(resizable); } @Override public void setFullScreen(Stage stage, boolean fs) { stage.fullScreenPropertyImpl().set(fs); } @Override public void setAlwaysOnTop(Stage stage, boolean aot) { stage.alwaysOnTopPropertyImpl().set(aot); } }; /** * Creates a new instance of decorated {@code Stage}. * * @throws IllegalStateException if this constructor is called on a thread * other than the JavaFX Application Thread. */ public Stage() { this(StageStyle.DECORATED); } /** * Creates a new instance of {@code Stage}. * * @param style The style of the {@code Stage} * * @throws IllegalStateException if this constructor is called on a thread * other than the JavaFX Application Thread. */ public Stage(@NamedArg(value = "style", defaultValue = "DECORATED") StageStyle style) { super(); Toolkit.getToolkit().checkFxUserThread(); // Set the style initStyle(style); StageHelper.initHelper(this); } /** * Specify the scene to be used on this stage. */ @Override final public void setScene(Scene value) { Toolkit.getToolkit().checkFxUserThread(); super.setScene(value); } /** * {@inheritDoc} */ @Override public final void show() { super.show(); } private boolean primary = false; //////////////////////////////////////////////////////////////////// // Flag indicating that this stage is being used to show a security dialog private boolean securityDialog = false; /** * Sets a flag indicating that this stage is used for a security dialog and * must always be on top. If set, this will cause the window to be always * on top, regardless of the setting of the alwaysOnTop property, and * whether or not permissions are granted when the dialog is shown. * NOTE: this flag must be set prior to showing the stage the first time. * * @param securityDialog flag indicating that this Stage is being used to * show a security dialog that should be always-on-top * * @throws IllegalStateException if this property is set after the stage * has ever been made visible. * * @defaultValue false */ final void initSecurityDialog(boolean securityDialog) { if (hasBeenVisible) { throw new IllegalStateException("Cannot set securityDialog once stage has been set visible"); } this.securityDialog = securityDialog; } /** * Returns the state of the securityDialog flag. * * @return a flag indicating whether or not this is a security dialog */ final boolean isSecurityDialog() { return securityDialog; } /* * Sets this stage to be the primary stage. */ void setPrimary(boolean primary) { this.primary = primary; } /* * Returns whether this stage is the primary stage. * * @return true if this stage is the primary stage for the application. */ boolean isPrimary() { return primary; } private boolean important = true; /* * Sets a flag indicating whether this stage is an "important" window for * the purpose of determining whether the application is idle and should * exit. The application is considered finished when the last important * window is closed. */ void setImportant(boolean important) { this.important = important; } private boolean isImportant() { return important; } /** * Shows this stage and waits for it to be hidden (closed) before returning * to the caller. This method temporarily blocks processing of the current * event, and starts a nested event loop to handle other events. * This method must be called on the FX Application thread. * <p> * A Stage is hidden (closed) by one of the following means: * <ul> * <li>the application calls the {@link #hide} or {@link #close} method on * this stage</li> * <li>this stage has a non-null owner window, and its owner is closed</li> * <li>the user closes the window via the window system (for example, * by pressing the close button in the window decoration)</li> * </ul> * * <p> * After the Stage is hidden, and the application has returned from the * event handler to the event loop, the nested event loop terminates * and this method returns to the caller. * </p> * <p> * For example, consider the following sequence of operations for different * event handlers, assumed to execute in the order shown below: * <pre>void evtHander1(...) { * stage1.showAndWait(); * doSomethingAfterStage1Closed(...) * } * * void evtHander2(...) { * stage1.hide(); * doSomethingElseHere(...) * }</pre> * evtHandler1 will block at the call to showAndWait. It will resume execution * after stage1 is hidden and the current event handler, in this case evtHandler2, * returns to the event loop. This means that doSomethingElseHere will * execute before doSomethingAfterStage1Closed. * * <p> * More than one stage may be shown with showAndWait. Each call * will start a new nested event loop. The stages may be hidden in any order, * but a particular nested event loop (and thus the showAndWait method * for the associated stage) will only terminate after all inner event loops * have also terminated. * </p> * <p> * For example, consider the following sequence of operations for different * event handlers, assumed to execute in the order shown below: * <pre>void evtHander1() { * stage1.showAndWait(); * doSomethingAfterStage1Closed(...) * } * * void evtHander2() { * stage2.showAndWait(); * doSomethingAfterStage2Closed(...) * } * * void evtHander3() { * stage1.hide(); * doSomethingElseHere(...) * } * * void evtHander4() { * stage2.hide(); * doSomethingElseHereToo(...) * }</pre> * evtHandler1 will block at the call to stage1.showAndWait, starting up * a nested event loop just like in the previous example. evtHandler2 will * then block at the call to stage2.showAndWait, starting up another (inner) * nested event loop. The first call to stage1.showAndWait will resume execution * after stage1 is hidden, but only after the inner nested event loop started * by stage2.showAndWait has terminated. This means that the call to * stage1.showAndWait won't return until after evtHandler2 has returned. * The order of execution is: stage1.showAndWait, stage2.showAndWait, * stage1.hide, doSomethingElseHere, stage2.hide, doSomethingElseHereToo, * doSomethingAfterStage2Closed, doSomethingAfterStage1Closed. * * <p> * This method must not be called on the primary stage or on a stage that * is already visible. * Additionally, it must either be called from an input event handler or * from the run method of a Runnable passed to * {@link javafx.application.Platform#runLater Platform.runLater}. * It must not be called during animation or layout processing. * </p> * * @throws IllegalStateException if this method is called on a thread * other than the JavaFX Application Thread. * @throws IllegalStateException if this method is called during * animation or layout processing. * @throws IllegalStateException if this method is called on the * primary stage. * @throws IllegalStateException if this stage is already showing. * @since JavaFX 2.2 */ public void showAndWait() { Toolkit.getToolkit().checkFxUserThread(); if (isPrimary()) { throw new IllegalStateException("Cannot call this method on primary stage"); } if (isShowing()) { throw new IllegalStateException("Stage already visible"); } if (!Toolkit.getToolkit().canStartNestedEventLoop()) { throw new IllegalStateException("showAndWait is not allowed during animation or layout processing"); } // TODO: file a new bug; the following assertion can fail if this // method is called from an event handler that is listening to a // WindowEvent.WINDOW_HIDING event. assert !inNestedEventLoop; show(); inNestedEventLoop = true; Toolkit.getToolkit().enterNestedEventLoop(this); } private StageStyle style; // default is set in constructor /** * Specifies the style for this stage. This must be done prior to making * the stage visible. The style is one of: StageStyle.DECORATED, * StageStyle.UNDECORATED, StageStyle.TRANSPARENT, or StageStyle.UTILITY. * * @param style the style for this stage. * * @throws IllegalStateException if this property is set after the stage * has ever been made visible. * * @defaultValue StageStyle.DECORATED */ public final void initStyle(StageStyle style) { if (hasBeenVisible) { throw new IllegalStateException("Cannot set style once stage has been set visible"); } this.style = style; } /** * Retrieves the style attribute for this stage. * * @return the stage style. */ public final StageStyle getStyle() { return style; } private Modality modality = Modality.NONE; /** * Specifies the modality for this stage. This must be done prior to making * the stage visible. The modality is one of: Modality.NONE, * Modality.WINDOW_MODAL, or Modality.APPLICATION_MODAL. * * @param modality the modality for this stage. * * @throws IllegalStateException if this property is set after the stage * has ever been made visible. * * @throws IllegalStateException if this stage is the primary stage. * * @defaultValue Modality.NONE */ public final void initModality(Modality modality) { if (hasBeenVisible) { throw new IllegalStateException("Cannot set modality once stage has been set visible"); } if (isPrimary()) { throw new IllegalStateException("Cannot set modality for the primary stage"); } this.modality = modality; } /** * Retrieves the modality attribute for this stage. * * @return the modality. */ public final Modality getModality() { return modality; } private Window owner = null; /** * Specifies the owner Window for this stage, or null for a top-level, * unowned stage. This must be done prior to making the stage visible. * * @param owner the owner for this stage. * * @throws IllegalStateException if this property is set after the stage * has ever been made visible. * * @throws IllegalStateException if this stage is the primary stage. * * @defaultValue null */ public final void initOwner(Window owner) { if (hasBeenVisible) { throw new IllegalStateException("Cannot set owner once stage has been set visible"); } if (isPrimary()) { throw new IllegalStateException("Cannot set owner for the primary stage"); } this.owner = owner; final Scene sceneValue = getScene(); if (sceneValue != null) { SceneHelper.parentEffectiveOrientationInvalidated(sceneValue); } } /** * Retrieves the owner Window for this stage, or null for an unowned stage. * * @return the owner Window. */ public final Window getOwner() { return owner; } /** * Specifies whether this {@code Stage} should be a full-screen, * undecorated window. * <p> * The implementation of full-screen mode is platform and profile-dependent. * </p> * <p> * When set to {@code true}, the {@code Stage} will attempt to enter * full-screen mode when visible. Set to {@code false} to return {@code Stage} * to windowed mode. * An {@link IllegalStateException} is thrown if this property is set * on a thread other than the JavaFX Application Thread. * </p> * <p> * The full-screen mode will be exited (and the {@code fullScreen} attribute * will be set to {@code false}) if the full-screen * {@code Stage} loses focus or if another {@code Stage} enters * full-screen mode on the same {@link Screen}. Note that a {@code Stage} * in full-screen mode can become invisible without losing its * full-screen status and will again enter full-screen mode when the * {@code Stage} becomes visible. * </p> * If the platform supports multiple screens an application can control * which {@code Screen} the Stage will enter full-screen mode on by * setting its position to be within the bounds of that {@code Screen} * prior to entering full-screen mode. * <p> * However once in full-screen mode, {@code Stage}'s {@code x}, {@code y}, * {@code width}, and {@code height} variables will continue to represent * the non-full-screen position and size of the window; the same for * {@code iconified}, {@code resizable}, {@code style}, and {@code * opacity}. If changes are made to any of these attributes while in * full-screen mode, upon exiting full-screen mode the {@code Stage} will * assume those attributes. * </p> * <p> * In case that more {@code Stage} modes are set simultaneously their order * of importance is {@code iconified}, fullScreen, {@code maximized} (from * strongest to weakest). * </p> * <p> * The property is read only because it can be changed externally * by the underlying platform and therefore must not be bindable. * </p> * * The user can unconditionally exit full-screen mode * at any time by pressing {@code ESC}. * <p> * If a security manager is present, the application must have the * {@link javafx.util.FXPermission} "unrestrictedFullScreen" in order * to enter full-screen mode with no restrictions. Applications without * permission will have the following restrictions: * </p> * <ul> * <li>Applications can only enter full-screen mode in response * to user input. More specifically, entering is allowed from mouse * ({@code Node.mousePressed/mouseReleased/mouseClicked}) or keyboard * ({@code Node.keyPressed/keyReleased/keyTyped}) event handlers. It is * not allowed to enter full-screen mode in response to {@code ESC} * key. Attempting to enter full-screen mode from any other context will * be ignored. * <p> * If {@code Stage} was constructed as full-screen but not visible * it will enter full-screen mode upon becoming visible, with the same * limitations to when this is allowed to happen as when setting * {@code fullScreen} to {@code true}. * </p> * </li> * <li> If the application was allowed to enter full-screen mode * it will have limited keyboard input. It will only receive KEY_PRESSED * and KEY_RELEASED events from the following keys: * {@code UP, DOWN, LEFT, RIGHT, SPACE, TAB, PAGE_UP, PAGE_DOWN, HOME, END, ENTER} * </li> * </ul> * @defaultValue false */ private ReadOnlyBooleanWrapper fullScreen; public final void setFullScreen(boolean value) { Toolkit.getToolkit().checkFxUserThread(); fullScreenPropertyImpl().set(value); if (getPeer() != null) getPeer().setFullScreen(value); } public final boolean isFullScreen() { return fullScreen == null ? false : fullScreen.get(); } public final ReadOnlyBooleanProperty fullScreenProperty() { return fullScreenPropertyImpl().getReadOnlyProperty(); } private ReadOnlyBooleanWrapper fullScreenPropertyImpl() { if (fullScreen == null) { fullScreen = new ReadOnlyBooleanWrapper(Stage.this, "fullScreen"); } return fullScreen; } /** * Defines the icon images to be used in the window decorations and when * minimized. The images should be different sizes of the same image and * the best size will be chosen, eg. 16x16, 32,32. * * @defaultValue empty */ private ObservableList<Image> icons = new VetoableListDecorator<Image>(new TrackableObservableList<Image>() { @Override protected void onChanged(Change<Image> c) { List<Object> platformImages = new ArrayList<Object>(); for (Image icon : icons) { platformImages.add(Toolkit.getImageAccessor().getPlatformImage(icon)); } if (getPeer() != null) { getPeer().setIcons(platformImages); } } }) { @Override protected void onProposedChange(final List<Image> toBeAddedIcons, int[] indices) { for (Image icon : toBeAddedIcons) { if (icon == null) { throw new NullPointerException("icon can not be null."); } } } }; /** * Gets the icon images to be used in the window decorations and when * minimized. The images should be different sizes of the same image and * the best size will be chosen, eg. 16x16, 32,32. * @return An observable list of icons of this window */ public final ObservableList<Image> getIcons() { return icons; } /** * Defines the title of the {@code Stage}. * * @defaultValue empty string */ private StringProperty title; public final void setTitle(String value) { titleProperty().set(value); } public final String getTitle() { return title == null ? null : title.get(); } public final StringProperty titleProperty() { if (title == null) { title = new StringPropertyBase() { @Override protected void invalidated() { if (getPeer() != null) { getPeer().setTitle(get()); } } @Override public Object getBean() { return Stage.this; } @Override public String getName() { return "title"; } }; } return title; } /** * Defines whether the {@code Stage} is iconified or not. * <p> * In case that more {@code Stage} modes are set simultaneously their order * of importance is iconified} {@code fullScreen}, {@code maximized} (from * strongest to weakest). * </p> * <p> * On some mobile and embedded platforms setting this property to true will * hide the {@code Stage} but not show an icon for it. * </p> * <p> * The property is read only because it can be changed externally * by the underlying platform and therefore must not be bindable. * </p> * * @defaultValue false */ private ReadOnlyBooleanWrapper iconified; public final void setIconified(boolean value) { iconifiedPropertyImpl().set(value); if (getPeer() != null) getPeer().setIconified(value); } public final boolean isIconified() { return iconified == null ? false : iconified.get(); } public final ReadOnlyBooleanProperty iconifiedProperty() { return iconifiedPropertyImpl().getReadOnlyProperty(); } private final ReadOnlyBooleanWrapper iconifiedPropertyImpl() { if (iconified == null) { iconified = new ReadOnlyBooleanWrapper(Stage.this, "iconified"); } return iconified; } /** * Defines whether the {@code Stage} is maximized or not. * <p> * In case that more {@code Stage} modes are set simultaneously their order * of importance is {@code iconified}, {@code fullScreen}, maximized (from * strongest to weakest). * </p> * <p> * The property is read only because it can be changed externally * by the underlying platform and therefore must not be bindable. * </p> * * @defaultValue false * @since JavaFX 8.0 */ private ReadOnlyBooleanWrapper maximized; public final void setMaximized(boolean value) { maximizedPropertyImpl().set(value); if (getPeer() != null) { getPeer().setMaximized(value); } } public final boolean isMaximized() { return maximized == null ? false : maximized.get(); } public final ReadOnlyBooleanProperty maximizedProperty() { return maximizedPropertyImpl().getReadOnlyProperty(); } private final ReadOnlyBooleanWrapper maximizedPropertyImpl() { if (maximized == null) { maximized = new ReadOnlyBooleanWrapper(Stage.this, "maximized"); } return maximized; } /** * Defines whether this {@code Stage} is kept on top of other windows. * <p> * If some other window is already always-on-top then the * relative order between these windows is unspecified (depends on * platform). * </p> * <p> * If a security manager is present, the application must have the * {@link javafx.util.FXPermission} "setWindowAlwaysOnTop" in order for * this property to have any effect. If the application does not have * permission, attempting to set this property will be ignored * and the property value will be restored to {@code false}. * </p> * <p> * The property is read only because it can be changed externally * by the underlying platform and therefore must not be bindable. * </p> * * @defaultValue false * @since JavaFX 8u20 */ private ReadOnlyBooleanWrapper alwaysOnTop; public final void setAlwaysOnTop(boolean value) { alwaysOnTopPropertyImpl().set(value); if (getPeer() != null) { getPeer().setAlwaysOnTop(value); } } public final boolean isAlwaysOnTop() { return alwaysOnTop == null ? false : alwaysOnTop.get(); } public final ReadOnlyBooleanProperty alwaysOnTopProperty() { return alwaysOnTopPropertyImpl().getReadOnlyProperty(); } private ReadOnlyBooleanWrapper alwaysOnTopPropertyImpl() { if (alwaysOnTop == null) { alwaysOnTop = new ReadOnlyBooleanWrapper(Stage.this, "alwaysOnTop"); } return alwaysOnTop; } /** * Defines whether the {@code Stage} is resizable or not by the user. * Programatically you may still change the size of the Stage. This is * a hint which allows the implementation to optionally make the Stage * resizable by the user. * <p> * <b>Warning:</b> Since 8.0 the property cannot be bound and will throw * {@code RuntimeException} on an attempt to do so. This is because * the setting of resizable is asynchronous on some systems or generally * might be set by the system / window manager. * <br> * Bidirectional binds are still allowed, as they don't block setting of the * property by the system. * * @defaultValue true */ private BooleanProperty resizable; public final void setResizable(boolean value) { resizableProperty().set(value); } public final boolean isResizable() { return resizable == null ? true : resizable.get(); } public final BooleanProperty resizableProperty() { if (resizable == null) { resizable = new ResizableProperty(); } return resizable; } //We cannot return ReadOnlyProperty in resizable, as this would be // backward incompatible. All we can do is to create this custom property // implementation that disallows binds private class ResizableProperty extends SimpleBooleanProperty { private boolean noInvalidate; public ResizableProperty() { super(Stage.this, "resizable", true); } void setNoInvalidate(boolean value) { noInvalidate = true; set(value); noInvalidate = false; } @Override protected void invalidated() { if (noInvalidate) { return; } if (getPeer() != null) { applyBounds(); getPeer().setResizable(get()); } } @Override public void bind(ObservableValue<? extends Boolean> rawObservable) { throw new RuntimeException("Resizable property cannot be bound"); } } /** * Defines the minimum width of this {@code Stage}. * * @defaultValue 0 * @since JavaFX 2.1 */ private DoubleProperty minWidth; public final void setMinWidth(double value) { minWidthProperty().set(value); } public final double getMinWidth() { return minWidth == null ? 0 : minWidth.get(); } public final DoubleProperty minWidthProperty() { if (minWidth == null) { minWidth = new DoublePropertyBase(0) { @Override protected void invalidated() { if (getPeer() != null) { getPeer().setMinimumSize((int) Math.ceil(get()), (int) Math.ceil(getMinHeight())); } if (getWidth() < getMinWidth()) { setWidth(getMinWidth()); } } @Override public Object getBean() { return Stage.this; } @Override public String getName() { return "minWidth"; } }; } return minWidth; } /** * Defines the minimum height of this {@code Stage}. * * @defaultValue 0 * @since JavaFX 2.1 */ private DoubleProperty minHeight; public final void setMinHeight(double value) { minHeightProperty().set(value); } public final double getMinHeight() { return minHeight == null ? 0 : minHeight.get(); } public final DoubleProperty minHeightProperty() { if (minHeight == null) { minHeight = new DoublePropertyBase(0) { @Override protected void invalidated() { if (getPeer() != null) { getPeer().setMinimumSize((int) Math.ceil(getMinWidth()), (int) Math.ceil(get())); } if (getHeight() < getMinHeight()) { setHeight(getMinHeight()); } } @Override public Object getBean() { return Stage.this; } @Override public String getName() { return "minHeight"; } }; } return minHeight; } /** * Defines the maximum width of this {@code Stage}. * * @defaultValue Double.MAX_VALUE * @since JavaFX 2.1 */ private DoubleProperty maxWidth; public final void setMaxWidth(double value) { maxWidthProperty().set(value); } public final double getMaxWidth() { return maxWidth == null ? Double.MAX_VALUE : maxWidth.get(); } public final DoubleProperty maxWidthProperty() { if (maxWidth == null) { maxWidth = new DoublePropertyBase(Double.MAX_VALUE) { @Override protected void invalidated() { if (getPeer() != null) { getPeer().setMaximumSize((int) Math.floor(get()), (int) Math.floor(getMaxHeight())); } if (getWidth() > getMaxWidth()) { setWidth(getMaxWidth()); } } @Override public Object getBean() { return Stage.this; } @Override public String getName() { return "maxWidth"; } }; } return maxWidth; } /** * Defines the maximum height of this {@code Stage}. * * @defaultValue Double.MAX_VALUE * @since JavaFX 2.1 */ private DoubleProperty maxHeight; public final void setMaxHeight(double value) { maxHeightProperty().set(value); } public final double getMaxHeight() { return maxHeight == null ? Double.MAX_VALUE : maxHeight.get(); } public final DoubleProperty maxHeightProperty() { if (maxHeight == null) { maxHeight = new DoublePropertyBase(Double.MAX_VALUE) { @Override protected void invalidated() { if (getPeer() != null) { getPeer().setMaximumSize((int) Math.floor(getMaxWidth()), (int) Math.floor(get())); } if (getHeight() > getMaxHeight()) { setHeight(getMaxHeight()); } } @Override public Object getBean() { return Stage.this; } @Override public String getName() { return "maxHeight"; } }; } return maxHeight; } /* * This can be replaced by listening for the onShowing/onHiding events * Note: This method MUST only be called via its accessor method. */ private void doVisibleChanging(boolean value) { Toolkit toolkit = Toolkit.getToolkit(); if (value && (getPeer() == null)) { // Setup the peer Window window = getOwner(); TKStage tkStage = (window == null ? null : window.getPeer()); Scene scene = getScene(); boolean rtl = scene != null && scene.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT; StageStyle stageStyle = getStyle(); if (stageStyle == StageStyle.TRANSPARENT) { final SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { try { securityManager.checkPermission(CREATE_TRANSPARENT_WINDOW_PERMISSION); } catch (final SecurityException e) { stageStyle = StageStyle.UNDECORATED; } } } setPeer(toolkit.createTKStage(this, isSecurityDialog(), stageStyle, isPrimary(), getModality(), tkStage, rtl, acc)); getPeer().setMinimumSize((int) Math.ceil(getMinWidth()), (int) Math.ceil(getMinHeight())); getPeer().setMaximumSize((int) Math.floor(getMaxWidth()), (int) Math.floor(getMaxHeight())); setPeerListener(new StagePeerListener(this, STAGE_ACCESSOR)); } } /* * This can be replaced by listening for the onShown/onHidden events * Note: This method MUST only be called via its accessor method. */ private void doVisibleChanged(boolean value) { if (value) { // Finish initialization TKStage peer = getPeer(); peer.setImportant(isImportant()); peer.setResizable(isResizable()); peer.setFullScreen(isFullScreen()); peer.setAlwaysOnTop(isAlwaysOnTop()); peer.setIconified(isIconified()); peer.setMaximized(isMaximized()); peer.setTitle(getTitle()); List<Object> platformImages = new ArrayList<Object>(); for (Image icon : icons) { platformImages.add(Toolkit.getImageAccessor().getPlatformImage(icon)); } if (peer != null) { peer.setIcons(platformImages); } } if (!value && inNestedEventLoop) { inNestedEventLoop = false; Toolkit.getToolkit().exitNestedEventLoop(this, null); } } /** * Bring the {@code Window} to the foreground. If the {@code Window} is * already in the foreground there is no visible difference. */ public void toFront() { if (getPeer() != null) { getPeer().toFront(); } } /** * Send the {@code Window} to the background. If the {@code Window} is * already in the background there is no visible difference. This action * places this {@code Window} at the bottom of the stacking order on * platforms that support stacking. */ public void toBack() { if (getPeer() != null) { getPeer().toBack(); } } /** * Closes this {@code Stage}. * This call is equivalent to {@code hide()}. */ public void close() { hide(); } @Override Window getWindowOwner() { return getOwner(); } private final ObjectProperty<KeyCombination> fullScreenExitCombination = new SimpleObjectProperty<KeyCombination>( this, "fullScreenExitCombination", null); /** * Specifies the KeyCombination that will allow the user to exit full screen * mode. A value of KeyCombination.NO_MATCH will not match any KeyEvent and * will make it so the user is not able to escape from Full Screen mode. * A value of null indicates that the default platform specific key combination * should be used. * <p> * An internal copy of this value is made when entering full-screen mode and will be * used to trigger the exit from the mode. * If a security manager is present, the application must have the * {@link javafx.util.FXPermission} "unrestrictedFullScreen" to modify the * exit key combination. If the application does not have permission, the * value of this property will be ignored, in which case the * default key combination will be used. * </p> * @param keyCombination the key combination to exit on * @since JavaFX 8.0 */ public final void setFullScreenExitKeyCombination(KeyCombination keyCombination) { fullScreenExitCombination.set(keyCombination); } /** * Get the current sequence used to exit Full Screen mode. * @return the current setting (null for system default) * @since JavaFX 8.0 */ public final KeyCombination getFullScreenExitKeyCombination() { return fullScreenExitCombination.get(); } /** * Get the property for the Full Screen exit key combination. * @return the property. * @since JavaFX 8.0 */ public final ObjectProperty<KeyCombination> fullScreenExitKeyProperty() { return fullScreenExitCombination; } private final ObjectProperty<String> fullScreenExitHint = new SimpleObjectProperty<String>(this, "fullScreenExitHint", null); /** * Specifies the text to show when a user enters full screen mode, usually * used to indicate the way a user should go about exiting out of full * screen mode. A value of null will result in the default per-locale * message being displayed. * If set to the empty string, then no message will be displayed. * <p> * If a security manager is present, the application must have the * {@link javafx.util.FXPermission} "unrestrictedFullScreen" to modify the * exit hint. If the application does not have permission, the * value of this property will be ignored, in which case the * default message will be displayed. * </p> * @param value the string to be displayed. * @since JavaFX 8.0 */ public final void setFullScreenExitHint(String value) { fullScreenExitHint.set(value); } public final String getFullScreenExitHint() { return fullScreenExitHint.get(); } public final ObjectProperty<String> fullScreenExitHintProperty() { return fullScreenExitHint; } }