rev 8703 : RT-40186: Update copyright header for files modified in 2015
1 /*
2 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javafx.scene.layout;
27
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.List;
31
32 import javafx.beans.NamedArg;
33 import javafx.geometry.Insets;
34 import javafx.scene.Node;
35 import javafx.scene.paint.Paint;
36 import com.sun.javafx.UnmodifiableArrayList;
37 import javafx.css.CssMetaData;
38 import com.sun.javafx.css.SubCssMetaData;
39 import com.sun.javafx.css.converters.InsetsConverter;
40 import com.sun.javafx.css.converters.URLConverter;
41 import com.sun.javafx.scene.layout.region.BorderImageSlices;
42 import com.sun.javafx.scene.layout.region.BorderImageWidthConverter;
43 import com.sun.javafx.scene.layout.region.CornerRadiiConverter;
44 import com.sun.javafx.scene.layout.region.LayeredBorderPaintConverter;
45 import com.sun.javafx.scene.layout.region.LayeredBorderStyleConverter;
46 import com.sun.javafx.scene.layout.region.Margins;
47 import com.sun.javafx.scene.layout.region.RepeatStruct;
48 import com.sun.javafx.scene.layout.region.RepeatStructConverter;
49 import com.sun.javafx.scene.layout.region.SliceSequenceConverter;
50 import javafx.css.Styleable;
51
52 /**
53 * The Border of a {@link Region}. A Border is an immutable object which
54 * encapsulates the entire set of data required to render the border
55 * of a Region. Because this class is immutable, you can freely reuse the same
56 * Border on many different Regions. Please refer to
57 * {@link ../doc-files/cssref.html JavaFX CSS Reference} for a complete description
58 * of the CSS rules for styling the border of a Region.
59 * <p/>
60 * Every Border is comprised of {@link #getStrokes() strokes} and / or
61 * {@link #getImages() images}. Neither list will ever be null, but either or
62 * both may be empty. When rendering, if no images are specified or no
63 * image succeeds in loading, then all strokes will be rendered in order.
64 * If any image is specified and succeeds in loading, then no strokes will
65 * be drawn, although they will still contribute to the {@link #getInsets() insets}
66 * and {@link #getOutsets() outsets} of the Border.
67 * <p/>
68 * The Border's {@link #getOutsets() outsets} define any extension of the drawing area of a Region
69 * which is necessary to account for all border drawing and positioning. These outsets are defined
70 * by both the {@link BorderStroke}s and {@link BorderImage}s specified on this Border.
71 * Outsets are strictly non-negative.
72 * <p/>
73 * {@link #getInsets()} are used to define the inner-most edge of all of the borders. It also is
74 * always strictly non-negative. The Region uses the insets of the {@link Background} and Border
75 * and the {@link javafx.scene.layout.Region#getPadding() Region's padding} to determine the
76 * Region {@link javafx.scene.layout.Region#getInsets() insets}, which define the content area
77 * for any children of the Region. The outsets of a Border together with the outsets of a Background
78 * and the width and height of the Region define the geometric bounds of the Region (which in
79 * turn contribute to the {@code layoutBounds}, {@code boundsInLocal}, and {@code boundsInParent}).
80 * <p/>
81 * A Border is most often used in cases where you want to skin the Region with an image,
82 * often used in conjunction with 9-patch scaling techniques. In such cases, you may
83 * also specify a stroked border which is only used when the image fails to load for some
84 * reason.
85 *
86 * @since JavaFX 8.0
87 */
88 @SuppressWarnings("unchecked")
89 public final class Border {
90 static final CssMetaData<Node,Paint[]> BORDER_COLOR =
91 new SubCssMetaData<Paint[]>("-fx-border-color",
92 LayeredBorderPaintConverter.getInstance());
93
94 static final CssMetaData<Node,BorderStrokeStyle[][]> BORDER_STYLE =
95 new SubCssMetaData<BorderStrokeStyle[][]>("-fx-border-style",
96 LayeredBorderStyleConverter.getInstance());
97
98 static final CssMetaData<Node,Margins[]> BORDER_WIDTH =
99 new SubCssMetaData<Margins[]> ("-fx-border-width",
100 Margins.SequenceConverter.getInstance());
101
102 static final CssMetaData<Node,CornerRadii[]> BORDER_RADIUS =
103 new SubCssMetaData<CornerRadii[]>("-fx-border-radius",
104 CornerRadiiConverter.getInstance());
105
106 static final CssMetaData<Node,Insets[]> BORDER_INSETS =
107 new SubCssMetaData<Insets[]>("-fx-border-insets",
108 InsetsConverter.SequenceConverter.getInstance());
109
110 static final CssMetaData<Node,String[]> BORDER_IMAGE_SOURCE =
111 new SubCssMetaData<String[]>("-fx-border-image-source",
112 URLConverter.SequenceConverter.getInstance());
113
114 static final CssMetaData<Node,RepeatStruct[]> BORDER_IMAGE_REPEAT =
115 new SubCssMetaData<RepeatStruct[]>("-fx-border-image-repeat",
116 RepeatStructConverter.getInstance(),
117 new RepeatStruct[] { new RepeatStruct(BackgroundRepeat.REPEAT, BackgroundRepeat.REPEAT) });
118
119 static final CssMetaData<Node,BorderImageSlices[]> BORDER_IMAGE_SLICE =
120 new SubCssMetaData<BorderImageSlices[]> ("-fx-border-image-slice",
121 SliceSequenceConverter.getInstance(),
122 new BorderImageSlices[] { BorderImageSlices.DEFAULT});
123
124 static final CssMetaData<Node,BorderWidths[]> BORDER_IMAGE_WIDTH =
125 new SubCssMetaData<BorderWidths[]>("-fx-border-image-width",
126 BorderImageWidthConverter.getInstance(),
127 new BorderWidths[] { BorderWidths.DEFAULT });
128
129 static final CssMetaData<Node,Insets[]> BORDER_IMAGE_INSETS =
130 new SubCssMetaData<Insets[]>("-fx-border-image-insets",
131 InsetsConverter.SequenceConverter.getInstance(),
132 new Insets[] {Insets.EMPTY});
133
134 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES =
135 (List<CssMetaData<? extends Styleable, ?>>) (List) Collections.unmodifiableList(
136 // Unchecked!
137 Arrays.asList(BORDER_COLOR,
138 BORDER_STYLE,
139 BORDER_WIDTH,
140 BORDER_RADIUS,
141 BORDER_INSETS,
142 BORDER_IMAGE_SOURCE,
143 BORDER_IMAGE_REPEAT,
144 BORDER_IMAGE_SLICE,
145 BORDER_IMAGE_WIDTH,
146 BORDER_IMAGE_INSETS));
147
148 /**
149 * @return The CssMetaData associated with this class, which may include the
150 * CssMetaData of its super classes.
151 */
152 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
153 return STYLEABLES;
154 }
155
156 /**
157 * An empty Border, useful to use instead of null.
158 */
159 public static final Border EMPTY = new Border((BorderStroke[])null, null);
160
161 /**
162 * The list of BorderStrokes which together define the stroked portion
163 * of this Border. This List is unmodifiable and immutable. It
164 * will never be null. It will never contain any null elements.
165 */
166 public final List<BorderStroke> getStrokes() { return strokes; }
167 final List<BorderStroke> strokes;
168
169 /**
170 * The list of BorderImages which together define the images to use
171 * instead of stroke for this Border. If this list is specified and
172 * at least one image within it succeeds in loading, then any specified
173 * {@link #getStrokes strokes} are not drawn. If this list is null or no images
174 * succeeded in loading, then any specified {@code strokes} are drawn.
175 * <p>
176 * This List is unmodifiable and immutable. It will never be null.
177 * It will never contain any null elements.
178 */
179 public final List<BorderImage> getImages() { return images; }
180 final List<BorderImage> images;
181
182 /**
183 * The outsets of the border define the outer-most edge of the border to be drawn.
184 * The values in these outsets are strictly non-negative.
185 */
186 public final Insets getOutsets() { return outsets; }
187 final Insets outsets;
188
189 /**
190 * The insets define the distance from the edge of the Region to the inner-most edge
191 * of the border, if that distance is non-negative. The values in these outsets
192 * are strictly non-negative.
193 */
194 public final Insets getInsets() { return insets; }
195 final Insets insets;
196
197 /**
198 * Gets whether the Border is empty. It is empty if there are no strokes or images.
199 * @return true if the Border is empty, false otherwise.
200 */
201 public final boolean isEmpty() {
202 return strokes.isEmpty() && images.isEmpty();
203 }
204
205 /**
206 * The cached hash code computation for the Border. One very big
207 * reason for making Border immutable was to make it possible to
208 * cache and reuse the same Border instance for multiple
209 * Regions. To enable efficient caching, we cache the hash.
210 */
211 private final int hash;
212
213 /**
214 * Creates a new Border by supplying an array of BorderStrokes.
215 * This array may be null, or may contain null values. Any null values
216 * will be ignored and will not contribute to the {@link #getStrokes() strokes}
217 * or {@link #getOutsets() outsets} or {@link #getInsets() insets}.
218 *
219 * @param strokes The strokes. This may be null, and may contain nulls. Any
220 * contained nulls are filtered out and not included in the
221 * final List of strokes. A null array becomes an empty List.
222 * If both strokes and images are specified, and if any one
223 * of the images specified succeeds in loading, then no
224 * strokes are shown. In this way, strokes can be defined as
225 * a fallback in the case that an image failed to load.
226 */
227 public Border(@NamedArg("strokes") BorderStroke... strokes) {
228 this(strokes, null);
229 }
230
231 /**
232 * Creates a new Border by supplying an array of BorderImages.
233 * This array may be null, or may contain null values. Any null values
234 * will be ignored and will not contribute to the {@link #getImages() images}
235 * or {@link #getOutsets() outsets} or {@link #getInsets() insets}.
236 *
237 * @param images The images. This may be null, and may contain nulls. Any
238 * contained nulls are filtered out and not included in the
239 * final List of images. A null array becomes an empty List.
240 */
241 public Border(@NamedArg("images") BorderImage... images) {
242 this(null, images);
243 }
244
245 /**
246 * Creates a new Border by supplying a List of BorderStrokes and BorderImages.
247 * These Lists may be null, or may contain null values. Any null values
248 * will be ignored and will not contribute to the {@link #getStrokes() strokes}
249 * or {@link #getImages() images}, {@link #getOutsets() outsets}, or
250 * {@link #getInsets() insets}.
251 *
252 * @param strokes The strokes. This may be null, and may contain nulls. Any
253 * contained nulls are filtered out and not included in the
254 * final List of strokes. A null array becomes an empty List.
255 * If both strokes and images are specified, and if any one
256 * of the images specified succeeds in loading, then no
257 * strokes are shown. In this way, strokes can be defined as
258 * a fallback in the case that an image failed to load.
259 * @param images The images. This may be null, and may contain nulls. Any
260 * contained nulls are filtered out and not included in the
261 * final List of images. A null array becomes an empty List.
262 */
263 public Border(@NamedArg("strokes") List<BorderStroke> strokes, @NamedArg("images") List<BorderImage> images) {
264 // NOTE: This constructor had to be supplied in order to cause a Builder
265 // to be auto-generated, because otherwise the types of the strokes and images
266 // properties didn't match the types of the array based constructor parameters.
267 // So a Builder will use this constructor, while the CSS engine uses the
268 // array based constructor (for speed).
269 this(strokes == null ? null : strokes.toArray(new BorderStroke[strokes.size()]),
270 images == null ? null : images.toArray(new BorderImage[images.size()]));
271 }
272
273 /**
274 * Creates a new Border by supplying an array of BorderStrokes and BorderImages.
275 * These arrays may be null, or may contain null values. Any null values
276 * will be ignored and will not contribute to the {@link #getStrokes() strokes}
277 * or {@link #getImages() images}, {@link #getOutsets() outsets}, or
278 * {@link #getInsets() insets}.
279 *
280 * @param strokes The strokes. This may be null, and may contain nulls. Any
281 * contained nulls are filtered out and not included in the
282 * final List of strokes. A null array becomes an empty List.
283 * If both strokes and images are specified, and if any one
284 * of the images specified succeeds in loading, then no
285 * strokes are shown. In this way, strokes can be defined as
286 * a fallback in the case that an image failed to load.
287 * @param images The images. This may be null, and may contain nulls. Any
288 * contained nulls are filtered out and not included in the
289 * final List of images. A null array becomes an empty List.
290 */
291 public Border(@NamedArg("strokes") BorderStroke[] strokes, @NamedArg("images") BorderImage[] images) {
292 double innerTop = 0, innerRight = 0, innerBottom = 0, innerLeft = 0;
293 double outerTop = 0, outerRight = 0, outerBottom = 0, outerLeft = 0;
294
295 if (strokes == null || strokes.length == 0) {
296 this.strokes = Collections.emptyList();
297 } else {
298 final BorderStroke[] noNulls = new BorderStroke[strokes.length];
299 int size = 0;
300 for (int i=0; i<strokes.length; i++) {
301 final BorderStroke stroke = strokes[i];
302 if (stroke != null) {
303 noNulls[size++] = stroke;
304
305 // Calculate the insets and outsets. "insets" are the distance
306 // from the edge of the region to the inmost edge of the inmost border.
307 // Outsets are the distance from the edge of the region out towards the
308 // outer-most edge of the outer-most border.
309 final double strokeInnerTop = stroke.innerEdge.getTop();
310 final double strokeInnerRight = stroke.innerEdge.getRight();
311 final double strokeInnerBottom = stroke.innerEdge.getBottom();
312 final double strokeInnerLeft = stroke.innerEdge.getLeft();
313
314 innerTop = innerTop >= strokeInnerTop ? innerTop : strokeInnerTop;
315 innerRight = innerRight >= strokeInnerRight? innerRight : strokeInnerRight;
316 innerBottom = innerBottom >= strokeInnerBottom ? innerBottom : strokeInnerBottom;
317 innerLeft = innerLeft >= strokeInnerLeft ? innerLeft : strokeInnerLeft;
318
319 final double strokeOuterTop = stroke.outerEdge.getTop();
320 final double strokeOuterRight = stroke.outerEdge.getRight();
321 final double strokeOuterBottom = stroke.outerEdge.getBottom();
322 final double strokeOuterLeft = stroke.outerEdge.getLeft();
323
324 outerTop = outerTop >= strokeOuterTop ? outerTop : strokeOuterTop;
325 outerRight = outerRight >= strokeOuterRight? outerRight : strokeOuterRight;
326 outerBottom = outerBottom >= strokeOuterBottom ? outerBottom : strokeOuterBottom;
327 outerLeft = outerLeft >= strokeOuterLeft ? outerLeft : strokeOuterLeft;
328 }
329 }
330 this.strokes = new UnmodifiableArrayList<BorderStroke>(noNulls, size);
331 }
332
333 if (images == null || images.length == 0) {
334 this.images = Collections.emptyList();
335 } else {
336 final BorderImage[] noNulls = new BorderImage[images.length];
337 int size = 0;
338 for (int i=0; i<images.length; i++) {
339 final BorderImage image = images[i];
340 if (image != null){
341 noNulls[size++] = image;
342
343 // The Image width + insets may contribute to the insets / outsets of
344 // this border.
345 final double imageInnerTop = image.innerEdge.getTop();
346 final double imageInnerRight = image.innerEdge.getRight();
347 final double imageInnerBottom = image.innerEdge.getBottom();
348 final double imageInnerLeft = image.innerEdge.getLeft();
349
350 innerTop = innerTop >= imageInnerTop ? innerTop : imageInnerTop;
351 innerRight = innerRight >= imageInnerRight? innerRight : imageInnerRight;
352 innerBottom = innerBottom >= imageInnerBottom ? innerBottom : imageInnerBottom;
353 innerLeft = innerLeft >= imageInnerLeft ? innerLeft : imageInnerLeft;
354
355 final double imageOuterTop = image.outerEdge.getTop();
356 final double imageOuterRight = image.outerEdge.getRight();
357 final double imageOuterBottom = image.outerEdge.getBottom();
358 final double imageOuterLeft = image.outerEdge.getLeft();
359
360 outerTop = outerTop >= imageOuterTop ? outerTop : imageOuterTop;
361 outerRight = outerRight >= imageOuterRight? outerRight : imageOuterRight;
362 outerBottom = outerBottom >= imageOuterBottom ? outerBottom : imageOuterBottom;
363 outerLeft = outerLeft >= imageOuterLeft ? outerLeft : imageOuterLeft;
364 }
365 }
366 this.images = new UnmodifiableArrayList<BorderImage>(noNulls, size);
367 }
368
369 // Both the BorderStroke and BorderImage class make sure to return the outsets
370 // and insets in the right way, such that we don't have to worry about adjusting
371 // the sign, etc, unlike in the Background implementation.
372 outsets = new Insets(outerTop, outerRight, outerBottom, outerLeft);
373 insets = new Insets(innerTop, innerRight, innerBottom, innerLeft);
374
375 // Pre-compute the hash code. NOTE: all variables are prefixed with "this" so that we
376 // do not accidentally compute the hash based on the constructor arguments rather than
377 // based on the fields themselves!
378 int result = this.strokes.hashCode();
379 result = 31 * result + this.images.hashCode();
380 hash = result;
381 }
382
383 /**
384 * @inheritDoc
385 */
386 @Override public boolean equals(Object o) {
387 if (this == o) return true;
388 if (o == null || getClass() != o.getClass()) return false;
389 Border border = (Border) o;
390 if (this.hash != border.hash) return false;
391
392 if (!images.equals(border.images)) return false;
393 if (!strokes.equals(border.strokes)) return false;
394
395 return true;
396 }
397
398 /**
399 * @inheritDoc
400 */
401 @Override public int hashCode() {
402 return hash;
403 }
404 }
--- EOF ---