diff --git a/modules/javafx.graphics/src/main/docs/javafx/scene/doc-files/cssref.html b/modules/javafx.graphics/src/main/docs/javafx/scene/doc-files/cssref.html index 27abfac7585..f57e5cf317e 100644 --- a/modules/javafx.graphics/src/main/docs/javafx/scene/doc-files/cssref.html +++ b/modules/javafx.graphics/src/main/docs/javafx/scene/doc-files/cssref.html @@ -1882,11 +1882,34 @@

ImageView

- -fx-image + -fx-fit-height + <number> + 0 + The height of the bounding box within which the source image is resized as necessary to fit. + + + -fx-fit-width + <number> + 0 + The width of the bounding box within which the source image is resized as necessary to fit. + + -fx-image <uri> null Relative URLs are resolved against the URL of the stylesheet. + + -fx-preserve-ratio + <boolean> + false + Indicates whether to preserve the aspect ratio of the source image when scaling to fit the image within the fitting bounding box. + + + -fx-smooth + <boolean> + Platform-specific + Indicates whether to use a better quality filtering algorithm or a faster one when transforming or scaling the source image to fit. + Also has all properties of Node diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/css/CssUtil.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/css/CssUtil.java new file mode 100644 index 00000000000..af44bf76aaf --- /dev/null +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/css/CssUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023, 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 com.sun.javafx.css; + +import java.util.List; +import javafx.css.CssMetaData; +import javafx.css.Styleable; +import javafx.scene.Node; + +/** + * Utility methods for dealing with CSS. + */ +public final class CssUtil { + + private CssUtil() { + } + + /** + * Utility method which combines {@code CssMetaData} items in one immutable list. + *

+ * The intended usage is to combine the parent and the child {@code CssMetaData} for + * the purposes of {@code getClassCssMetaData()} method, see for example {@link Node#getClassCssMetaData()}. + *

+ * Example: + *

{@code
+     * private static final List> STYLEABLES = CssMetaData.combine(
+     *      .getClassCssMetaData(),
+     *      STYLEABLE1,
+     *      STYLEABLE2
+     *  );
+     * }
+ * This method returns an instance of a {@code List} that implements + * {@link java.util.RandomAccess} interface. + * + * @param inheritedFromParent the {@code CssMetaData} items inherited from parent, must not be null + * @param items the additional items + * @return the immutable list containing all of the items + */ + // NOTE: this should be a public utility, see https://bugs.openjdk.org/browse/JDK-8320796 + public static List> combine( + List> inheritedFromParent, + CssMetaData... items) + { + CssMetaData[] combined = new CssMetaData[inheritedFromParent.size() + items.length]; + inheritedFromParent.toArray(combined); + System.arraycopy(items, 0, combined, inheritedFromParent.size(), items.length); + return List.of(combined); + } +} diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/image/ImageView.java b/modules/javafx.graphics/src/main/java/javafx/scene/image/ImageView.java index 0c59aa5e140..70ef84a9b94 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/image/ImageView.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/image/ImageView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2023, 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 @@ -25,31 +25,39 @@ package javafx.scene.image; -import com.sun.javafx.beans.event.AbstractNotifyListener; -import com.sun.javafx.css.StyleManager; -import javafx.css.converter.URLConverter; -import com.sun.javafx.geom.BaseBounds; -import com.sun.javafx.geom.transform.BaseTransform; -import com.sun.javafx.scene.DirtyBits; -import com.sun.javafx.scene.ImageViewHelper; -import com.sun.javafx.scene.NodeHelper; -import com.sun.javafx.sg.prism.NGImageView; -import com.sun.javafx.sg.prism.NGNode; -import com.sun.javafx.tk.Toolkit; +import java.util.List; import javafx.beans.DefaultProperty; import javafx.beans.Observable; -import javafx.beans.property.*; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.DoublePropertyBase; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ObjectPropertyBase; +import javafx.beans.property.StringProperty; import javafx.css.CssMetaData; import javafx.css.Styleable; +import javafx.css.StyleableBooleanProperty; +import javafx.css.StyleableDoubleProperty; import javafx.css.StyleableProperty; import javafx.css.StyleableStringProperty; +import javafx.css.converter.BooleanConverter; +import javafx.css.converter.SizeConverter; +import javafx.css.converter.URLConverter; import javafx.geometry.NodeOrientation; import javafx.geometry.Rectangle2D; import javafx.scene.AccessibleRole; import javafx.scene.Node; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import com.sun.javafx.beans.event.AbstractNotifyListener; +import com.sun.javafx.css.CssUtil; +import com.sun.javafx.css.StyleManager; +import com.sun.javafx.geom.BaseBounds; +import com.sun.javafx.geom.transform.BaseTransform; +import com.sun.javafx.scene.DirtyBits; +import com.sun.javafx.scene.ImageViewHelper; +import com.sun.javafx.scene.NodeHelper; +import com.sun.javafx.sg.prism.NGImageView; +import com.sun.javafx.sg.prism.NGNode; +import com.sun.javafx.tk.Toolkit; /** * The {@code ImageView} is a {@code Node} used for painting images loaded with @@ -408,8 +416,7 @@ public String getName() { * * @defaultValue 0 */ - private DoubleProperty fitWidth; - + private StyleableDoubleProperty fitWidth; public final void setFitWidth(double value) { fitWidthProperty().set(value); @@ -421,8 +428,7 @@ public final double getFitWidth() { public final DoubleProperty fitWidthProperty() { if (fitWidth == null) { - fitWidth = new DoublePropertyBase() { - + fitWidth = new StyleableDoubleProperty() { @Override protected void invalidated() { invalidateWidthHeight(); @@ -439,6 +445,11 @@ public Object getBean() { public String getName() { return "fitWidth"; } + + @Override + public CssMetaData getCssMetaData() { + return StyleableProperties.FIT_WIDTH; + } }; } return fitWidth; @@ -456,8 +467,7 @@ public String getName() { * * @defaultValue 0 */ - private DoubleProperty fitHeight; - + private StyleableDoubleProperty fitHeight; public final void setFitHeight(double value) { fitHeightProperty().set(value); @@ -469,8 +479,7 @@ public final double getFitHeight() { public final DoubleProperty fitHeightProperty() { if (fitHeight == null) { - fitHeight = new DoublePropertyBase() { - + fitHeight = new StyleableDoubleProperty() { @Override protected void invalidated() { invalidateWidthHeight(); @@ -487,6 +496,11 @@ public Object getBean() { public String getName() { return "fitHeight"; } + + @Override + public CssMetaData getCssMetaData() { + return StyleableProperties.FIT_HEIGHT; + } }; } return fitHeight; @@ -521,8 +535,7 @@ public String getName() { * * @defaultValue false */ - private BooleanProperty preserveRatio; - + private StyleableBooleanProperty preserveRatio; public final void setPreserveRatio(boolean value) { preserveRatioProperty().set(value); @@ -534,8 +547,7 @@ public final boolean isPreserveRatio() { public final BooleanProperty preserveRatioProperty() { if (preserveRatio == null) { - preserveRatio = new BooleanPropertyBase() { - + preserveRatio = new StyleableBooleanProperty() { @Override protected void invalidated() { invalidateWidthHeight(); @@ -552,6 +564,11 @@ public Object getBean() { public String getName() { return "preserveRatio"; } + + @Override + public CssMetaData getCssMetaData() { + return StyleableProperties.PRESERVE_RATIO; + } }; } return preserveRatio; @@ -573,8 +590,7 @@ public String getName() { * * @defaultValue platform-dependent */ - private BooleanProperty smooth; - + private StyleableBooleanProperty smooth; public final void setSmooth(boolean value) { smoothProperty().set(value); @@ -586,8 +602,7 @@ public final boolean isSmooth() { public final BooleanProperty smoothProperty() { if (smooth == null) { - smooth = new BooleanPropertyBase(SMOOTH_DEFAULT) { - + smooth = new StyleableBooleanProperty(SMOOTH_DEFAULT) { @Override protected void invalidated() { NodeHelper.markDirty(ImageView.this, DirtyBits.NODE_SMOOTH); @@ -602,6 +617,11 @@ public Object getBean() { public String getName() { return "smooth"; } + + @Override + public CssMetaData getCssMetaData() { + return StyleableProperties.SMOOTH; + } }; } return smooth; @@ -804,16 +824,38 @@ private boolean doComputeContains(double localX, double localY) { private static final String DEFAULT_STYLE_CLASS = "image-view"; - /* - * Super-lazy instantiation pattern from Bill Pugh. - */ - private static class StyleableProperties { - // TODO - // "preserve-ratio","smooth","viewport","fit-width","fit-height" - private static final CssMetaData IMAGE = - new CssMetaData<>("-fx-image", - URLConverter.getInstance()) { + private static class StyleableProperties { + private static final CssMetaData FIT_HEIGHT = + new CssMetaData<>("-fx-fit-height", SizeConverter.getInstance(), 0.0) + { + @Override + public boolean isSettable(ImageView n) { + return n.fitHeight == null || !n.fitHeight.isBound(); + } + + @Override + public StyleableProperty getStyleableProperty(ImageView n) { + return (StyleableProperty)n.fitHeightProperty(); + } + }; + + private static final CssMetaData FIT_WIDTH = + new CssMetaData<>("-fx-fit-width", SizeConverter.getInstance(), 0.0) + { + @Override + public boolean isSettable(ImageView n) { + return n.fitWidth == null || !n.fitWidth.isBound(); + } + + @Override + public StyleableProperty getStyleableProperty(ImageView n) { + return (StyleableProperty)n.fitWidthProperty(); + } + }; + private static final CssMetaData IMAGE = + new CssMetaData<>("-fx-image", URLConverter.getInstance()) + { @Override public boolean isSettable(ImageView n) { // Note that we care about the image, not imageUrl @@ -826,13 +868,42 @@ public StyleableProperty getStyleableProperty(ImageView n) { } }; - private static final List> STYLEABLES; - static { - final List> styleables = - new ArrayList<>(Node.getClassCssMetaData()); - styleables.add(IMAGE); - STYLEABLES = Collections.unmodifiableList(styleables); - } + private static final CssMetaData PRESERVE_RATIO = + new CssMetaData<>("-fx-preserve-ratio", BooleanConverter.getInstance(), Boolean.FALSE) + { + @Override + public boolean isSettable(ImageView n) { + return n.preserveRatio == null || !n.preserveRatio.isBound(); + } + + @Override + public StyleableProperty getStyleableProperty(ImageView n) { + return (StyleableProperty)n.preserveRatioProperty(); + } + }; + + private static final CssMetaData SMOOTH = + new CssMetaData<>("-fx-smooth", BooleanConverter.getInstance(), SMOOTH_DEFAULT) + { + @Override + public boolean isSettable(ImageView n) { + return n.smooth == null || !n.smooth.isBound(); + } + + @Override + public StyleableProperty getStyleableProperty(ImageView n) { + return (StyleableProperty)n.smoothProperty(); + } + }; + + private static final List> STYLEABLES = CssUtil.combine( + Node.getClassCssMetaData(), + FIT_HEIGHT, + FIT_WIDTH, + IMAGE, + PRESERVE_RATIO, + SMOOTH + ); } /** diff --git a/modules/javafx.graphics/src/test/java/test/javafx/scene/image/ImageView_cssMethods_Test.java b/modules/javafx.graphics/src/test/java/test/javafx/scene/image/ImageView_cssMethods_Test.java index 7c3b4dde104..12f1f541484 100644 --- a/modules/javafx.graphics/src/test/java/test/javafx/scene/image/ImageView_cssMethods_Test.java +++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/image/ImageView_cssMethods_Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023, 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 @@ -74,14 +74,16 @@ public static void configureImageLoaderFactory() { @Parameters public static Collection data() { + boolean smooth = ImageView.SMOOTH_DEFAULT; + return Arrays.asList(new Object[] { - config(TEST_IMAGE_VIEW, "image", null, - "-fx-image", TEST_IMAGE_URL1, IMAGE_COMPARATOR), - config(TEST_IMAGE_VIEW, "image", - TestImages.TEST_IMAGE_32x32, - "-fx-image", TEST_IMAGE_URL2, IMAGE_COMPARATOR), - config(TEST_IMAGE_VIEW, "translateX", 0.0, - "-fx-translate-x", 10.0) + config(TEST_IMAGE_VIEW, "image", null, "-fx-image", TEST_IMAGE_URL1, IMAGE_COMPARATOR), + config(TEST_IMAGE_VIEW, "image", TestImages.TEST_IMAGE_32x32, "-fx-image", TEST_IMAGE_URL2, IMAGE_COMPARATOR), + config(TEST_IMAGE_VIEW, "translateX", 0.0, "-fx-translate-x", 10.0), + config(TEST_IMAGE_VIEW, "fitHeight", 0.0, "-fx-fit-height", 10.0), + config(TEST_IMAGE_VIEW, "fitWidth", 0.0, "-fx-fit-width", 10.0), + config(TEST_IMAGE_VIEW, "preserveRatio", false, "-fx-preserve-ratio", true), + config(TEST_IMAGE_VIEW, "smooth", smooth, "-fx-smooth", !smooth), }); }