/* * Copyright (c) 2011, 2012, 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.scene.layout; import java.util.Arrays; import java.util.Collections; import java.util.List; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.image.Image; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; import com.sun.javafx.UnmodifiableArrayList; import com.sun.javafx.css.StyleableProperty; import com.sun.javafx.css.SubStyleableProperty; import com.sun.javafx.css.converters.InsetsConverter; import com.sun.javafx.css.converters.PaintConverter; import com.sun.javafx.css.converters.URLConverter; import com.sun.javafx.scene.layout.region.LayeredBackgroundPositionConverter; import com.sun.javafx.scene.layout.region.LayeredBackgroundSizeConverter; import com.sun.javafx.scene.layout.region.RepeatStruct; import com.sun.javafx.scene.layout.region.RepeatStructConverter; /** * The Background of a {@link Region}. A Background is an immutable object which * encapsulates the entire set of data required to render the background * of a Region. Because this class is immutable, you can freely reuse the same * Background on many different Regions. Please refer to * {@link ../doc-files/cssref.html JavaFX CSS Reference} for a complete description * of the CSS rules for styling the background of a Region. *

* Every Background is comprised of {@link #getFills() fills} and / or * {@link #getImages() images}. Neither list will ever be null, but either or * both may be empty. Each defined {@link BackgroundFill} is rendered in order, * followed by each defined {@link BackgroundImage}. *

* The Background's {@link #getOutsets() outsets} define any extension of the drawing area of a Region * which is necessary to account for all background drawing. These outsets are strictly * defined by the BackgroundFills that are specified on this Background, if any, because * all BackgroundImages are clipped to the drawing area, and do not define it. The * outsets values are strictly non-negative. * * @since JavaFX 8 */ public final class Background { static final StyleableProperty BACKGROUND_COLOR = new SubStyleableProperty("-fx-background-color", PaintConverter.SequenceConverter.getInstance(), new Paint[] {Color.BLACK}); static final StyleableProperty BACKGROUND_RADIUS = new SubStyleableProperty("-fx-background-radii", InsetsConverter.SequenceConverter.getInstance(), new Insets[] {Insets.EMPTY}); static final StyleableProperty BACKGROUND_INSETS = new SubStyleableProperty("-fx-background-insets", InsetsConverter.SequenceConverter.getInstance(), new Insets[] {Insets.EMPTY}); static final StyleableProperty BACKGROUND_IMAGE = new SubStyleableProperty("-fx-background-image", URLConverter.SequenceConverter.getInstance()); static final StyleableProperty BACKGROUND_REPEAT = new SubStyleableProperty("-fx-background-repeat", RepeatStructConverter.getInstance(), new RepeatStruct[] {new RepeatStruct(BackgroundRepeat.REPEAT, BackgroundRepeat.REPEAT) }); static final StyleableProperty BACKGROUND_POSITION = new SubStyleableProperty("-fx-background-position", LayeredBackgroundPositionConverter.getInstance(), new BackgroundPosition[] { BackgroundPosition.DEFAULT }); static final StyleableProperty BACKGROUND_SIZE = new SubStyleableProperty("-fx-background-size", LayeredBackgroundSizeConverter.getInstance(), new BackgroundSize[] { BackgroundSize.DEFAULT } ); private static final List STYLEABLES = (List) (List) Collections.unmodifiableList( Arrays.asList(BACKGROUND_COLOR, BACKGROUND_INSETS, BACKGROUND_RADIUS, BACKGROUND_IMAGE, BACKGROUND_REPEAT, BACKGROUND_POSITION, BACKGROUND_SIZE)); /** * @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 static List impl_CSS_STYLEABLES() { return STYLEABLES; } /** * The list of BackgroundFills which together define the filled portion * of this Background. This List is unmodifiable and immutable. It * will never be null. The elements of this list will also never be null. */ final List fills; public final List getFills() { return fills; } /** * The list of BackgroundImages which together define the image portion * of this Background. This List is unmodifiable and immutable. It * will never be null. The elements of this list will also never be null. */ final List images; public final List getImages() { return images; } /** * The cached hash code computation for the Background. One very big * reason for making Background immutable was to make it possible to * cache and reuse the same Background instance for multiple * Regions (for example, every un-hovered Button should have the same * Background instance). To enable efficient caching, we cache the hash. */ private int hash = 0; /** * The outsets of this Background. This represents the largest * bounding rectangle within which all drawing for the Background * will take place. The outsets will never be negative, and represent * the distance from the edge of the Region outward. Any BackgroundImages * which would extend beyond the outsets will be clipped. Only the * BackgroundFills contribute to the outsets. */ final Insets outsets; public final Insets getOutsets() { return outsets; } /** * Create a new Background by supplying an array of BackgroundFills. * This array may be null, or may contain null values. Any null values * will be ignored and will not contribute to the {@link #getFills() fills} * or {@link #getOutsets() outsets}. * * @param fills The fills. This may be null, and may contain nulls. Any * contained nulls are filtered out and not included in the * final List of fills. A null array becomes an empty List. */ public Background(final BackgroundFill... fills) { this(fills, null); } /** * Create a new Background by supplying an array of BackgroundImages. * This array may be null, or may contain null values. Any null values will * be ignored and will not contribute to the {@link #getImages() images}. * * @param images The images. This may be null, and may contain nulls. Any * contained nulls are filtered out and not included in the * final List of images. A null array becomes an empty List. */ public Background(final BackgroundImage... images) { this(null, images); } /** * Create a new Background supply two Lists, one for background fills and * one for background images. Either list may be null, and may contain nulls. * Any null values in these lists will be ignored and will not * contribute to the {@link #getFills() fills}, {@link #getImages() images}, or * {@link #getOutsets() outsets}. * * @param fills The fills. This may be null, and may contain nulls. Any * contained nulls are filtered out and not included in the * final List of fills. A null List becomes an empty List. * @param images The images. This may be null, and may contain nulls. Any * contained nulls are filtered out and not included in the * final List of images. A null List becomes an empty List. */ public Background(final List fills, final List images) { // NOTE: This constructor had to be supplied in order to cause a Builder // to be auto-generated, because otherwise the types of the fills and images // properties didn't match the types of the array based constructor parameters. // So a Builder will use this constructor, while the CSS engine uses the // array based constructor (for speed). this(fills == null ? null : fills.toArray(new BackgroundFill[fills.size()]), images == null ? null : images.toArray(new BackgroundImage[images.size()])); } /** * Create a new Background by supplying two arrays, one for background fills, * and one for background images. Either array may be null, and may contain null * values. Any null values in these arrays will be ignored and will not * contribute to the {@link #getFills() fills}, {@link #getImages() images}, or * {@link #getOutsets() outsets}. * * @param fills The fills. This may be null, and may contain nulls. Any * contained nulls are filtered out and not included in the * final List of fills. A null array becomes an empty List. * @param images The images. This may be null, and may contain nulls. Any * contained nulls are filtered out and not included in the * final List of images. A null array becomes an empty List. */ public Background(final BackgroundFill[] fills, final BackgroundImage[] images) { // The cumulative insets double outerTop = 0, outerRight = 0, outerBottom = 0, outerLeft = 0; // If the fills is empty or null then we know we can just use the shared // immutable empty list from Collections. if (fills == null || fills.length == 0) { this.fills = Collections.emptyList(); } else { // We need to iterate over all of the supplied elements in the fills array. // Each null element is ignored. Each non-null element is inspected to // see if it contributes to the outsets. final BackgroundFill[] noNulls = new BackgroundFill[fills.length]; int size = 0; for (int i=0; i(noNulls, size); } // This ensures that we either have outsets of 0, if all the insets were positive, // or a value greater than zero if they were negative. outsets = new Insets( Math.max(0, -outerTop), Math.max(0, -outerRight), Math.max(0, -outerBottom), Math.max(0, -outerLeft)); // An null or empty images array results in an empty list if (images == null || images.length == 0) { this.images = Collections.emptyList(); } else { // Filter out any null values and create an immutable array list final BackgroundImage[] noNulls = new BackgroundImage[images.length]; int size = 0; for (int i=0; i(noNulls, size); } } /** * @inheritDoc */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; // Because the hash is cached, this can be a very fast check if (hashCode() != o.hashCode()) return false; Background that = (Background) o; if (!fills.equals(that.fills)) return false; if (!images.equals(that.images)) return false; return true; } /** * @inheritDoc */ @Override public int hashCode() { if (hash == 0) { // Please note: I have to compute into a temporary result variable // before making the final assignment, or the hash code could // come out wrong in a multi-threaded scenario. int result = fills.hashCode(); result = 31 * result + images.hashCode(); hash = result; } return hash; } }