/*
* 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;
}
}