javafx.stage.Stage Maven / Gradle / Ivy
/*
* Copyright (c) 2010, 2013, 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.beans.property.BooleanProperty;
import javafx.beans.property.BooleanPropertyBase;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.StringPropertyBase;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.geometry.NodeOrientation;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import com.sun.javafx.beans.annotations.Default;
import com.sun.javafx.collections.TrackableObservableList;
import com.sun.javafx.robot.impl.FXRobotHelper;
import com.sun.javafx.scene.SceneHelper;
import com.sun.javafx.stage.StageHelper;
import com.sun.javafx.stage.StagePeerListener;
import com.sun.javafx.tk.TKPulseListener;
import com.sun.javafx.tk.TKStage;
import com.sun.javafx.tk.Toolkit;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.DoublePropertyBase;
import javafx.beans.property.Property;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
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.
*
*
* Stage objects must be constructed and modified on the
* JavaFX Application Thread.
*
*
* 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.
*
*
* Style
*
* A stage has one of the following styles:
*
* - {@link StageStyle#DECORATED} - a stage with a solid white background and
* platform decorations.
* - {@link StageStyle#UNDECORATED} - a stage with a solid white background
* and no decorations.
* - {@link StageStyle#TRANSPARENT} - a stage with a transparent background
* and no decorations.
* - {@link StageStyle#UTILITY} - a stage with a solid white background and
* minimal platform decorations.
*
* The style must be initialized before the stage is made visible.
* 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.
*
* Owner
*
* 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.
* When a parent window is closed, all its descendant windows are closed.
* The same chained behavior applied for a parent window that is iconified.
* A stage will always be on top of its parent window.
* The owner must be initialized before the stage is made visible.
*
*
Modality
*
* A stage has one of the following modalities:
*
* - {@link Modality#NONE} - a stage that does not block any other window.
* - {@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.
* - {@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.
*
*
* 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.
*
* Example:
*
*
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 {
@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);
}
}
*
* produces the following on Windows:
*
*
* produces the following on Mac OSX:
*
*
* produces the following on Linux:
*
* @since JavaFX 2.0
*/
public class Stage extends Window {
private boolean inNestedEventLoop = false;
private static ObservableList stages = FXCollections.observableArrayList();
static {
FXRobotHelper.setStageAccessor(new FXRobotHelper.FXRobotStageAccessor() {
@Override public ObservableList getStages() {
return stages;
}
});
StageHelper.setStageAccessor(new StageHelper.StageAccessor() {
@Override public ObservableList getStages() {
return stages;
}
});
}
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);
}
};
/**
* 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(@Default("javafx.stage.StageStyle.DECORATED") StageStyle style) {
super();
Toolkit.getToolkit().checkFxUserThread();
// Set the style
initStyle(style);
}
/**
* Specify the scene to be used on this stage.
*/
@Override final public void setScene(Scene value) {
super.setScene(value);
}
/**
* @inheritDoc
*/
@Override public final void show() {
super.show();
}
private boolean primary = false;
/**
* sets this stage to be the primary stage.
* When run as an applet, this stage will appear in the broswer
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended for use and will be removed in the next version
*/
@Deprecated
public void impl_setPrimary(boolean primary) {
this.primary = primary;
}
/**
* Returns whether this stage is the primary stage.
* When run as an applet, the primary stage will appear in the broswer
*
* @return true if this stage is the primary stage for the application.
*/
boolean isPrimary() {
return primary;
}
/**
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended for use and will be removed in the next version
*/
@Deprecated
@Override
public String impl_getMXWindowType() {
return (primary) ? "PrimaryStage" : getClass().getSimpleName();
}
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.
* @treatAsPrivate implementation detail
* @deprecated This is an internal API that is not intended for use and will be removed in the next version
*/
@Deprecated
public void impl_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.
*
* A Stage is hidden (closed) by one of the following means:
*
* - the application calls the {@link #hide} or {@link #close} method on
* this stage
* - this stage has a non-null owner window, and its owner is closed
* - the user closes the window via the window system (for example,
* by pressing the close button in the window decoration)
*
*
*
*
* 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.
*
*
* For example, consider the following sequence of operations for different
* event handlers, assumed to execute in the order shown below:
*
void evtHander1(...) {
* stage1.showAndWait();
* doSomethingAfterStage1Closed(...)
* }
*
* void evtHander2(...) {
* stage1.hide();
* doSomethingElseHere(...)
* }
* 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.
*
*
*
* 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.
*
*
* For example, consider the following sequence of operations for different
* event handlers, assumed to execute in the order shown below:
*
*
void evtHander1() {
* stage1.showAndWait();
* doSomethingAfterStage1Closed(...)
* }
*
* void evtHander2() {
* stage2.showAndWait();
* doSomethingAfterStage2Closed(...)
* }
*
* void evtHander3() {
* stage1.hide();
* doSomethingElseHere(...)
* }
*
* void evtHander4() {
* stage2.hide();
* doSomethingElseHereToo(...)
* }
*
* 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.
*
*
*
* This method must not be called on the primary stage or on a stage that
* is already visible.
*
*
* @throws IllegalStateException if this method is called on a thread
* other than the JavaFX Application Thread.
* @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");
}
// 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.
*
* The implementation of full-screen mode is platform and profile-dependent.
*
*
* 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.
*
*
* 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.
*
* 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.
*
* 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.
*
*
* In case that more {@code Stage} modes are set simultaneously their order
* of importance is {@code iconified}, fullScreen, {@code maximized} (from
* strongest to weakest).
*
*
* The property is read only because it can be changed externally
* by the underlying platform and therefore must not be bindable.
*
*
* Notes regarding desktop profile implementation.
*
* For desktop profile the runtime will attempt to enter full-screen
* exclusive mode (FSEM) if such is supported by the platform and it is
* allowed for this application. If either is not the case a
* simulated full-screen window will be used instead; the window will be
* maximized, made undecorated if possible, and moved to the front.
*
* For desktop profile the user can unconditionally exit full-screen mode
* at any time by pressing {@code ESC}.
*
* There are differences in behavior between signed and unsigned
* applications. Signed applications are allowed to enter full-screen
* exclusive mode unrestricted while unsigned applications will
* have the following restrictions:
*
*
* - Applications can only enter FSEM 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 FSEM in response to {@code ESC}
* key. Attempting to enter FSEM from any other context will result in
* emulated full-screen mode.
*
* 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}.
*
*
* - If the application was allowed to enter FSEM
* 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}
*
*
* @defaultValue false
*/
private ReadOnlyBooleanWrapper fullScreen;
public final void setFullScreen(boolean value) {
Toolkit.getToolkit().checkFxUserThread();
fullScreenPropertyImpl().set(value);
if (impl_peer != null)
impl_peer.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 icons = new TrackableObservableList() {
@Override protected void onChanged(Change c) {
List