--- old/modules/graphics/src/main/java/javafx/stage/Window.java 2016-04-05 16:32:35.000000000 -0700 +++ new/modules/graphics/src/main/java/javafx/stage/Window.java 2016-04-05 16:32:35.000000000 -0700 @@ -28,10 +28,7 @@ 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; @@ -45,6 +42,7 @@ 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; @@ -58,7 +56,6 @@ 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; @@ -67,6 +64,8 @@ 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; /** @@ -108,6 +107,13 @@ } @Override + public void notifyScaleChanged(Window window, + double newOutputScaleX, + double newOutputScaleY) { + window.updateOutputScales(newOutputScaleX, newOutputScaleY); + } + + @Override public void notifyScreenChanged(Window window, Object from, Object to) { @@ -115,15 +121,15 @@ } @Override - public float getUIScale(Window window) { + public float getPlatformScaleX(Window window) { TKStage peer = window.impl_peer; - return peer == null ? 1.0f : peer.getUIScale(); + return peer == null ? 1.0f : peer.getPlatformScaleX(); } @Override - public float getRenderScale(Window window) { + public float getPlatformScaleY(Window window) { TKStage peer = window.impl_peer; - return peer == null ? 1.0f : peer.getRenderScale(); + return peer == null ? 1.0f : peer.getPlatformScaleY(); } @Override @@ -289,6 +295,153 @@ } } + 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: + * + * @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: + * + * @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 @@ -842,6 +995,7 @@ // 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); @@ -1205,6 +1359,8 @@ * the next pulse. */ private final class TKBoundsConfigurator implements TKPulseListener { + private double renderScaleX; + private double renderScaleY; private double x; private double y; private float xGravity; @@ -1220,6 +1376,16 @@ 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; @@ -1276,17 +1442,26 @@ 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); } } @@ -1296,6 +1471,8 @@ } private void reset() { + renderScaleX = 0.0; + renderScaleY = 0.0; x = Double.NaN; y = Double.NaN; xGravity = 0;