/*
* Copyright (c) 2010, 2019, 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.security.AccessControlContext;
import java.security.AccessController;
import java.util.HashMap;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.DoublePropertyBase;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.event.Event;
import javafx.event.EventDispatchChain;
import javafx.event.EventDispatcher;
import javafx.event.EventHandler;
import javafx.event.EventTarget;
import javafx.event.EventType;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import com.sun.javafx.util.Utils;
import com.sun.javafx.css.StyleManager;
import com.sun.javafx.stage.WindowEventDispatcher;
import com.sun.javafx.stage.WindowHelper;
import com.sun.javafx.stage.WindowPeerListener;
import com.sun.javafx.tk.TKPulseListener;
import com.sun.javafx.tk.TKScene;
import com.sun.javafx.tk.TKStage;
import com.sun.javafx.tk.Toolkit;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import static com.sun.javafx.FXPermissions.ACCESS_WINDOW_LIST_PERMISSION;
import com.sun.javafx.scene.NodeHelper;
import com.sun.javafx.scene.SceneHelper;
A top level window within which a scene is hosted, and with which the user interacts. A Window might be a Stage
, PopupWindow
, or other such top level window.
Window objects must be constructed and modified on the
JavaFX Application Thread.
The JavaFX Application Thread is created as part of the startup process for the JavaFX runtime. See the Application
class and the Platform.startup(Runnable)
method for more information.
Since: JavaFX 2.0
/**
* A top level window within which a scene is hosted, and with which the user
* interacts. A Window might be a {@link Stage}, {@link PopupWindow}, or other
* such top level window.
* <p>
* Window 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>
*
* @since JavaFX 2.0
*/
public class Window implements EventTarget {
A list of all the currently _showing_ windows. This is publicly accessible via the unmodifiableWindows wrapper.
/**
* A list of all the currently _showing_ windows. This is publicly accessible via the unmodifiableWindows wrapper.
*/
private static ObservableList<Window> windows = FXCollections.observableArrayList();
private static ObservableList<Window> unmodifiableWindows = FXCollections.unmodifiableObservableList(windows);
/*
* Store the singleton instance of the WindowHelper subclass corresponding
* to the subclass of this instance of Window
*/
private WindowHelper windowHelper = null;
static {
WindowHelper.setWindowAccessor(
new WindowHelper.WindowAccessor() {
@Override
public WindowHelper getHelper(Window window) {
return window.windowHelper;
}
@Override
public void setHelper(Window window, WindowHelper windowHelper) {
window.windowHelper = windowHelper;
}
@Override
public void doVisibleChanging(Window window, boolean visible) {
window.doVisibleChanging(visible);
}
@Override
public void doVisibleChanged(Window window, boolean visible) {
window.doVisibleChanged(visible);
}
@Override
public TKStage getPeer(Window window) {
return window.getPeer();
}
@Override
public void setPeer(Window window, TKStage peer) {
window.setPeer(peer);
}
@Override
public WindowPeerListener getPeerListener(Window window) {
return window.getPeerListener();
}
@Override
public void setPeerListener(Window window, WindowPeerListener peerListener) {
window.setPeerListener(peerListener);
}
@Override
public void setFocused(Window window, boolean value) {
window.setFocused(value);
}
/*
* Allow window peer listeners to directly change reported
* window location and size without changing the xExplicit,
* yExplicit, widthExplicit and heightExplicit values.
*/
@Override
public void notifyLocationChanged(
Window window, double x, double y) {
window.notifyLocationChanged(x, y);
}
@Override
public void notifySizeChanged(Window window,
double width,
double height) {
window.notifySizeChanged(width, height);
}
@Override
public void notifyScaleChanged(Window window,
double newOutputScaleX,
double newOutputScaleY) {
window.updateOutputScales(newOutputScaleX, newOutputScaleY);
}
@Override
public void notifyScreenChanged(Window window,
Object from,
Object to) {
window.notifyScreenChanged(from, to);
}
@Override
public float getPlatformScaleX(Window window) {
TKStage peer = window.getPeer();
return peer == null ? 1.0f : peer.getPlatformScaleX();
}
@Override
public float getPlatformScaleY(Window window) {
TKStage peer = window.getPeer();
return peer == null ? 1.0f : peer.getPlatformScaleY();
}
@Override
public ReadOnlyObjectProperty<Screen> screenProperty(Window window) {
return window.screenProperty();
}
@Override
public AccessControlContext getAccessControlContext(Window window) {
return window.acc;
}
});
}
Returns a list containing a reference to the currently showing JavaFX windows. The list is unmodifiable - attempting to modify this list will result in an UnsupportedOperationException
being thrown at runtime. Returns: A list containing all windows that are currently showing. Since: 9
/**
* Returns a list containing a reference to the currently showing JavaFX windows. The list is unmodifiable -
* attempting to modify this list will result in an {@link UnsupportedOperationException} being thrown at runtime.
*
* @return A list containing all windows that are currently showing.
* @since 9
*/
public static ObservableList<Window> getWindows() {
final SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkPermission(ACCESS_WINDOW_LIST_PERMISSION);
}
return unmodifiableWindows;
}
final AccessControlContext acc = AccessController.getContext();
protected Window() {
// necessary for WindowCloseRequestHandler
initializeInternalEventDispatcher();
WindowHelper.initHelper(this);
}
/*
* The listener that gets called by peer. It's also responsible for
* window size/location synchronization with the window peer, which
* occurs on every pulse.
*/
private WindowPeerListener peerListener;
WindowPeerListener getPeerListener() {
return peerListener;
}
void setPeerListener(WindowPeerListener peerListener) {
this.peerListener = peerListener;
}
/*
* The peer of this Stage. All external access should be
* made though getPeer(). Implementors note: Please ensure that this
* variable is defined *after* style and *before* the other variables so
* that style has been initialized prior to this call, and so that
* peer is initialized prior to subsequent initialization.
*/
private TKStage peer;
private TKBoundsConfigurator peerBoundsConfigurator =
new TKBoundsConfigurator();
/*
* Get Stage's peer
*/
TKStage getPeer() {
return peer;
}
void setPeer(TKStage peer) {
this.peer = peer;
}
Indicates if a user requested the window to be sized to match the scene
size.
/**
* Indicates if a user requested the window to be sized to match the scene
* size.
*/
private boolean sizeToScene = false;
Set the width and height of this Window to match the size of the content
of this Window's Scene.
/**
* Set the width and height of this Window to match the size of the content
* of this Window's Scene.
*/
public void sizeToScene() {
if (getScene() != null && peer != null) {
SceneHelper.preferredSize(getScene());
adjustSize(false);
} else {
// Remember the request to reapply it later if needed
sizeToScene = true;
}
}
private void adjustSize(boolean selfSizePriority) {
if (getScene() == null) {
return;
}
if (peer != null) {
double sceneWidth = getScene().getWidth();
double cw = (sceneWidth > 0) ? sceneWidth : -1;
double w = -1;
if (selfSizePriority && widthExplicit) {
w = getWidth();
} else if (cw <= 0) {
w = widthExplicit ? getWidth() : -1;
} else {
widthExplicit = false;
}
double sceneHeight = getScene().getHeight();
double ch = (sceneHeight > 0) ? sceneHeight : -1;
double h = -1;
if (selfSizePriority && heightExplicit) {
h = getHeight();
} else if (ch <= 0) {
h = heightExplicit ? getHeight() : -1;
} else {
heightExplicit = false;
}
peerBoundsConfigurator.setSize(w, h, cw, ch);
applyBounds();
}
}
private static final float CENTER_ON_SCREEN_X_FRACTION = 1.0f / 2;
private static final float CENTER_ON_SCREEN_Y_FRACTION = 1.0f / 3;
Sets x and y properties on this Window so that it is centered on the
curent screen.
The current screen is determined from the intersection of current window bounds and
visual bounds of all screens.
/**
* Sets x and y properties on this Window so that it is centered on the
* curent screen.
* The current screen is determined from the intersection of current window bounds and
* visual bounds of all screens.
*/
public void centerOnScreen() {
xExplicit = false;
yExplicit = false;
if (peer != null) {
Rectangle2D bounds = getWindowScreen().getVisualBounds();
double centerX =
bounds.getMinX() + (bounds.getWidth() - getWidth())
* CENTER_ON_SCREEN_X_FRACTION;
double centerY =
bounds.getMinY() + (bounds.getHeight() - getHeight())
* CENTER_ON_SCREEN_Y_FRACTION;
x.set(centerX);
y.set(centerY);
peerBoundsConfigurator.setLocation(centerX, centerY,
CENTER_ON_SCREEN_X_FRACTION,
CENTER_ON_SCREEN_Y_FRACTION);
applyBounds();
}
}
private void updateOutputScales(double sx, double sy) {
// We call updateRenderScales() before updating the property
// values so that an application can listen to the properties
// and set their own values overriding the default values we set.
updateRenderScales(sx, sy);
// Now set the properties and trigger any potential listeners.
outputScaleX.set(sx);
outputScaleY.set(sy);
}
void updateRenderScales(double sx, double sy) {
boolean forceInt = forceIntegerRenderScale.get();
if (!renderScaleX.isBound()) {
renderScaleX.set(forceInt ? Math.ceil(sx) : sx);
}
if (!renderScaleY.isBound()) {
renderScaleY.set(forceInt ? Math.ceil(sy) : sy);
}
}
The scale that the Window
will apply to horizontal scene coordinates in all stages of rendering and compositing the output to the screen or other destination device. This property is updated asynchronously by the system at various times including:
- Window creation
- At some point during moving a window to a new
Screen
which may be before or after the Screen
property is updated. - In response to a change in user preferences for output scaling.
See Also: Since: 9
/**
* The scale that the {@code Window} will apply to horizontal scene
* coordinates in all stages of rendering and compositing the output
* to the screen or other destination device.
* This property is updated asynchronously by the system at various
* times including:
* <ul>
* <li>Window creation
* <li>At some point during moving a window to a new {@code Screen}
* which may be before or after the {@link Screen} property is updated.
* <li>In response to a change in user preferences for output scaling.
* </ul>
*
* @see #renderScaleXProperty()
* @since 9
*/
private ReadOnlyDoubleWrapper outputScaleX =
new ReadOnlyDoubleWrapper(this, "outputScaleX", 1.0);
public final double getOutputScaleX() {
return outputScaleX.get();
}
public final ReadOnlyDoubleProperty outputScaleXProperty() {
return outputScaleX.getReadOnlyProperty();
}
The scale that the Window
will apply to vertical scene coordinates in all stages of rendering and compositing the output to the screen or other destination device. This property is updated asynchronously by the system at various times including:
- Window creation
- At some point during moving a window to a new
Screen
which may be before or after the Screen
property is updated. - In response to a change in user preferences for output scaling.
See Also: Since: 9
/**
* The scale that the {@code Window} will apply to vertical scene
* coordinates in all stages of rendering and compositing the output
* to the screen or other destination device.
* This property is updated asynchronously by the system at various
* times including:
* <ul>
* <li>Window creation
* <li>At some point during moving a window to a new {@code Screen}
* which may be before or after the {@link Screen} property is updated.
* <li>In response to a change in user preferences for output scaling.
* </ul>
*
* @see #renderScaleYProperty()
* @since 9
*/
private ReadOnlyDoubleWrapper outputScaleY =
new ReadOnlyDoubleWrapper(this, "outputScaleY", 1.0);
public final double getOutputScaleY() {
return outputScaleY.get();
}
public final ReadOnlyDoubleProperty outputScaleYProperty() {
return outputScaleY.getReadOnlyProperty();
}
Boolean property that controls whether only integer render scales are set by default by the system when there is a change in the associated output scale. The renderScale
properties will be updated directly and simultaneously with any changes in the associated outputScale
properties, but the values can be overridden by subsequent calls to the setRenderScale
setters or through appropriate use of binding. This property will not prevent setting non-integer scales directly using the renderScale
property object or the convenience setter method. See Also: @defaultValue false Since: 9
/**
* Boolean property that controls whether only integer render scales
* are set by default by the system when there is a change in the
* associated output scale.
* The {@code renderScale} properties will be updated directly and
* simultaneously with any changes in the associated {@code outputScale}
* properties, but the values can be overridden by subsequent calls to
* the {@code setRenderScale} setters or through appropriate use of
* binding.
* This property will not prevent setting non-integer scales
* directly using the {@code renderScale} property object or the
* convenience setter method.
*
* @defaultValue false
* @see #renderScaleXProperty()
* @see #renderScaleYProperty()
* @since 9
*/
private BooleanProperty forceIntegerRenderScale =
new SimpleBooleanProperty(this, "forceIntegerRenderScale", false) {
@Override
protected void invalidated() {
updateRenderScales(getOutputScaleX(),
getOutputScaleY());
}
};
public final void setForceIntegerRenderScale(boolean forced) {
forceIntegerRenderScale.set(forced);
}
public final boolean isForceIntegerRenderScale() {
return forceIntegerRenderScale.get();
}
public final BooleanProperty forceIntegerRenderScaleProperty() {
return forceIntegerRenderScale;
}
The horizontal scale that the Window
will use when rendering its Scene
to the rendering buffer. This property is automatically updated whenever there is a change in the outputScaleX
property and can be overridden either by calling setRenderScaleX()
in response to a listener on the outputScaleX
property or by binding it appropriately. See Also: @defaultValue outputScaleX Since: 9
/**
* The horizontal scale that the {@code Window} will use when rendering
* its {@code Scene} to the rendering buffer.
* This property is automatically updated whenever there is a change in
* the {@link #outputScaleXProperty() outputScaleX} property and can be overridden either by
* calling {@code setRenderScaleX()} in response to a listener on the
* {@code outputScaleX} property or by binding it appropriately.
*
* @defaultValue outputScaleX
* @see #outputScaleXProperty()
* @see #forceIntegerRenderScaleProperty()
* @since 9
*/
private DoubleProperty renderScaleX =
new SimpleDoubleProperty(this, "renderScaleX", 1.0) {
@Override
protected void invalidated() {
peerBoundsConfigurator.setRenderScaleX(get());
}
};
public final void setRenderScaleX(double scale) {
renderScaleX.set(scale);
}
public final double getRenderScaleX() {
return renderScaleX.get();
}
public final DoubleProperty renderScaleXProperty() {
return renderScaleX;
}
The vertical scale that the Window
will use when rendering its Scene
to the rendering buffer. This property is automatically updated whenever there is a change in the outputScaleY
property and can be overridden either by calling setRenderScaleY()
in response to a listener on the outputScaleY
property or by binding it appropriately. See Also: @defaultValue outputScaleY Since: 9
/**
* The vertical scale that the {@code Window} will use when rendering
* its {@code Scene} to the rendering buffer.
* This property is automatically updated whenever there is a change in
* the {@link #outputScaleYProperty() outputScaleY} property and can be overridden either by
* calling {@code setRenderScaleY()} in response to a listener on the
* {@code outputScaleY} property or by binding it appropriately.
*
* @defaultValue outputScaleY
* @see #outputScaleYProperty()
* @see #forceIntegerRenderScaleProperty()
* @since 9
*/
private DoubleProperty renderScaleY =
new SimpleDoubleProperty(this, "renderScaleY", 1.0) {
@Override
protected void invalidated() {
peerBoundsConfigurator.setRenderScaleY(get());
}
};
public final void setRenderScaleY(double scale) {
renderScaleY.set(scale);
}
public final double getRenderScaleY() {
return renderScaleY.get();
}
public final DoubleProperty renderScaleYProperty() {
return renderScaleY;
}
private boolean xExplicit = false;
The horizontal location of this Window
on the screen. Changing this attribute will move the Window
horizontally. If this Window
is an instance of Stage
, changing this attribute will not visually affect the Window
while fullScreen
is true, but will be honored by the Window
once fullScreen
becomes false. /**
* The horizontal location of this {@code Window} on the screen. Changing
* this attribute will move the {@code Window} horizontally. If this
* {@code Window} is an instance of {@code Stage}, changing this attribute
* will not visually affect the {@code Window} while
* {@link Stage#fullScreenProperty() fullScreen} is true, but will be honored
* by the {@code Window} once {@link Stage#fullScreenProperty() fullScreen}
* becomes false.
*/
private ReadOnlyDoubleWrapper x =
new ReadOnlyDoubleWrapper(this, "x", Double.NaN);
public final void setX(double value) {
setXInternal(value);
}
public final double getX() { return x.get(); }
public final ReadOnlyDoubleProperty xProperty() { return x.getReadOnlyProperty(); }
void setXInternal(double value) {
x.set(value);
peerBoundsConfigurator.setX(value, 0);
xExplicit = true;
}
private boolean yExplicit = false;
The vertical location of this Window
on the screen. Changing this attribute will move the Window
vertically. If this Window
is an instance of Stage
, changing this attribute will not visually affect the Window
while fullScreen
is true, but will be honored by the Window
once fullScreen
becomes false. /**
* The vertical location of this {@code Window} on the screen. Changing this
* attribute will move the {@code Window} vertically. If this
* {@code Window} is an instance of {@code Stage}, changing this attribute
* will not visually affect the {@code Window} while
* {@link Stage#fullScreenProperty() fullScreen} is true, but will be honored
* by the {@code Window} once {@link Stage#fullScreenProperty() fullScreen}
* becomes false.
*/
private ReadOnlyDoubleWrapper y =
new ReadOnlyDoubleWrapper(this, "y", Double.NaN);
public final void setY(double value) {
setYInternal(value);
}
public final double getY() { return y.get(); }
public final ReadOnlyDoubleProperty yProperty() { return y.getReadOnlyProperty(); }
void setYInternal(double value) {
y.set(value);
peerBoundsConfigurator.setY(value, 0);
yExplicit = true;
}
Notification from the windowing system that the window's position has
changed.
Params: - newX – the new window x position
- newY – the new window y position
/**
* Notification from the windowing system that the window's position has
* changed.
*
* @param newX the new window x position
* @param newY the new window y position
*/
void notifyLocationChanged(double newX, double newY) {
x.set(newX);
y.set(newY);
}
private boolean widthExplicit = false;
The width of this Window
. Changing this attribute will narrow or widen the width of the Window
. This value includes any and all decorations which may be added by the Operating System such as resizable frame handles. Typical applications will set the Scene
width instead. This Window
will take its width from the scene if it has never been set by the application. Resizing the window by end user does not count as a setting the width by the application. If this Window
is an instance of Stage
, changing this attribute will not visually affect the Window
while fullScreen
is true, but will be honored by the Window
once fullScreen
becomes false.
The property is read only because it can be changed externally
by the underlying platform and therefore must not be bindable.
/**
* The width of this {@code Window}. Changing this attribute will narrow or
* widen the width of the {@code Window}. This value includes any and all
* decorations which may be added by the Operating System such as resizable
* frame handles. Typical applications will set the {@link javafx.scene.Scene}
* width instead. This {@code Window} will take its width from the scene if
* it has never been set by the application. Resizing the window by end user
* does not count as a setting the width by the application. If this
* {@code Window} is an instance of {@code Stage}, changing this attribute
* will not visually affect the {@code Window} while
* {@link Stage#fullScreenProperty() fullScreen} is true, but will be honored
* by the {@code Window} once {@link Stage#fullScreenProperty() fullScreen}
* becomes false.
* <p>
* The property is read only because it can be changed externally
* by the underlying platform and therefore must not be bindable.
* </p>
*/
private ReadOnlyDoubleWrapper width =
new ReadOnlyDoubleWrapper(this, "width", Double.NaN);
public final void setWidth(double value) {
width.set(value);
peerBoundsConfigurator.setWindowWidth(value);
widthExplicit = true;
}
public final double getWidth() { return width.get(); }
public final ReadOnlyDoubleProperty widthProperty() { return width.getReadOnlyProperty(); }
private boolean heightExplicit = false;
The height of this Window
. Changing this attribute will shrink or heighten the height of the Window
. This value includes any and all decorations which may be added by the Operating System such as the title bar. Typical applications will set the Scene
height instead. This window will take its height from the scene if it has never been set by the application. Resizing this window by end user does not count as a setting the height by the application. If this Window
is an instance of Stage
, changing this attribute will not visually affect the Window
while fullScreen
is true, but will be honored by the Window
once fullScreen
becomes false.
The property is read only because it can be changed externally
by the underlying platform and therefore must not be bindable.
/**
* The height of this {@code Window}. Changing this attribute will shrink
* or heighten the height of the {@code Window}. This value includes any and all
* decorations which may be added by the Operating System such as the title
* bar. Typical applications will set the {@link javafx.scene.Scene} height
* instead. This window will take its height from the scene if it has never
* been set by the application. Resizing this window by end user does not
* count as a setting the height by the application. If this
* {@code Window} is an instance of {@code Stage}, changing this attribute
* will not visually affect the {@code Window} while
* {@link Stage#fullScreenProperty() fullScreen} is true, but will be honored
* by the {@code Window} once {@link Stage#fullScreenProperty() fullScreen}
* becomes false.
* <p>
* The property is read only because it can be changed externally
* by the underlying platform and therefore must not be bindable.
* </p>
*/
private ReadOnlyDoubleWrapper height =
new ReadOnlyDoubleWrapper(this, "height", Double.NaN);
public final void setHeight(double value) {
height.set(value);
peerBoundsConfigurator.setWindowHeight(value);
heightExplicit = true;
}
public final double getHeight() { return height.get(); }
public final ReadOnlyDoubleProperty heightProperty() { return height.getReadOnlyProperty(); }
Notification from the windowing system that the window's size has
changed.
Params: - newWidth – the new window width
- newHeight – the new window height
/**
* Notification from the windowing system that the window's size has
* changed.
*
* @param newWidth the new window width
* @param newHeight the new window height
*/
void notifySizeChanged(double newWidth, double newHeight) {
width.set(newWidth);
height.set(newHeight);
}
Whether or not this Window
has the keyboard or input focus.
The property is read only because it can be changed externally
by the underlying platform and therefore must not be bindable.
/**
* Whether or not this {@code Window} has the keyboard or input focus.
* <p>
* The property is read only because it can be changed externally
* by the underlying platform and therefore must not be bindable.
* </p>
*/
private ReadOnlyBooleanWrapper focused = new ReadOnlyBooleanWrapper() {
@Override protected void invalidated() {
focusChanged(get());
}
@Override
public Object getBean() {
return Window.this;
}
@Override
public String getName() {
return "focused";
}
};
final void setFocused(boolean value) { focused.set(value); }
Requests that this Window
get the input focus. /**
* Requests that this {@code Window} get the input focus.
*/
public final void requestFocus() {
if (peer != null) {
peer.requestFocus();
}
}
public final boolean isFocused() { return focused.get(); }
public final ReadOnlyBooleanProperty focusedProperty() { return focused.getReadOnlyProperty(); }
*
*
*
/*************************************************************************
* *
* *
* *
*************************************************************************/
private static final Object USER_DATA_KEY = new Object();
// A map containing a set of properties for this window
private ObservableMap<Object, Object> properties;
Returns an observable map of properties on this node for use primarily
by application developers.
Returns: an observable map of properties on this node for use primarily
by application developers Since: JavaFX 8u40
/**
* Returns an observable map of properties on this node for use primarily
* by application developers.
*
* @return an observable map of properties on this node for use primarily
* by application developers
*
* @since JavaFX 8u40
*/
public final ObservableMap<Object, Object> getProperties() {
if (properties == null) {
properties = FXCollections.observableMap(new HashMap<Object, Object>());
}
return properties;
}
Tests if Window has properties.
Returns: true if node has properties. Since: JavaFX 8u40
/**
* Tests if Window has properties.
* @return true if node has properties.
*
* @since JavaFX 8u40
*/
public boolean hasProperties() {
return properties != null && !properties.isEmpty();
}
Convenience method for setting a single Object property that can be retrieved at a later date. This is functionally equivalent to calling the getProperties().put(Object key, Object value) method. This can later be retrieved by calling getUserData()
. Params: - value – The value to be stored - this can later be retrieved by calling
getUserData()
.
Since: JavaFX 8u40
/**
* Convenience method for setting a single Object property that can be
* retrieved at a later date. This is functionally equivalent to calling
* the getProperties().put(Object key, Object value) method. This can later
* be retrieved by calling {@link Window#getUserData()}.
*
* @param value The value to be stored - this can later be retrieved by calling
* {@link Window#getUserData()}.
*
* @since JavaFX 8u40
*/
public void setUserData(Object value) {
getProperties().put(USER_DATA_KEY, value);
}
Returns a previously set Object property, or null if no such property has been set using the setUserData(Object)
method. Returns: The Object that was previously set, or null if no property
has been set or if null was set. Since: JavaFX 8u40
/**
* Returns a previously set Object property, or null if no such property
* has been set using the {@link Window#setUserData(java.lang.Object)} method.
*
* @return The Object that was previously set, or null if no property
* has been set or if null was set.
*
* @since JavaFX 8u40
*/
public Object getUserData() {
return getProperties().get(USER_DATA_KEY);
}
The Scene
to be rendered on this Window
. There can only be one Scene
on the Window
at a time, and a Scene
can only be on one Window
at a time. Setting a Scene
on a different Window
will cause the old Window
to lose the reference before the new one gains it. You may swap Scene
s on a Window
at any time, even if it is an instance of Stage
and with fullScreen
set to true. If the width or height of this Window
have never been set by the application, setting the scene will cause this Window
to take its width or height from that scene. Resizing this window by end user does not count as setting the width or height by the application. An IllegalStateException
is thrown if this property is set on a thread other than the JavaFX Application Thread. @defaultValue null
/**
* The {@code Scene} to be rendered on this {@code Window}. There can only
* be one {@code Scene} on the {@code Window} at a time, and a {@code Scene}
* can only be on one {@code Window} at a time. Setting a {@code Scene} on
* a different {@code Window} will cause the old {@code Window} to lose the
* reference before the new one gains it. You may swap {@code Scene}s on
* a {@code Window} at any time, even if it is an instance of {@code Stage}
* and with {@link Stage#fullScreenProperty() fullScreen} set to true.
* If the width or height of this {@code Window} have never been set by the
* application, setting the scene will cause this {@code Window} to take its
* width or height from that scene. Resizing this window by end user does
* not count as setting the width or height by the application.
*
* An {@link IllegalStateException} is thrown if this property is set
* on a thread other than the JavaFX Application Thread.
*
* @defaultValue null
*/
private SceneModel scene = new SceneModel();
protected void setScene(Scene value) { scene.set(value); }
public final Scene getScene() { return scene.get(); }
public final ReadOnlyObjectProperty<Scene> sceneProperty() { return scene.getReadOnlyProperty(); }
private final class SceneModel extends ReadOnlyObjectWrapper<Scene> {
private Scene oldScene;
@Override protected void invalidated() {
final Scene newScene = get();
if (oldScene == newScene) {
return;
}
if (peer != null) {
Toolkit.getToolkit().checkFxUserThread();
}
// First, detach scene peer from this window
updatePeerScene(null);
// Second, dispose scene peer
if (oldScene != null) {
SceneHelper.setWindow(oldScene, null);
StyleManager.getInstance().forget(oldScene);
}
if (newScene != null) {
final Window oldWindow = newScene.getWindow();
if (oldWindow != null) {
// if the new scene was previously set to a window
// we need to remove it from that window
// NOTE: can this "scene" property be bound?
oldWindow.setScene(null);
}
// Set the "window" on the new scene. This will also trigger
// scene's peer creation.
SceneHelper.setWindow(newScene, Window.this);
// Set scene impl on stage impl
updatePeerScene(SceneHelper.getPeer(newScene));
// Fix for RT-15432: we should update new Scene's stylesheets, if the
// window is already showing. For not yet shown windows, the update is
// performed in doVisibleChanging()
if (isShowing()) {
NodeHelper.reapplyCSS(newScene.getRoot());
if (!widthExplicit || !heightExplicit) {
SceneHelper.preferredSize(getScene());
adjustSize(true);
}
}
}
oldScene = newScene;
}
@Override
public Object getBean() {
return Window.this;
}
@Override
public String getName() {
return "scene";
}
private void updatePeerScene(final TKScene tkScene) {
if (peer != null) {
// Set scene impl on stage impl
peer.setScene(tkScene);
}
}
}
Defines the opacity of the Window
as a value between 0.0 and 1.0. The opacity is reflected across the Window
, its Decoration
and its Scene
content. On a JavaFX runtime platform that does not support opacity, assigning a value to this variable will have no visible difference. A Window
with 0% opacity is fully translucent. Typically, a Window
with 0% opacity will not receive any mouse events. @defaultValue 1.0
/**
* Defines the opacity of the {@code Window} as a value between 0.0 and 1.0.
* The opacity is reflected across the {@code Window}, its {@code Decoration}
* and its {@code Scene} content. On a JavaFX runtime platform that does not
* support opacity, assigning a value to this variable will have no
* visible difference. A {@code Window} with 0% opacity is fully translucent.
* Typically, a {@code Window} with 0% opacity will not receive any mouse
* events.
*
* @defaultValue 1.0
*/
private DoubleProperty opacity;
public final void setOpacity(double value) {
opacityProperty().set(value);
}
public final double getOpacity() {
return opacity == null ? 1.0 : opacity.get();
}
public final DoubleProperty opacityProperty() {
if (opacity == null) {
opacity = new DoublePropertyBase(1.0) {
@Override
protected void invalidated() {
if (peer != null) {
peer.setOpacity((float) get());
}
}
@Override
public Object getBean() {
return Window.this;
}
@Override
public String getName() {
return "opacity";
}
};
}
return opacity;
}
Called when there is an external request to close this Window
. The installed event handler can prevent window closing by consuming the received event. /**
* Called when there is an external request to close this {@code Window}.
* The installed event handler can prevent window closing by consuming the
* received event.
*/
private ObjectProperty<EventHandler<WindowEvent>> onCloseRequest;
public final void setOnCloseRequest(EventHandler<WindowEvent> value) {
onCloseRequestProperty().set(value);
}
public final EventHandler<WindowEvent> getOnCloseRequest() {
return (onCloseRequest != null) ? onCloseRequest.get() : null;
}
public final ObjectProperty<EventHandler<WindowEvent>>
onCloseRequestProperty() {
if (onCloseRequest == null) {
onCloseRequest = new ObjectPropertyBase<EventHandler<WindowEvent>>() {
@Override protected void invalidated() {
setEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, get());
}
@Override
public Object getBean() {
return Window.this;
}
@Override
public String getName() {
return "onCloseRequest";
}
};
}
return onCloseRequest;
}
Called just prior to the Window being shown.
/**
* Called just prior to the Window being shown.
*/
private ObjectProperty<EventHandler<WindowEvent>> onShowing;
public final void setOnShowing(EventHandler<WindowEvent> value) { onShowingProperty().set(value); }
public final EventHandler<WindowEvent> getOnShowing() {
return onShowing == null ? null : onShowing.get();
}
public final ObjectProperty<EventHandler<WindowEvent>> onShowingProperty() {
if (onShowing == null) {
onShowing = new ObjectPropertyBase<EventHandler<WindowEvent>>() {
@Override protected void invalidated() {
setEventHandler(WindowEvent.WINDOW_SHOWING, get());
}
@Override
public Object getBean() {
return Window.this;
}
@Override
public String getName() {
return "onShowing";
}
};
}
return onShowing;
}
Called just after the Window is shown.
/**
* Called just after the Window is shown.
*/
private ObjectProperty<EventHandler<WindowEvent>> onShown;
public final void setOnShown(EventHandler<WindowEvent> value) { onShownProperty().set(value); }
public final EventHandler<WindowEvent> getOnShown() {
return onShown == null ? null : onShown.get();
}
public final ObjectProperty<EventHandler<WindowEvent>> onShownProperty() {
if (onShown == null) {
onShown = new ObjectPropertyBase<EventHandler<WindowEvent>>() {
@Override protected void invalidated() {
setEventHandler(WindowEvent.WINDOW_SHOWN, get());
}
@Override
public Object getBean() {
return Window.this;
}
@Override
public String getName() {
return "onShown";
}
};
}
return onShown;
}
Called just prior to the Window being hidden.
/**
* Called just prior to the Window being hidden.
*/
private ObjectProperty<EventHandler<WindowEvent>> onHiding;
public final void setOnHiding(EventHandler<WindowEvent> value) { onHidingProperty().set(value); }
public final EventHandler<WindowEvent> getOnHiding() {
return onHiding == null ? null : onHiding.get();
}
public final ObjectProperty<EventHandler<WindowEvent>> onHidingProperty() {
if (onHiding == null) {
onHiding = new ObjectPropertyBase<EventHandler<WindowEvent>>() {
@Override protected void invalidated() {
setEventHandler(WindowEvent.WINDOW_HIDING, get());
}
@Override
public Object getBean() {
return Window.this;
}
@Override
public String getName() {
return "onHiding";
}
};
}
return onHiding;
}
Called just after the Window has been hidden. When the Window
is hidden, this event handler is invoked allowing the developer to clean up resources or perform other tasks when the Window
is closed. /**
* Called just after the Window has been hidden.
* When the {@code Window} is hidden, this event handler is invoked allowing
* the developer to clean up resources or perform other tasks when the
* {@link Window} is closed.
*/
private ObjectProperty<EventHandler<WindowEvent>> onHidden;
public final void setOnHidden(EventHandler<WindowEvent> value) { onHiddenProperty().set(value); }
public final EventHandler<WindowEvent> getOnHidden() {
return onHidden == null ? null : onHidden.get();
}
public final ObjectProperty<EventHandler<WindowEvent>> onHiddenProperty() {
if (onHidden == null) {
onHidden = new ObjectPropertyBase<EventHandler<WindowEvent>>() {
@Override protected void invalidated() {
setEventHandler(WindowEvent.WINDOW_HIDDEN, get());
}
@Override
public Object getBean() {
return Window.this;
}
@Override
public String getName() {
return "onHidden";
}
};
}
return onHidden;
}
Whether or not this Window
is showing (that is, open on the user's system). The Window might be "showing", yet the user might not be able to see it due to the Window being rendered behind another Window or due to the Window being positioned off the monitor. @defaultValue false
/**
* Whether or not this {@code Window} is showing (that is, open on the
* user's system). The Window might be "showing", yet the user might not
* be able to see it due to the Window being rendered behind another Window
* or due to the Window being positioned off the monitor.
*
* @defaultValue false
*/
private ReadOnlyBooleanWrapper showing = new ReadOnlyBooleanWrapper() {
private boolean oldVisible;
@Override protected void invalidated() {
final boolean newVisible = get();
if (oldVisible == newVisible) {
return;
}
if (!oldVisible && newVisible) {
fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_SHOWING));
} else {
fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_HIDING));
}
oldVisible = newVisible;
WindowHelper.visibleChanging(Window.this, newVisible);
if (newVisible) {
hasBeenVisible = true;
windows.add(Window.this);
} else {
windows.remove(Window.this);
}
Toolkit tk = Toolkit.getToolkit();
if (peer != null) {
if (newVisible) {
if (peerListener == null) {
peerListener = new WindowPeerListener(Window.this);
}
// Setup listener for changes coming back from peer
peer.setTKStageListener(peerListener);
// Register pulse listener
tk.addStageTkPulseListener(peerBoundsConfigurator);
if (getScene() != null) {
SceneHelper.initPeer(getScene());
peer.setScene(SceneHelper.getPeer(getScene()));
SceneHelper.preferredSize(getScene());
}
updateOutputScales(peer.getOutputScaleX(), peer.getOutputScaleY());
// updateOutputScales may cause an update to the render
// scales in many cases, but if the scale has not changed
// then the lazy render scale properties might think
// they do not need to send down the new values. In some
// cases we have been show()n with a brand new peer, so
// it is better to force the render scales into the PBC.
// This may usually be a NOP, but it is similar to the
// forced setSize and setLocation down below.
peerBoundsConfigurator.setRenderScaleX(getRenderScaleX());
peerBoundsConfigurator.setRenderScaleY(getRenderScaleY());
// Set peer bounds
if ((getScene() != null) && (!widthExplicit || !heightExplicit)) {
adjustSize(true);
} else {
peerBoundsConfigurator.setSize(
getWidth(), getHeight(), -1, -1);
}
if (!xExplicit && !yExplicit) {
centerOnScreen();
} else {
peerBoundsConfigurator.setLocation(getX(), getY(),
0, 0);
}
// set peer bounds before the window is shown
applyBounds();
peer.setOpacity((float)getOpacity());
peer.setVisible(true);
fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_SHOWN));
} else {
peer.setVisible(false);
// Call listener
fireEvent(new WindowEvent(Window.this, WindowEvent.WINDOW_HIDDEN));
if (getScene() != null) {
peer.setScene(null);
SceneHelper.disposePeer(getScene());
StyleManager.getInstance().forget(getScene());
}
// Remove toolkit pulse listener
tk.removeStageTkPulseListener(peerBoundsConfigurator);
// Remove listener for changes coming back from peer
peer.setTKStageListener(null);
// Notify peer
peer.close();
}
}
if (newVisible) {
tk.requestNextPulse();
}
WindowHelper.visibleChanged(Window.this, newVisible);
if (sizeToScene) {
if (newVisible) {
// Now that the visibleChanged has completed, the insets of the window
// might have changed (e.g. due to setResizable(false)). Reapply the
// sizeToScene() request if needed to account for the new insets.
sizeToScene();
}
// Reset the flag unconditionally upon visibility changes
sizeToScene = false;
}
}
@Override
public Object getBean() {
return Window.this;
}
@Override
public String getName() {
return "showing";
}
};
private void setShowing(boolean value) {
Toolkit.getToolkit().checkFxUserThread();
showing.set(value);
}
public final boolean isShowing() { return showing.get(); }
public final ReadOnlyBooleanProperty showingProperty() { return showing.getReadOnlyProperty(); }
// flag indicating whether this window has ever been made visible.
boolean hasBeenVisible = false;
Attempts to show this Window by setting visibility to true
Throws: - IllegalStateException – if this method is called on a thread
other than the JavaFX Application Thread.
/**
* Attempts to show this Window by setting visibility to true
*
* @throws IllegalStateException if this method is called on a thread
* other than the JavaFX Application Thread.
*/
protected void show() {
setShowing(true);
}
Attempts to hide this Window by setting the visibility to false.
Throws: - IllegalStateException – if this method is called on a thread
other than the JavaFX Application Thread.
/**
* Attempts to hide this Window by setting the visibility to false.
*
* @throws IllegalStateException if this method is called on a thread
* other than the JavaFX Application Thread.
*/
public void hide() {
setShowing(false);
}
/*
* 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 visible) {
if (visible && (getScene() != null)) {
NodeHelper.reapplyCSS(getScene().getRoot());
}
}
/*
* 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 visible) {
assert peer != null;
if (!visible) {
peerListener = null;
peer = null;
}
}
// PENDING_DOC_REVIEW
Specifies the event dispatcher for this node. The default event dispatcher sends the received events to the registered event handlers and filters. When replacing the value with a new EventDispatcher
, the new dispatcher should forward events to the replaced dispatcher to maintain the node's default event handling behavior. /**
* Specifies the event dispatcher for this node. The default event
* dispatcher sends the received events to the registered event handlers and
* filters. When replacing the value with a new {@code EventDispatcher},
* the new dispatcher should forward events to the replaced dispatcher
* to maintain the node's default event handling behavior.
*/
private ObjectProperty<EventDispatcher> eventDispatcher;
public final void setEventDispatcher(EventDispatcher value) {
eventDispatcherProperty().set(value);
}
public final EventDispatcher getEventDispatcher() {
return eventDispatcherProperty().get();
}
public final ObjectProperty<EventDispatcher> eventDispatcherProperty() {
initializeInternalEventDispatcher();
return eventDispatcher;
}
private WindowEventDispatcher internalEventDispatcher;
// PENDING_DOC_REVIEW
Registers an event handler to this node. The handler is called when the node receives an Event
of the specified type during the bubbling phase of event delivery. Params: - eventType – the type of the events to receive by the handler
- eventHandler – the handler to register
Type parameters: - <T> – the specific event class of the handler
Throws: - NullPointerException – if the event type or handler is null
/**
* Registers an event handler to this node. The handler is called when the
* node receives an {@code Event} of the specified type during the bubbling
* phase of event delivery.
*
* @param <T> the specific event class of the handler
* @param eventType the type of the events to receive by the handler
* @param eventHandler the handler to register
* @throws NullPointerException if the event type or handler is null
*/
public final <T extends Event> void addEventHandler(
final EventType<T> eventType,
final EventHandler<? super T> eventHandler) {
getInternalEventDispatcher().getEventHandlerManager()
.addEventHandler(eventType, eventHandler);
}
// PENDING_DOC_REVIEW
Unregisters a previously registered event handler from this node. One
handler might have been registered for different event types, so the
caller needs to specify the particular event type from which to
unregister the handler.
Params: - eventType – the event type from which to unregister
- eventHandler – the handler to unregister
Type parameters: - <T> – the specific event class of the handler
Throws: - NullPointerException – if the event type or handler is null
/**
* Unregisters a previously registered event handler from this node. One
* handler might have been registered for different event types, so the
* caller needs to specify the particular event type from which to
* unregister the handler.
*
* @param <T> the specific event class of the handler
* @param eventType the event type from which to unregister
* @param eventHandler the handler to unregister
* @throws NullPointerException if the event type or handler is null
*/
public final <T extends Event> void removeEventHandler(
final EventType<T> eventType,
final EventHandler<? super T> eventHandler) {
getInternalEventDispatcher().getEventHandlerManager()
.removeEventHandler(eventType,
eventHandler);
}
// PENDING_DOC_REVIEW
Registers an event filter to this node. The filter is called when the node receives an Event
of the specified type during the capturing phase of event delivery. Params: - eventType – the type of the events to receive by the filter
- eventFilter – the filter to register
Type parameters: - <T> – the specific event class of the filter
Throws: - NullPointerException – if the event type or filter is null
/**
* Registers an event filter to this node. The filter is called when the
* node receives an {@code Event} of the specified type during the capturing
* phase of event delivery.
*
* @param <T> the specific event class of the filter
* @param eventType the type of the events to receive by the filter
* @param eventFilter the filter to register
* @throws NullPointerException if the event type or filter is null
*/
public final <T extends Event> void addEventFilter(
final EventType<T> eventType,
final EventHandler<? super T> eventFilter) {
getInternalEventDispatcher().getEventHandlerManager()
.addEventFilter(eventType, eventFilter);
}
// PENDING_DOC_REVIEW
Unregisters a previously registered event filter from this node. One
filter might have been registered for different event types, so the
caller needs to specify the particular event type from which to
unregister the filter.
Params: - eventType – the event type from which to unregister
- eventFilter – the filter to unregister
Type parameters: - <T> – the specific event class of the filter
Throws: - NullPointerException – if the event type or filter is null
/**
* Unregisters a previously registered event filter from this node. One
* filter might have been registered for different event types, so the
* caller needs to specify the particular event type from which to
* unregister the filter.
*
* @param <T> the specific event class of the filter
* @param eventType the event type from which to unregister
* @param eventFilter the filter to unregister
* @throws NullPointerException if the event type or filter is null
*/
public final <T extends Event> void removeEventFilter(
final EventType<T> eventType,
final EventHandler<? super T> eventFilter) {
getInternalEventDispatcher().getEventHandlerManager()
.removeEventFilter(eventType, eventFilter);
}
Sets the handler to use for this event type. There can only be one such handler
specified at a time. This handler is guaranteed to be called first. This is
used for registering the user-defined onFoo event handlers.
Params: - eventType – the event type to associate with the given eventHandler
- eventHandler – the handler to register, or null to unregister
Type parameters: - <T> – the specific event class of the handler
Throws: - NullPointerException – if the event type is null
/**
* Sets the handler to use for this event type. There can only be one such handler
* specified at a time. This handler is guaranteed to be called first. This is
* used for registering the user-defined onFoo event handlers.
*
* @param <T> the specific event class of the handler
* @param eventType the event type to associate with the given eventHandler
* @param eventHandler the handler to register, or null to unregister
* @throws NullPointerException if the event type is null
*/
protected final <T extends Event> void setEventHandler(
final EventType<T> eventType,
final EventHandler<? super T> eventHandler) {
getInternalEventDispatcher().getEventHandlerManager()
.setEventHandler(eventType, eventHandler);
}
WindowEventDispatcher getInternalEventDispatcher() {
initializeInternalEventDispatcher();
return internalEventDispatcher;
}
private void initializeInternalEventDispatcher() {
if (internalEventDispatcher == null) {
internalEventDispatcher = createInternalEventDispatcher();
eventDispatcher = new SimpleObjectProperty<EventDispatcher>(
this,
"eventDispatcher",
internalEventDispatcher);
}
}
WindowEventDispatcher createInternalEventDispatcher() {
return new WindowEventDispatcher(this);
}
Fires the specified event.
This method must be called on the FX user thread.
Params: - event – the event to fire
/**
* Fires the specified event.
* <p>
* This method must be called on the FX user thread.
*
* @param event the event to fire
*/
public final void fireEvent(Event event) {
Event.fireEvent(this, event);
}
// PENDING_DOC_REVIEW
Construct an event dispatch chain for this window.
Params: - tail – the initial chain to build from
Returns: the resulting event dispatch chain for this window
/**
* Construct an event dispatch chain for this window.
*
* @param tail the initial chain to build from
* @return the resulting event dispatch chain for this window
*/
@Override
public EventDispatchChain buildEventDispatchChain(
EventDispatchChain tail) {
if (eventDispatcher != null) {
final EventDispatcher eventDispatcherValue = eventDispatcher.get();
if (eventDispatcherValue != null) {
tail = tail.prepend(eventDispatcherValue);
}
}
return tail;
}
private int focusGrabCounter;
void increaseFocusGrabCounter() {
if ((++focusGrabCounter == 1) && (peer != null) && isFocused()) {
peer.grabFocus();
}
}
void decreaseFocusGrabCounter() {
if ((--focusGrabCounter == 0) && (peer != null)) {
peer.ungrabFocus();
}
}
private void focusChanged(final boolean newIsFocused) {
if ((focusGrabCounter > 0) && (peer != null) && newIsFocused) {
peer.grabFocus();
}
}
final void applyBounds() {
peerBoundsConfigurator.apply();
}
Window getWindowOwner() {
return null;
}
private Screen getWindowScreen() {
Window window = this;
do {
if (!Double.isNaN(window.getX())
&& !Double.isNaN(window.getY())
&& !Double.isNaN(window.getWidth())
&& !Double.isNaN(window.getHeight())) {
return Utils.getScreenForRectangle(
new Rectangle2D(window.getX(),
window.getY(),
window.getWidth(),
window.getHeight()));
}
window = window.getWindowOwner();
} while (window != null);
return Screen.getPrimary();
}
private final ReadOnlyObjectWrapper<Screen> screen = new ReadOnlyObjectWrapper<>(Screen.getPrimary());
private ReadOnlyObjectProperty<Screen> screenProperty() { return screen.getReadOnlyProperty(); }
private void notifyScreenChanged(Object from, Object to) {
screen.set(Screen.getScreenForNative(to));
}
Caches all requested bounds settings and applies them at once during
the next pulse.
/**
* Caches all requested bounds settings and applies them at once during
* the next pulse.
*/
private final class TKBoundsConfigurator implements TKPulseListener {
private double renderScaleX;
private double renderScaleY;
private double x;
private double y;
private float xGravity;
private float yGravity;
private double windowWidth;
private double windowHeight;
private double clientWidth;
private double clientHeight;
private boolean dirty;
public TKBoundsConfigurator() {
reset();
}
public void setRenderScaleX(final double renderScaleX) {
this.renderScaleX = renderScaleX;
setDirty();
}
public void setRenderScaleY(final double renderScaleY) {
this.renderScaleY = renderScaleY;
setDirty();
}
public void setX(final double x, final float xGravity) {
this.x = x;
this.xGravity = xGravity;
setDirty();
}
public void setY(final double y, final float yGravity) {
this.y = y;
this.yGravity = yGravity;
setDirty();
}
public void setWindowWidth(final double windowWidth) {
this.windowWidth = windowWidth;
setDirty();
}
public void setWindowHeight(final double windowHeight) {
this.windowHeight = windowHeight;
setDirty();
}
public void setClientWidth(final double clientWidth) {
this.clientWidth = clientWidth;
setDirty();
}
public void setClientHeight(final double clientHeight) {
this.clientHeight = clientHeight;
setDirty();
}
public void setLocation(final double x,
final double y,
final float xGravity,
final float yGravity) {
this.x = x;
this.y = y;
this.xGravity = xGravity;
this.yGravity = yGravity;
setDirty();
}
public void setSize(final double windowWidth,
final double windowHeight,
final double clientWidth,
final double clientHeight) {
this.windowWidth = windowWidth;
this.windowHeight = windowHeight;
this.clientWidth = clientWidth;
this.clientHeight = clientHeight;
setDirty();
}
public void apply() {
if (dirty) {
if (peer == null) {
reset();
return;
}
// Snapshot values and then reset() before we call down
// as we may end up with recursive calls back up with
// new values that must be recorded as dirty.
boolean xSet = !Double.isNaN(x);
float newX = xSet ? (float) x : 0f;
boolean ySet = !Double.isNaN(y);
float newY = ySet ? (float) y : 0f;
float newWW = (float) windowWidth;
float newWH = (float) windowHeight;
float newCW = (float) clientWidth;
float newCH = (float) clientHeight;
float newXG = xGravity;
float newYG = yGravity;
float newRX = (float) renderScaleX;
float newRY = (float) renderScaleY;
reset();
peer.setBounds(newX, newY, xSet, ySet,
newWW, newWH, newCW, newCH,
newXG, newYG,
newRX, newRY);
}
}
@Override
public void pulse() {
apply();
}
private void reset() {
renderScaleX = 0.0;
renderScaleY = 0.0;
x = Double.NaN;
y = Double.NaN;
xGravity = 0;
yGravity = 0;
windowWidth = -1;
windowHeight = -1;
clientWidth = -1;
clientHeight = -1;
dirty = false;
}
private void setDirty() {
if (!dirty) {
Toolkit.getToolkit().requestNextPulse();
dirty = true;
}
}
}
}