Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.layout; 027 028import java.util.Arrays; 029import java.util.Collections; 030import java.util.List; 031import javafx.geometry.Insets; 032import javafx.scene.Node; 033import javafx.scene.paint.Paint; 034import com.sun.javafx.UnmodifiableArrayList; 035import javafx.css.CssMetaData; 036import com.sun.javafx.css.SubCssMetaData; 037import com.sun.javafx.css.converters.InsetsConverter; 038import com.sun.javafx.css.converters.URLConverter; 039import com.sun.javafx.scene.layout.region.BorderImageSlices; 040import com.sun.javafx.scene.layout.region.BorderImageWidthConverter; 041import com.sun.javafx.scene.layout.region.LayeredBorderPaintConverter; 042import com.sun.javafx.scene.layout.region.LayeredBorderStyleConverter; 043import com.sun.javafx.scene.layout.region.Margins; 044import com.sun.javafx.scene.layout.region.RepeatStruct; 045import com.sun.javafx.scene.layout.region.RepeatStructConverter; 046import com.sun.javafx.scene.layout.region.SliceSequenceConverter; 047import javafx.css.Styleable; 048 049/** 050 * The Border of a {@link Region}. A Border is an immutable object which 051 * encapsulates the entire set of data required to render the border 052 * of a Region. Because this class is immutable, you can freely reuse the same 053 * Border on many different Regions. Please refer to 054 * {@link ../doc-files/cssref.html JavaFX CSS Reference} for a complete description 055 * of the CSS rules for styling the border of a Region. 056 * <p/> 057 * Every Border is comprised of {@link #getStrokes() strokes} and / or 058 * {@link #getImages() images}. Neither list will ever be null, but either or 059 * both may be empty. When rendering, if no images are specified or no 060 * image succeeds in loading, then all strokes will be rendered in order. 061 * If any image is specified and succeeds in loading, then no strokes will 062 * be drawn, although they will still contribute to the {@link #getInsets() insets} 063 * and {@link #getOutsets() outsets} of the Border. 064 * <p/> 065 * The Border's {@link #getOutsets() outsets} define any extension of the drawing area of a Region 066 * which is necessary to account for all border drawing and positioning. These outsets are defined 067 * by both the {@link BorderStroke}s and {@link BorderImage}s specified on this Border. 068 * Outsets are strictly non-negative. 069 * <p/> 070 * {@link #getInsets()} are used to define the inner-most edge of all of the borders. It also is 071 * always strictly non-negative. The Region uses the insets of the {@link Background} and Border 072 * and the {@link javafx.scene.layout.Region#getPadding() Region's padding} to determine the 073 * Region {@link javafx.scene.layout.Region#getInsets() insets}, which define the content area 074 * for any children of the Region. The outsets of a Border together with the outsets of a Background 075 * and the width and height of the Region define the geometric bounds of the Region (which in 076 * turn contribute to the {@code layoutBounds}, {@code boundsInLocal}, and {@code boundsInParent}). 077 * <p/> 078 * A Border is most often used in cases where you want to skin the Region with an image, 079 * often used in conjunction with 9-patch scaling techniques. In such cases, you may 080 * also specify a stroked border which is only used when the image fails to load for some 081 * reason. 082 * 083 * @since JavaFX 8 084 */ 085@SuppressWarnings("unchecked") 086public final class Border { 087 static final CssMetaData<Node,Paint[]> BORDER_COLOR = 088 new SubCssMetaData<Paint[]>("-fx-border-color", 089 LayeredBorderPaintConverter.getInstance()); 090 091 static final CssMetaData<Node,BorderStrokeStyle[][]> BORDER_STYLE = 092 new SubCssMetaData<BorderStrokeStyle[][]>("-fx-border-style", 093 LayeredBorderStyleConverter.getInstance()); 094 095 static final CssMetaData<Node,Margins[]> BORDER_WIDTH = 096 new SubCssMetaData<Margins[]> ("-fx-border-width", 097 Margins.SequenceConverter.getInstance()); 098 099 static final CssMetaData<Node,Margins[]> BORDER_RADIUS = 100 new SubCssMetaData<Margins[]>("-fx-border-radius", 101 Margins.SequenceConverter.getInstance()); 102 103 static final CssMetaData<Node,Insets[]> BORDER_INSETS = 104 new SubCssMetaData<Insets[]>("-fx-border-insets", 105 InsetsConverter.SequenceConverter.getInstance()); 106 107 static final CssMetaData<Node,String[]> BORDER_IMAGE_SOURCE = 108 new SubCssMetaData<String[]>("-fx-border-image-source", 109 URLConverter.SequenceConverter.getInstance()); 110 111 static final CssMetaData<Node,RepeatStruct[]> BORDER_IMAGE_REPEAT = 112 new SubCssMetaData<RepeatStruct[]>("-fx-border-image-repeat", 113 RepeatStructConverter.getInstance(), 114 new RepeatStruct[] { new RepeatStruct(BackgroundRepeat.REPEAT, BackgroundRepeat.REPEAT) }); 115 116 static final CssMetaData<Node,BorderImageSlices[]> BORDER_IMAGE_SLICE = 117 new SubCssMetaData<BorderImageSlices[]> ("-fx-border-image-slice", 118 SliceSequenceConverter.getInstance(), 119 new BorderImageSlices[] { BorderImageSlices.DEFAULT}); 120 121 static final CssMetaData<Node,BorderWidths[]> BORDER_IMAGE_WIDTH = 122 new SubCssMetaData<BorderWidths[]>("-fx-border-image-width", 123 BorderImageWidthConverter.getInstance(), 124 new BorderWidths[] { BorderWidths.DEFAULT }); 125 126 static final CssMetaData<Node,Insets[]> BORDER_IMAGE_INSETS = 127 new SubCssMetaData<Insets[]>("-fx-border-image-insets", 128 InsetsConverter.SequenceConverter.getInstance(), 129 new Insets[] {Insets.EMPTY}); 130 131 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES = 132 (List<CssMetaData<? extends Styleable, ?>>) (List) Collections.unmodifiableList( 133 // Unchecked! 134 Arrays.asList(BORDER_COLOR, 135 BORDER_STYLE, 136 BORDER_WIDTH, 137 BORDER_RADIUS, 138 BORDER_INSETS, 139 BORDER_IMAGE_SOURCE, 140 BORDER_IMAGE_REPEAT, 141 BORDER_IMAGE_SLICE, 142 BORDER_IMAGE_WIDTH, 143 BORDER_IMAGE_INSETS)); 144 145 /** 146 * @return The CssMetaData associated with this class, which may include the 147 * CssMetaData of its super classes. 148 */ 149 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 150 return STYLEABLES; 151 } 152 153 /** 154 * An empty Border, useful to use instead of null. 155 */ 156 public static final Border EMPTY = new Border((BorderStroke[])null, null); 157 158 /** 159 * The list of BorderStrokes which together define the stroked portion 160 * of this Border. This List is unmodifiable and immutable. It 161 * will never be null. It will never contain any null elements. 162 */ 163 final List<BorderStroke> strokes; 164 public final List<BorderStroke> getStrokes() { return strokes; } 165 166 /** 167 * The list of BorderImages which together define the images to use 168 * instead of stroke for this Border. If this list is specified and 169 * at least one image within it succeeds in loading, then any specified 170 * {@link #getStrokes strokes} are not drawn. If this list is null or no images 171 * succeeded in loading, then any specified {@code strokes} are drawn. 172 * <p> 173 * This List is unmodifiable and immutable. It will never be null. 174 * It will never contain any null elements. 175 */ 176 final List<BorderImage> images; 177 public final List<BorderImage> getImages() { return images; } 178 179 /** 180 * The outsets of the border define the outer-most edge of the border to be drawn. 181 * The values in these outsets are strictly non-negative. 182 */ 183 final Insets outsets; 184 public final Insets getOutsets() { return outsets; } 185 186 /** 187 * The insets define the distance from the edge of the Region to the inner-most edge 188 * of the border, if that distance is non-negative. The values in these outsets 189 * are strictly non-negative. 190 */ 191 final Insets insets; 192 public final Insets getInsets() { return insets; } 193 194 /** 195 * Gets whether the Border is empty. It is empty if there are no strokes or images. 196 * @return true if the Border is empty, false otherwise. 197 */ 198 public final boolean isEmpty() { 199 return strokes.isEmpty() && images.isEmpty(); 200 } 201 202 /** 203 * The cached hash code computation for the Border. One very big 204 * reason for making Border immutable was to make it possible to 205 * cache and reuse the same Border instance for multiple 206 * Regions. To enable efficient caching, we cache the hash. 207 */ 208 private final int hash; 209 210 /** 211 * Creates a new Border by supplying an array of BorderStrokes. 212 * This array may be null, or may contain null values. Any null values 213 * will be ignored and will not contribute to the {@link #getStrokes() strokes} 214 * or {@link #getOutsets() outsets} or {@link #getInsets() insets}. 215 * 216 * @param strokes The strokes. This may be null, and may contain nulls. Any 217 * contained nulls are filtered out and not included in the 218 * final List of strokes. A null array becomes an empty List. 219 * If both strokes and images are specified, and if any one 220 * of the images specified succeeds in loading, then no 221 * strokes are shown. In this way, strokes can be defined as 222 * a fallback in the case that an image failed to load. 223 */ 224 public Border(BorderStroke... strokes) { 225 this(strokes, null); 226 } 227 228 /** 229 * Creates a new Border by supplying an array of BorderImages. 230 * This array may be null, or may contain null values. Any null values 231 * will be ignored and will not contribute to the {@link #getImages() images} 232 * or {@link #getOutsets() outsets} or {@link #getInsets() insets}. 233 * 234 * @param images The images. This may be null, and may contain nulls. Any 235 * contained nulls are filtered out and not included in the 236 * final List of images. A null array becomes an empty List. 237 */ 238 public Border(BorderImage... images) { 239 this(null, images); 240 } 241 242 /** 243 * Creates a new Border by supplying a List of BorderStrokes and BorderImages. 244 * These Lists may be null, or may contain null values. Any null values 245 * will be ignored and will not contribute to the {@link #getStrokes() strokes} 246 * or {@link #getImages() images}, {@link #getOutsets() outsets}, or 247 * {@link #getInsets() insets}. 248 * 249 * @param strokes The strokes. This may be null, and may contain nulls. Any 250 * contained nulls are filtered out and not included in the 251 * final List of strokes. A null array becomes an empty List. 252 * If both strokes and images are specified, and if any one 253 * of the images specified succeeds in loading, then no 254 * strokes are shown. In this way, strokes can be defined as 255 * a fallback in the case that an image failed to load. 256 * @param images The images. This may be null, and may contain nulls. Any 257 * contained nulls are filtered out and not included in the 258 * final List of images. A null array becomes an empty List. 259 */ 260 public Border(List<BorderStroke> strokes, List<BorderImage> images) { 261 // NOTE: This constructor had to be supplied in order to cause a Builder 262 // to be auto-generated, because otherwise the types of the strokes and images 263 // properties didn't match the types of the array based constructor parameters. 264 // So a Builder will use this constructor, while the CSS engine uses the 265 // array based constructor (for speed). 266 this(strokes == null ? null : strokes.toArray(new BorderStroke[strokes.size()]), 267 images == null ? null : images.toArray(new BorderImage[images.size()])); 268 } 269 270 /** 271 * Creates a new Border by supplying an array of BorderStrokes and BorderImages. 272 * These arrays may be null, or may contain null values. Any null values 273 * will be ignored and will not contribute to the {@link #getStrokes() strokes} 274 * or {@link #getImages() images}, {@link #getOutsets() outsets}, or 275 * {@link #getInsets() insets}. 276 * 277 * @param strokes The strokes. This may be null, and may contain nulls. Any 278 * contained nulls are filtered out and not included in the 279 * final List of strokes. A null array becomes an empty List. 280 * If both strokes and images are specified, and if any one 281 * of the images specified succeeds in loading, then no 282 * strokes are shown. In this way, strokes can be defined as 283 * a fallback in the case that an image failed to load. 284 * @param images The images. This may be null, and may contain nulls. Any 285 * contained nulls are filtered out and not included in the 286 * final List of images. A null array becomes an empty List. 287 */ 288 public Border(BorderStroke[] strokes, BorderImage[] images) { 289 double innerTop = 0, innerRight = 0, innerBottom = 0, innerLeft = 0; 290 double outerTop = 0, outerRight = 0, outerBottom = 0, outerLeft = 0; 291 292 if (strokes == null || strokes.length == 0) { 293 this.strokes = Collections.emptyList(); 294 } else { 295 final BorderStroke[] noNulls = new BorderStroke[strokes.length]; 296 int size = 0; 297 for (int i=0; i<strokes.length; i++) { 298 final BorderStroke stroke = strokes[i]; 299 if (stroke != null) { 300 noNulls[size++] = stroke; 301 302 // Calculate the insets and outsets. "insets" are the distance 303 // from the edge of the region to the inmost edge of the inmost border. 304 // Outsets are the distance from the edge of the region out towards the 305 // outer-most edge of the outer-most border. 306 final double strokeInnerTop = stroke.innerEdge.getTop(); 307 final double strokeInnerRight = stroke.innerEdge.getRight(); 308 final double strokeInnerBottom = stroke.innerEdge.getBottom(); 309 final double strokeInnerLeft = stroke.innerEdge.getLeft(); 310 311 innerTop = innerTop >= strokeInnerTop ? innerTop : strokeInnerTop; 312 innerRight = innerRight >= strokeInnerRight? innerRight : strokeInnerRight; 313 innerBottom = innerBottom >= strokeInnerBottom ? innerBottom : strokeInnerBottom; 314 innerLeft = innerLeft >= strokeInnerLeft ? innerLeft : strokeInnerLeft; 315 316 final double strokeOuterTop = stroke.outerEdge.getTop(); 317 final double strokeOuterRight = stroke.outerEdge.getRight(); 318 final double strokeOuterBottom = stroke.outerEdge.getBottom(); 319 final double strokeOuterLeft = stroke.outerEdge.getLeft(); 320 321 outerTop = outerTop >= strokeOuterTop ? outerTop : strokeOuterTop; 322 outerRight = outerRight >= strokeOuterRight? outerRight : strokeOuterRight; 323 outerBottom = outerBottom >= strokeOuterBottom ? outerBottom : strokeOuterBottom; 324 outerLeft = outerLeft >= strokeOuterLeft ? outerLeft : strokeOuterLeft; 325 } 326 } 327 this.strokes = new UnmodifiableArrayList<BorderStroke>(noNulls, size); 328 } 329 330 if (images == null || images.length == 0) { 331 this.images = Collections.emptyList(); 332 } else { 333 final BorderImage[] noNulls = new BorderImage[images.length]; 334 int size = 0; 335 for (int i=0; i<images.length; i++) { 336 final BorderImage image = images[i]; 337 if (image != null){ 338 noNulls[size++] = image; 339 340 // The Image width + insets may contribute to the insets / outsets of 341 // this border. 342 final double imageInnerTop = image.innerEdge.getTop(); 343 final double imageInnerRight = image.innerEdge.getRight(); 344 final double imageInnerBottom = image.innerEdge.getBottom(); 345 final double imageInnerLeft = image.innerEdge.getLeft(); 346 347 innerTop = innerTop >= imageInnerTop ? innerTop : imageInnerTop; 348 innerRight = innerRight >= imageInnerRight? innerRight : imageInnerRight; 349 innerBottom = innerBottom >= imageInnerBottom ? innerBottom : imageInnerBottom; 350 innerLeft = innerLeft >= imageInnerLeft ? innerLeft : imageInnerLeft; 351 352 final double imageOuterTop = image.outerEdge.getTop(); 353 final double imageOuterRight = image.outerEdge.getRight(); 354 final double imageOuterBottom = image.outerEdge.getBottom(); 355 final double imageOuterLeft = image.outerEdge.getLeft(); 356 357 outerTop = outerTop >= imageOuterTop ? outerTop : imageOuterTop; 358 outerRight = outerRight >= imageOuterRight? outerRight : imageOuterRight; 359 outerBottom = outerBottom >= imageOuterBottom ? outerBottom : imageOuterBottom; 360 outerLeft = outerLeft >= imageOuterLeft ? outerLeft : imageOuterLeft; 361 } 362 } 363 this.images = new UnmodifiableArrayList<BorderImage>(noNulls, size); 364 } 365 366 // Both the BorderStroke and BorderImage class make sure to return the outsets 367 // and insets in the right way, such that we don't have to worry about adjusting 368 // the sign, etc, unlike in the Background implementation. 369 outsets = new Insets(outerTop, outerRight, outerBottom, outerLeft); 370 insets = new Insets(innerTop, innerRight, innerBottom, innerLeft); 371 372 // Pre-compute the hash code. NOTE: all variables are prefixed with "this" so that we 373 // do not accidentally compute the hash based on the constructor arguments rather than 374 // based on the fields themselves! 375 int result = this.strokes.hashCode(); 376 result = 31 * result + this.images.hashCode(); 377 hash = result; 378 } 379 380 /** 381 * @inheritDoc 382 */ 383 @Override public boolean equals(Object o) { 384 if (this == o) return true; 385 if (o == null || getClass() != o.getClass()) return false; 386 Border border = (Border) o; 387 if (this.hash != border.hash) return false; 388 389 if (!images.equals(border.images)) return false; 390 if (!strokes.equals(border.strokes)) return false; 391 392 return true; 393 } 394 395 /** 396 * @inheritDoc 397 */ 398 @Override public int hashCode() { 399 return hash; 400 } 401}