modules/graphics/src/main/java/javafx/stage/Window.java
Print this page
@@ -26,14 +26,11 @@
package javafx.stage;
import java.security.AllPermission;
import java.security.AccessControlContext;
import java.security.AccessController;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.DoublePropertyBase;
import javafx.beans.property.ObjectProperty;
@@ -43,10 +40,11 @@
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;
@@ -56,19 +54,20 @@
import javafx.event.EventType;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import com.sun.javafx.util.Utils;
-import com.sun.javafx.util.WeakReferenceQueue;
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;
/**
* <p>
* A top level window within which a scene is hosted, and with which the user
@@ -106,29 +105,24 @@
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 getUIScale(Window window) {
- TKStage peer = window.impl_peer;
- return peer == null ? 1.0f : peer.getUIScale();
- }
-
- @Override
- public float getRenderScale(Window window) {
- TKStage peer = window.impl_peer;
- return peer == null ? 1.0f : peer.getRenderScale();
- }
-
- @Override
public ReadOnlyObjectProperty<Screen> screenProperty(Window window) {
return window.screenProperty();
}
@Override
@@ -287,10 +281,157 @@
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 {@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 outputScaleThreshold
+ */
+ 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 {@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 outputScaleThreshold
+ */
+ 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 {@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.
+ */
+ 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 {@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 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.
+ */
+ 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 {@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 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.
+ */
+ 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 {@code Stage} on the screen. Changing
* this attribute will move the {@code Stage} horizontally. Changing this
* attribute will not visually affect a {@code Stage} while
@@ -840,10 +981,11 @@
peerListener = new WindowPeerListener(Window.this);
}
// Setup listener for changes coming back from peer
impl_peer.setTKStageListener(peerListener);
+ updateOutputScales(impl_peer.getOutputScaleX(), impl_peer.getOutputScaleY());
// Register pulse listener
tk.addStageTkPulseListener(peerBoundsConfigurator);
if (getScene() != null) {
getScene().impl_initPeer();
@@ -1203,10 +1345,12 @@
/**
* 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;
@@ -1218,10 +1362,20 @@
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();
}
@@ -1274,30 +1428,41 @@
setDirty();
}
public void apply() {
if (dirty) {
- impl_peer.setBounds((float) (Double.isNaN(x) ? 0 : x),
- (float) (Double.isNaN(y) ? 0 : y),
- !Double.isNaN(x),
- !Double.isNaN(y),
- (float) windowWidth,
- (float) windowHeight,
- (float) clientWidth,
- (float) clientHeight,
- xGravity, yGravity);
-
+ // 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();
+ impl_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;