1 /*
   2  * Copyright (c) 2010, 2013, 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 javafx.beans.InvalidationListener;
  29 import javafx.beans.Observable;
  30 import javafx.beans.property.BooleanProperty;
  31 import javafx.beans.property.DoubleProperty;
  32 import javafx.beans.property.ObjectProperty;
  33 import javafx.beans.property.ReadOnlyDoubleProperty;
  34 import javafx.beans.property.ReadOnlyDoubleWrapper;
  35 import javafx.beans.property.ReadOnlyObjectProperty;
  36 import javafx.beans.property.ReadOnlyObjectPropertyBase;
  37 import javafx.beans.value.ChangeListener;
  38 import javafx.collections.ObservableList;
  39 import javafx.css.CssMetaData;
  40 import javafx.css.Styleable;
  41 import javafx.css.StyleableBooleanProperty;
  42 import javafx.css.StyleableDoubleProperty;
  43 import javafx.css.StyleableObjectProperty;
  44 import javafx.css.StyleableProperty;
  45 import javafx.geometry.BoundingBox;
  46 import javafx.geometry.Bounds;
  47 import javafx.geometry.HPos;
  48 import javafx.geometry.Insets;
  49 import javafx.geometry.Orientation;
  50 import javafx.geometry.VPos;
  51 import javafx.scene.Node;
  52 import javafx.scene.Parent;
  53 import javafx.scene.image.Image;
  54 import javafx.scene.shape.Shape;
  55 import javafx.util.Callback;
  56 import java.util.ArrayList;
  57 import java.util.Collections;
  58 import java.util.List;
  59 import java.util.function.Function;
  60 import com.sun.javafx.Logging;
  61 import com.sun.javafx.TempState;
  62 import com.sun.javafx.binding.ExpressionHelper;
  63 import com.sun.javafx.css.converters.BooleanConverter;
  64 import com.sun.javafx.css.converters.InsetsConverter;
  65 import com.sun.javafx.css.converters.ShapeConverter;
  66 import com.sun.javafx.css.converters.SizeConverter;
  67 import com.sun.javafx.geom.BaseBounds;
  68 import com.sun.javafx.geom.PickRay;
  69 import com.sun.javafx.geom.RectBounds;
  70 import com.sun.javafx.geom.Vec2d;
  71 import com.sun.javafx.geom.transform.Affine2D;
  72 import com.sun.javafx.geom.transform.BaseTransform;
  73 import com.sun.javafx.scene.DirtyBits;
  74 import com.sun.javafx.scene.input.PickResultChooser;
  75 import com.sun.javafx.sg.prism.NGNode;
  76 import com.sun.javafx.sg.prism.NGRegion;
  77 import com.sun.javafx.tk.Toolkit;
  78 import sun.util.logging.PlatformLogger;
  79 import sun.util.logging.PlatformLogger.Level;
  80 
  81 /**
  82  * Region is the base class for all JavaFX Node-based UI Controls, and all layout containers.
  83  * It is a resizable Parent node which can be styled from CSS. It can have multiple backgrounds
  84  * and borders. It is designed to support as much of the CSS3 specification for backgrounds
  85  * and borders as is relevant to JavaFX.
  86  * The full specification is available at <a href="http://www.w3.org/TR/2012/CR-css3-background-20120724/">the W3C</a>.
  87  * <p/>
  88  * Every Region has its layout bounds, which are specified to be (0, 0, width, height). A Region might draw outside
  89  * these bounds. The content area of a Region is the area which is occupied for the layout of its children.
  90  * This area is, by default, the same as the layout bounds of the Region, but can be modified by either the
  91  * properties of a border (either with BorderStrokes or BorderImages), and by padding. The padding can
  92  * be negative, such that the content area of a Region might extend beyond the layout bounds of the Region,
  93  * but does not affect the layout bounds.
  94  * <p/>
  95  * A Region has a Background, and a Border, although either or both of these might be empty. The Background
  96  * of a Region is made up of zero or more BackgroundFills, and zero or more BackgroundImages. Likewise, the
  97  * border of a Region is defined by its Border, which is made up of zero or more BorderStrokes and
  98  * zero or more BorderImages. All BackgroundFills are drawn first, followed by BackgroundImages, BorderStrokes,
  99  * and finally BorderImages. The content is drawn above all backgrounds and borders. If a BorderImage is
 100  * present (and loaded all images properly), then no BorderStrokes are actually drawn, although they are
 101  * considered for computing the position of the content area (see the stroke width property of a BorderStroke).
 102  * These semantics are in line with the CSS 3 specification. The purpose of these semantics are to allow an
 103  * application to specify a fallback BorderStroke to be displayed in the case that an ImageStroke fails to
 104  * download or load.
 105  * <p/>
 106  * By default a Region appears as a Rectangle. A BackgroundFill radii might cause the Rectangle to appear rounded.
 107  * This affects not only making the visuals look like a rounded rectangle, but it also causes the picking behavior
 108  * of the Region to act like a rounded rectangle, such that locations outside the corner radii are ignored. A
 109  * Region can be made to use any shape, however, by specifing the {@code shape} property. If a shape is specified,
 110  * then all BackgroundFills, BackgroundImages, and BorderStrokes will be applied to the shape. BorderImages are
 111  * not used for Regions which have a shape specified.
 112  * <p/>
 113  * A Region with a shape
 114  * <p/>
 115  * Although the layout bounds of a Region are not influenced by any Border or Background, the content area
 116  * insets and the picking area of the Region are. The {@code insets} of the Region define the distance
 117  * between the edge of the layout bounds and the edge of the content area. For example, if the Region
 118  * layout bounds are (x=0, y=0, width=200, height=100), and the insets are (top=10, right=20, bottom=30, left=40),
 119  * then the content area bounds will be (x=40, y=10, width=140, height=60). A Region subclass which is laying
 120  * out its children should compute and honor these content area bounds.
 121  * <p/>
 122  * By default a Region inherits the layout behavior of its superclass, {@link Parent},
 123  * which means that it will resize any resizable child nodes to their preferred
 124  * size, but will not reposition them.  If an application needs more specific
 125  * layout behavior, then it should use one of the Region subclasses:
 126  * {@link StackPane}, {@link HBox}, {@link VBox}, {@link TilePane}, {@link FlowPane},
 127  * {@link BorderPane}, {@link GridPane}, or {@link AnchorPane}.
 128  * <p/>
 129  * To implement a more custom layout, a Region subclass must override
 130  * {@link #computePrefWidth(double) computePrefWidth}, {@link #computePrefHeight(double) computePrefHeight}, and
 131  * {@link #layoutChildren() layoutChildren}. Note that {@link #layoutChildren() layoutChildren} is called automatically
 132  * by the scene graph while executing a top-down layout pass and it should not be invoked directly by the
 133  * region subclass.
 134  * <p/>
 135  * Region subclasses which layout their children will position nodes by setting
 136  * {@link #setLayoutX(double) layoutX}/{@link #setLayoutY(double) layoutY} and do not alter
 137  * {@link #setTranslateX(double) translateX}/{@link #setTranslateY(double) translateY}, which are reserved for
 138  * adjustments and animation.
 139  * @since JavaFX 2.0
 140  */
 141 public class Region extends Parent {
 142 
 143     /**
 144      * Sentinel value which can be passed to a region's
 145      * {@link #setMinWidth(double) setMinWidth},
 146      * {@link #setMinHeight(double) setMinHeight},
 147      * {@link #setMaxWidth(double) setMaxWidth} or
 148      * {@link #setMaxHeight(double) setMaxHeight}
 149      * methods to indicate that the preferred dimension should be used for that max and/or min constraint.
 150      */
 151     public static final double USE_PREF_SIZE = Double.NEGATIVE_INFINITY;
 152 
 153     /**
 154      * Sentinel value which can be passed to a region's
 155      * {@link #setMinWidth(double) setMinWidth},
 156      * {@link #setMinHeight(double) setMinHeight},
 157      * {@link #setPrefWidth(double) setPrefWidth},
 158      * {@link #setPrefHeight(double) setPrefHeight},
 159      * {@link #setMaxWidth(double) setMaxWidth},
 160      * {@link #setMaxHeight(double) setMaxHeight} methods
 161      * to reset the region's size constraint back to it's intrinsic size returned
 162      * by {@link #computeMinWidth(double) computeMinWidth}, {@link #computeMinHeight(double) computeMinHeight},
 163      * {@link #computePrefWidth(double) computePrefWidth}, {@link #computePrefHeight(double) computePrefHeight},
 164      * {@link #computeMaxWidth(double) computeMaxWidth}, or {@link #computeMaxHeight(double) computeMaxHeight}.
 165      */
 166     public static final double USE_COMPUTED_SIZE = -1;
 167 
 168     static Vec2d TEMP_VEC2D = new Vec2d();
 169 
 170     /***************************************************************************
 171      *                                                                         *
 172      * Static convenience methods for layout                                   *
 173      *                                                                         *
 174      **************************************************************************/
 175 
 176     /**
 177      * Computes the value based on the given min and max values. We encode in this
 178      * method the logic surrounding various edge cases, such as when the min is
 179      * specified as greater than the max, or the max less than the min, or a pref
 180      * value that exceeds either the max or min in their extremes.
 181      * <p/>
 182      * If the min is greater than the max, then we want to make sure the returned
 183      * value is the min. In other words, in such a case, the min becomes the only
 184      * acceptable return value.
 185      * <p/>
 186      * If the min and max values are well ordered, and the pref is less than the min
 187      * then the min is returned. Likewise, if the values are well ordered and the
 188      * pref is greater than the max, then the max is returned. If the pref lies
 189      * between the min and the max, then the pref is returned.
 190      *
 191      *
 192      * @param min The minimum bound
 193      * @param pref The value to be clamped between the min and max
 194      * @param max the maximum bound
 195      * @return the size bounded by min, pref, and max.
 196      */
 197     static double boundedSize(double min, double pref, double max) {
 198         double a = pref >= min ? pref : min;
 199         double b = min >= max ? min : max;
 200         return a <= b ? a : b;
 201     }
 202 
 203     double adjustWidthByMargin(double width, Insets margin) {
 204         if (margin == null || margin == Insets.EMPTY) {
 205             return width;
 206         }
 207         boolean isSnapToPixel = isSnapToPixel();
 208         return width - snapSpace(margin.getLeft(), isSnapToPixel) - snapSpace(margin.getRight(), isSnapToPixel);
 209     }
 210 
 211     double adjustHeightByMargin(double height, Insets margin) {
 212         if (margin == null || margin == Insets.EMPTY) {
 213             return height;
 214         }
 215         boolean isSnapToPixel = isSnapToPixel();
 216         return height - snapSpace(margin.getTop(), isSnapToPixel) - snapSpace(margin.getBottom(), isSnapToPixel);
 217     }
 218 
 219     /**
 220      * If snapToPixel is true, then the value is rounded using Math.round. Otherwise,
 221      * the value is simply returned. This method will surely be JIT'd under normal
 222      * circumstances, however on an interpreter it would be better to inline this
 223      * method. However the use of Math.round here, and Math.ceil in snapSize is
 224      * not obvious, and so for code maintenance this logic is pulled out into
 225      * a separate method.
 226      *
 227      * @param value The value that needs to be snapped
 228      * @param snapToPixel Whether to snap to pixel
 229      * @return value either as passed in or rounded based on snapToPixel
 230      */
 231     private static double snapSpace(double value, boolean snapToPixel) {
 232         return snapToPixel ? Math.round(value) : value;
 233     }
 234 
 235     /**
 236      * If snapToPixel is true, then the value is ceil'd using Math.ceil. Otherwise,
 237      * the value is simply returned.
 238      *
 239      * @param value The value that needs to be snapped
 240      * @param snapToPixel Whether to snap to pixel
 241      * @return value either as passed in or ceil'd based on snapToPixel
 242      */
 243     private static double snapSize(double value, boolean snapToPixel) {
 244         return snapToPixel ? Math.ceil(value) : value;
 245     }
 246 
 247     /**
 248      * If snapToPixel is true, then the value is rounded using Math.round. Otherwise,
 249      * the value is simply returned.
 250      *
 251      * @param value The value that needs to be snapped
 252      * @param snapToPixel Whether to snap to pixel
 253      * @return value either as passed in or rounded based on snapToPixel
 254      */
 255     private static double snapPosition(double value, boolean snapToPixel) {
 256         return snapToPixel ? Math.round(value) : value;
 257     }
 258 
 259     private static double snapPortion(double value, boolean snapToPixel) {
 260         if (snapToPixel) {
 261             return (value > 0 ? Math.max(1, Math.floor(value)) : Math.min(-1, Math.ceil(value)));
 262         }
 263         return value;
 264     }
 265 
 266     double getAreaBaselineOffset(List<Node> children, Callback<Node, Insets> margins,
 267                                         Function<Integer, Double> positionToWidth,
 268                                         double areaHeight, boolean fillHeight) {
 269         return getAreaBaselineOffset(children, margins, positionToWidth, areaHeight, fillHeight, isSnapToPixel());
 270     }
 271 
 272     static double getAreaBaselineOffset(List<Node> children, Callback<Node, Insets> margins,
 273             Function<Integer, Double> positionToWidth,
 274             double areaHeight, boolean fillHeight, boolean snapToPixel) {
 275         return getAreaBaselineOffset(children, margins, positionToWidth, areaHeight, fillHeight,
 276                 getMinBaselineComplement(children), snapToPixel);
 277     }
 278 
 279     double getAreaBaselineOffset(List<Node> children, Callback<Node, Insets> margins,
 280                                  Function<Integer, Double> positionToWidth,
 281                                  double areaHeight, final boolean fillHeight, double minComplement) {
 282         return getAreaBaselineOffset(children, margins, positionToWidth, areaHeight, fillHeight, minComplement, isSnapToPixel());
 283     }
 284 
 285     static double getAreaBaselineOffset(List<Node> children, Callback<Node, Insets> margins,
 286             Function<Integer, Double> positionToWidth,
 287             double areaHeight, final boolean fillHeight, double minComplement, boolean snapToPixel) {
 288         return getAreaBaselineOffset(children, margins, positionToWidth, areaHeight, new Function<Integer, Boolean>() {
 289 
 290             @Override
 291             public Boolean apply(Integer t) {
 292                 return fillHeight;
 293             }
 294         }, minComplement, snapToPixel);
 295     }
 296 
 297     double getAreaBaselineOffset(List<Node> children, Callback<Node, Insets> margins,
 298                                  Function<Integer, Double> positionToWidth,
 299                                  double areaHeight, Function<Integer, Boolean> fillHeight, double minComplement) {
 300         return getAreaBaselineOffset(children, margins, positionToWidth, areaHeight, fillHeight, minComplement, isSnapToPixel());
 301     }
 302 
 303     /**
 304      * Returns the baseline offset of provided children, with respect to the minimum complement, computed
 305      * by {@link #getMinBaselineComplement(java.util.List)} from the same set of children.
 306      * @param children the children with baseline alignment
 307      * @param margins their margins (callback)
 308      * @param positionToWidth callback for children widths (can return -1 if no bias is used)
 309      * @param areaHeight height of the area to layout in
 310      * @param fillHeight callback to specify children that has fillHeight constraint
 311      * @param minComplement minimum complement
 312      */
 313     static double getAreaBaselineOffset(List<Node> children, Callback<Node, Insets> margins,
 314             Function<Integer, Double> positionToWidth,
 315             double areaHeight, Function<Integer, Boolean> fillHeight, double minComplement, boolean snapToPixel) {
 316         double b = 0;
 317         for (int i = 0;i < children.size(); ++i) {
 318             Node n = children.get(i);
 319             Insets margin = margins.call(n);
 320             double top = margin != null? snapSpace(margin.getTop(), snapToPixel) : 0;
 321             double bottom = (margin != null? snapSpace(margin.getBottom(), snapToPixel) : 0);
 322             final double bo = n.getBaselineOffset();
 323             if (bo == BASELINE_OFFSET_SAME_AS_HEIGHT) {
 324                 double alt = -1;
 325                 if (n.getContentBias() == Orientation.HORIZONTAL) {
 326                     alt = positionToWidth.apply(i);
 327                 }
 328                 if (fillHeight.apply(i)) {
 329                     // If the children fills it's height, than it's "preferred" height is the area without the complement and insets
 330                     b = Math.max(b, top + boundedSize(n.minHeight(alt), areaHeight - minComplement - top - bottom,
 331                             n.maxHeight(alt)));
 332                 } else {
 333                     // Otherwise, we must use the area without complement and insets as a maximum for the Node
 334                     b = Math.max(b, top + boundedSize(n.minHeight(alt), n.prefHeight(alt),
 335                             Math.min(n.maxHeight(alt), areaHeight - minComplement - top - bottom)));
 336                 }
 337             } else {
 338                 b = Math.max(b, top + bo);
 339             }
 340         }
 341         return b;
 342     }
 343 
 344     /**
 345      * Return the minimum complement of baseline
 346      * @param children
 347      * @return
 348      */
 349     static double getMinBaselineComplement(List<Node> children) {
 350         return getBaselineComplement(children, true, false);
 351     }
 352 
 353     /**
 354      * Return the preferred complement of baseline
 355      * @param children
 356      * @return
 357      */
 358     static double getPrefBaselineComplement(List<Node> children) {
 359         return getBaselineComplement(children, false, false);
 360     }
 361 
 362     /**
 363      * Return the maximal complement of baseline
 364      * @param children
 365      * @return
 366      */
 367     static double getMaxBaselineComplement(List<Node> children) {
 368         return getBaselineComplement(children, false, true);
 369     }
 370 
 371     private static double getBaselineComplement(List<Node> children, boolean min, boolean max) {
 372         double bc = 0;
 373         for (Node n : children) {
 374             final double bo = n.getBaselineOffset();
 375             if (bo == BASELINE_OFFSET_SAME_AS_HEIGHT) {
 376                 continue;
 377             }
 378             if (n.isResizable()) {
 379                 bc = Math.max(bc, (min ? n.minHeight(-1) : max ? n.maxHeight(-1) : n.prefHeight(-1)) - bo);
 380             } else {
 381                 bc = Math.max(bc, n.getLayoutBounds().getHeight() - bo);
 382             }
 383         }
 384         return bc;
 385     }
 386 
 387 
 388     static double computeXOffset(double width, double contentWidth, HPos hpos) {
 389         switch(hpos) {
 390             case LEFT:
 391                 return 0;
 392             case CENTER:
 393                 return (width - contentWidth) / 2;
 394             case RIGHT:
 395                 return width - contentWidth;
 396             default:
 397                 throw new AssertionError("Unhandled hPos");
 398         }
 399     }
 400 
 401     static double computeYOffset(double height, double contentHeight, VPos vpos) {
 402         switch(vpos) {
 403             case BASELINE:
 404             case TOP:
 405                 return 0;
 406             case CENTER:
 407                 return (height - contentHeight) / 2;
 408             case BOTTOM:
 409                 return height - contentHeight;
 410             default:
 411                 throw new AssertionError("Unhandled vPos");
 412         }
 413     }
 414 
 415     static double[] createDoubleArray(int length, double value) {
 416         double[] array = new double[length];
 417         for (int i = 0; i < length; i++) {
 418             array[i] = value;
 419         }
 420         return array;
 421     }
 422 
 423 
 424     /***************************************************************************
 425      *                                                                         *
 426      * Constructors                                                            *
 427      *                                                                         *
 428      **************************************************************************/
 429 
 430     /**
 431      * At the time that a Background or Border is set on a Region, we inspect any
 432      * BackgroundImage or BorderImage objects, to see if the Image backing them
 433      * is background loading and not yet complete, or is animated. In such cases
 434      * we attach the imageChangeListener to them, so that when the image finishes,
 435      * the Region will be redrawn. If the particular image object is not animating
 436      * (but was just background loading), then we also remove the listener.
 437      * We also are sure to remove this listener from any old BackgroundImage or
 438      * BorderImage images in the background and border property invalidation code.
 439      */
 440     private InvalidationListener imageChangeListener = new InvalidationListener() {
 441         @Override public void invalidated(Observable observable) {
 442             final ReadOnlyObjectPropertyBase imageProperty = (ReadOnlyObjectPropertyBase) observable;
 443             final Image image = (Image) imageProperty.getBean();
 444             final Toolkit.ImageAccessor acc = Toolkit.getImageAccessor();
 445             if (image.getProgress() == 1 && !acc.isAnimation(image)) {
 446                 // We can go ahead and remove the listener since loading is done.
 447                 removeImageListener(image);
 448             }
 449             // Cause the region to repaint
 450             impl_markDirty(DirtyBits.NODE_CONTENTS);
 451         }
 452     };
 453 
 454     /**
 455      * Creates a new Region with an empty Background and and empty Border. The
 456      * Region defaults to having pickOnBounds set to true, meaning that any pick
 457      * (mouse picking or touch picking etc) that occurs within the bounds in local
 458      * of the Region will return true, regardless of whether the Region is filled
 459      * or transparent.
 460      */
 461     public Region() {
 462         super();
 463         setPickOnBounds(true);
 464     }
 465 
 466     /***************************************************************************
 467      *                                                                         *
 468      * Region properties                                                       *
 469      *                                                                         *
 470      **************************************************************************/
 471 
 472     /**
 473      * Defines whether this region adjusts position, spacing, and size values of
 474      * its children to pixel boundaries. This defaults to true, which is generally
 475      * the expected behavior in order to have crisp user interfaces. A value of
 476      * false will allow for fractional alignment, which may lead to "fuzzy"
 477      * looking borders.
 478      */
 479     private BooleanProperty snapToPixel;
 480     /**
 481      * I'm using a super-lazy property pattern here, so as to only create the
 482      * property object when needed for listeners or when being set from CSS,
 483      * but also making sure that we only call requestParentLayout in the case
 484      * that the snapToPixel value has actually changed, whether set via the setter
 485      * or set via the property object.
 486      */
 487     private boolean _snapToPixel = true;
 488     public final boolean isSnapToPixel() { return _snapToPixel; }
 489     public final void setSnapToPixel(boolean value) {
 490         if (snapToPixel == null) {
 491             if (_snapToPixel != value) {
 492                 _snapToPixel = value;
 493                 updateSnappedInsets();
 494                 requestParentLayout();
 495             }
 496         } else {
 497             snapToPixel.set(value);
 498         }
 499     }
 500     public final BooleanProperty snapToPixelProperty() {
 501         // Note: snapToPixel is virtually never set, and never listened to.
 502         // Because of this, it works reasonably well as a lazy property,
 503         // since this logic is just about never going to be called.
 504         if (snapToPixel == null) {
 505             snapToPixel = new StyleableBooleanProperty(_snapToPixel) {
 506                 @Override public Object getBean() { return Region.this; }
 507                 @Override public String getName() { return "snapToPixel"; }
 508                 @Override public CssMetaData<Region, Boolean> getCssMetaData() {
 509                     return StyleableProperties.SNAP_TO_PIXEL;
 510                 }
 511                 @Override public void invalidated() {
 512                     boolean value = get();
 513                     if (_snapToPixel != value) {
 514                         _snapToPixel = value;
 515                         updateSnappedInsets();
 516                         requestParentLayout();
 517                     }
 518                 }
 519             };
 520         }
 521         return snapToPixel;
 522     }
 523 
 524     /**
 525      * The top, right, bottom, and left padding around the region's content.
 526      * This space will be included in the calculation of the region's
 527      * minimum and preferred sizes. By default padding is Insets.EMPTY. Setting the
 528      * value to null should be avoided.
 529      */
 530     private ObjectProperty<Insets> padding = new StyleableObjectProperty<Insets>(Insets.EMPTY) {
 531         // Keep track of the last valid value for the sake of
 532         // rollback in case padding is set to null. Note that
 533         // Richard really does not like this pattern because
 534         // it essentially means that binding the padding property
 535         // is not possible since a binding expression could very
 536         // easily produce an intermediate null value.
 537 
 538         // Also note that because padding is set virtually everywhere via CSS, and CSS
 539         // requires a property object in order to set it, there is no benefit to having
 540         // lazy initialization here.
 541 
 542         private Insets lastValidValue = Insets.EMPTY;
 543 
 544         @Override public Object getBean() { return Region.this; }
 545         @Override public String getName() { return "padding"; }
 546         @Override public CssMetaData<Region, Insets> getCssMetaData() {
 547             return StyleableProperties.PADDING;
 548         }
 549         @Override public void invalidated() {
 550             final Insets newValue = get();
 551             if (newValue == null) {
 552                 // rollback
 553                 if (isBound()) {
 554                     unbind();
 555                 }
 556                 set(lastValidValue);
 557                 throw new NullPointerException("cannot set padding to null");
 558             } else if (!newValue.equals(lastValidValue)) {
 559                 lastValidValue = newValue;
 560                 insets.fireValueChanged();
 561             }
 562         }
 563     };
 564     public final void setPadding(Insets value) { padding.set(value); }
 565     public final Insets getPadding() { return padding.get(); }
 566     public final ObjectProperty<Insets> paddingProperty() { return padding; }
 567 
 568     /**
 569      * The background of the Region, which is made up of zero or more BackgroundFills, and
 570      * zero or more BackgroundImages. It is possible for a Background to be empty, where it
 571      * has neither fills nor images, and is semantically equivalent to null.
 572      * @since JavaFX 8.0
 573      */
 574     private final ObjectProperty<Background> background = new StyleableObjectProperty<Background>(null) {
 575         private Background old = null;
 576         @Override public Object getBean() { return Region.this; }
 577         @Override public String getName() { return "background"; }
 578         @Override public CssMetaData<Region, Background> getCssMetaData() {
 579             return StyleableProperties.BACKGROUND;
 580         }
 581 
 582         @Override protected void invalidated() {
 583             final Background b = get();
 584             if(old != null ? !old.equals(b) : b != null) {
 585                 // They are different! Both cannot be null
 586                 if (old == null || b == null || !old.getOutsets().equals(b.getOutsets())) {
 587                     // We have determined that the outsets of these two different background
 588                     // objects is different, and therefore the bounds have changed.
 589                     impl_geomChanged();
 590                     insets.fireValueChanged();
 591                 }
 592 
 593                 // If the Background is made up of any BackgroundImage objects, then we must
 594                 // inspect the images of those BackgroundImage objects to see if they are still
 595                 // being loaded in the background or if they are animated. If so, then we need
 596                 // to attach a listener, so that when the image finishes loading or changes,
 597                 // we can repaint the region.
 598                 if (b != null) {
 599                     for (BackgroundImage i : b.getImages()) {
 600                         final Image image = i.image;
 601                         final Toolkit.ImageAccessor acc = Toolkit.getImageAccessor();
 602                         if (acc.isAnimation(image) || image.getProgress() < 1) {
 603                             addImageListener(image);
 604                         }
 605                     }
 606                 }
 607 
 608                 // And we must remove this listener from any old images
 609                 if (old != null) {
 610                     for (BackgroundImage i : old.getImages()) {
 611                         removeImageListener(i.image);
 612                     }
 613                 }
 614 
 615                 // No matter what, the fill has changed, so we have to update it
 616                 impl_markDirty(DirtyBits.SHAPE_FILL);
 617                 old = b;
 618             }
 619         }
 620     };
 621     public final void setBackground(Background value) { background.set(value); }
 622     public final Background getBackground() { return background.get(); }
 623     public final ObjectProperty<Background> backgroundProperty() { return background; }
 624 
 625     /**
 626      * The border of the Region, which is made up of zero or more BorderStrokes, and
 627      * zero or more BorderImages. It is possible for a Border to be empty, where it
 628      * has neither strokes nor images, and is semantically equivalent to null.
 629      * @since JavaFX 8.0
 630      */
 631     private final ObjectProperty<Border> border = new StyleableObjectProperty<Border>(null) {
 632         private Border old = null;
 633         @Override public Object getBean() { return Region.this; }
 634         @Override public String getName() { return "border"; }
 635         @Override public CssMetaData<Region, Border> getCssMetaData() {
 636             return StyleableProperties.BORDER;
 637         }
 638         @Override protected void invalidated() {
 639             final Border b = get();
 640             if(old != null ? !old.equals(b) : b != null) {
 641                 // They are different! Both cannot be null
 642                 if (old == null || b == null || !old.getOutsets().equals(b.getOutsets())) {
 643                     // We have determined that the outsets of these two different border
 644                     // objects is different, and therefore the bounds have changed.
 645                     impl_geomChanged();
 646                     insets.fireValueChanged();
 647                 }
 648 
 649                 // If the Border is made up of any BorderImage objects, then we must
 650                 // inspect the images of those BorderImage objects to see if they are still
 651                 // being loaded in the background or if they are animated. If so, then we need
 652                 // to attach a listener, so that when the image finishes loading or changes,
 653                 // we can repaint the region.
 654                 if (b != null) {
 655                     for (BorderImage i : b.getImages()) {
 656                         final Image image = i.image;
 657                         final Toolkit.ImageAccessor acc = Toolkit.getImageAccessor();
 658                         if (acc.isAnimation(image) || image.getProgress() < 1) {
 659                             addImageListener(image);
 660                         }
 661                     }
 662                 }
 663 
 664                 // And we must remove this listener from any old images
 665                 if (old != null) {
 666                     for (BorderImage i : old.getImages()) {
 667                         removeImageListener(i.image);
 668                     }
 669                 }
 670 
 671                 // No matter what, the fill has changed, so we have to update it
 672                 impl_markDirty(DirtyBits.SHAPE_STROKE);
 673                 old = b;
 674             }
 675         }
 676     };
 677     public final void setBorder(Border value) { border.set(value); }
 678     public final Border getBorder() { return border.get(); }
 679     public final ObjectProperty<Border> borderProperty() { return border; }
 680 
 681     /**
 682      * Adds the imageChangeListener to this image. This method was broken out and made
 683      * package private for testing purposes.
 684      *
 685      * @param image a non-null image
 686      */
 687     void addImageListener(Image image) {
 688         final Toolkit.ImageAccessor acc = Toolkit.getImageAccessor();
 689         acc.getImageProperty(image).addListener(imageChangeListener);
 690     }
 691 
 692     /**
 693      * Removes the imageChangeListener from this image. This method was broken out and made
 694      * package private for testing purposes.
 695      *
 696      * @param image a non-null image
 697      */
 698     void removeImageListener(Image image) {
 699         final Toolkit.ImageAccessor acc = Toolkit.getImageAccessor();
 700         acc.getImageProperty(image).removeListener(imageChangeListener);
 701     }
 702 
 703     /**
 704      * Defines the area of the region within which completely opaque pixels
 705      * are drawn. This is used for various performance optimizations.
 706      * The pixels within this area MUST BE fully opaque, or rendering
 707      * artifacts will result. It is the responsibility of the application, either
 708      * via code or via CSS, to ensure that the opaqueInsets is correct for
 709      * a Region based on the backgrounds and borders of that region. The values
 710      * for each of the insets must be real numbers, not NaN or Infinity. If
 711      * no known insets exist, then the opaqueInsets should be set to null.
 712      * @since JavaFX 8.0
 713      */
 714     public final ObjectProperty<Insets> opaqueInsetsProperty() {
 715         if (opaqueInsets == null) {
 716             opaqueInsets = new StyleableObjectProperty<Insets>() {
 717                 @Override public Object getBean() { return Region.this; }
 718                 @Override public String getName() { return "opaqueInsets"; }
 719                 @Override public CssMetaData<Region, Insets> getCssMetaData() {
 720                     return StyleableProperties.OPAQUE_INSETS;
 721                 }
 722                 @Override protected void invalidated() {
 723                     // This causes the background to be updated, which
 724                     // is the code block where we also compute the opaque insets
 725                     // since updating the background is super fast even when
 726                     // nothing has changed.
 727                     impl_markDirty(DirtyBits.SHAPE_FILL);
 728                 }
 729             };
 730         }
 731         return opaqueInsets;
 732     }
 733     private ObjectProperty<Insets> opaqueInsets;
 734     public final void setOpaqueInsets(Insets value) { opaqueInsetsProperty().set(value); }
 735     public final Insets getOpaqueInsets() { return opaqueInsets == null ? null : opaqueInsets.get(); }
 736 
 737     /**
 738      * The insets of the Region define the distance from the edge of the region (its layout bounds,
 739      * or (0, 0, width, height)) to the edge of the content area. All child nodes should be laid out
 740      * within the content area. The insets are computed based on the Border which has been specified,
 741      * if any, and also the padding.
 742      * @since JavaFX 8.0
 743      */
 744     private final InsetsProperty insets = new InsetsProperty();
 745     public final Insets getInsets() { return insets.get(); }
 746     public final ReadOnlyObjectProperty<Insets> insetsProperty() { return insets; }
 747     private final class InsetsProperty extends ReadOnlyObjectProperty<Insets> {
 748         private Insets cache = null;
 749         private ExpressionHelper<Insets> helper = null;
 750 
 751         @Override public Object getBean() { return Region.this; }
 752         @Override public String getName() { return "insets"; }
 753 
 754         @Override public void addListener(InvalidationListener listener) {
 755             helper = ExpressionHelper.addListener(helper, this, listener);
 756         }
 757 
 758         @Override public void removeListener(InvalidationListener listener) {
 759             helper = ExpressionHelper.removeListener(helper, listener);
 760         }
 761 
 762         @Override public void addListener(ChangeListener<? super Insets> listener) {
 763             helper = ExpressionHelper.addListener(helper, this, listener);
 764         }
 765 
 766         @Override public void removeListener(ChangeListener<? super Insets> listener) {
 767             helper = ExpressionHelper.removeListener(helper, listener);
 768         }
 769 
 770         void fireValueChanged() {
 771             cache = null;
 772             updateSnappedInsets();
 773             requestLayout();
 774             ExpressionHelper.fireValueChangedEvent(helper);
 775         }
 776 
 777         @Override public Insets get() {
 778             // If a shape is specified, then we don't really care whether there are any borders
 779             // specified, since borders of shapes do not contribute to the insets.
 780             if (_shape != null) return getPadding();
 781 
 782             // If there is no border or the border has no insets itself, then the only thing
 783             // affecting the insets is the padding, so we can just return it directly.
 784             final Border b = getBorder();
 785             if (b == null || Insets.EMPTY.equals(b.getInsets())) {
 786                 return getPadding();
 787             }
 788 
 789             // There is a border with some non-zero insets and we do not have a _shape, so we need
 790             // to take the border's insets into account
 791             if (cache == null) {
 792                 // Combine the padding and the border insets.
 793                 // TODO note that negative border insets were being ignored, but
 794                 // I'm not sure that that made sense or was reasonable, so I have
 795                 // changed it so that we just do simple math.
 796                 // TODO Stroke borders should NOT contribute to the insets. Ensure via tests.
 797                 final Insets borderInsets = b.getInsets();
 798                 final Insets paddingInsets = getPadding();
 799                 cache = new Insets(
 800                         borderInsets.getTop() + paddingInsets.getTop(),
 801                         borderInsets.getRight() + paddingInsets.getRight(),
 802                         borderInsets.getBottom() + paddingInsets.getBottom(),
 803                         borderInsets.getLeft() + paddingInsets.getLeft()
 804                 );
 805             }
 806             return cache;
 807         }
 808     };
 809 
 810     /**
 811      * cached results of snapped insets, this are used a lot during layout so makes sense
 812      * to keep fast access cached copies here.
 813      */
 814     private double snappedTopInset = 0;
 815     private double snappedRightInset = 0;
 816     private double snappedBottomInset = 0;
 817     private double snappedLeftInset = 0;
 818 
 819     /** Called to update the cached snapped insets */
 820     private void updateSnappedInsets() {
 821         final Insets insets = getInsets();
 822         if (_snapToPixel) {
 823             snappedTopInset = Math.ceil(insets.getTop());
 824             snappedRightInset = Math.ceil(insets.getRight());
 825             snappedBottomInset = Math.ceil(insets.getBottom());
 826             snappedLeftInset = Math.ceil(insets.getLeft());
 827         } else {
 828             snappedTopInset = insets.getTop();
 829             snappedRightInset = insets.getRight();
 830             snappedBottomInset = insets.getBottom();
 831             snappedLeftInset = insets.getLeft();
 832         }
 833     }
 834 
 835     /**
 836     * The width of this resizable node.  This property is set by the region's parent
 837     * during layout and may not be set by the application.  If an application
 838     * needs to explicitly control the size of a region, it should override its
 839     * preferred size range by setting the <code>minWidth</code>, <code>prefWidth</code>,
 840     * and <code>maxWidth</code> properties.
 841     */
 842     private ReadOnlyDoubleWrapper width;
 843 
 844     /**
 845      * Because the width is very often set and very often read but only sometimes
 846      * listened to, it is beneficial to use the super-lazy pattern property, where we
 847      * only inflate the property object when widthProperty() is explicitly invoked.
 848      */
 849     private double _width;
 850 
 851     // Note that it is OK for this method to be protected so long as the width
 852     // property is never bound. Only Region could do so because only Region has
 853     // access to a writable property for "width", but since there is now a protected
 854     // set method, it is impossible for Region to ever bind this property.
 855     protected void setWidth(double value) {
 856         if(width == null) {
 857             widthChanged(value);
 858         } else {
 859             width.set(value);
 860         }
 861     }
 862 
 863     private void widthChanged(double value) {
 864         // It is possible that somebody sets the width of the region to a value which
 865         // it previously held. If this is the case, we want to avoid excessive layouts.
 866         // Note that I have biased this for layout over binding, because the widthProperty
 867         // is now going to recompute the width eagerly. The cost of excessive and
 868         // unnecessary bounds changes, however, is relatively high.
 869         if (value != _width) {
 870             _width = value;
 871             boundingBox = null;
 872             scaledShape = null;
 873             impl_layoutBoundsChanged();
 874             impl_geomChanged();
 875             impl_markDirty(DirtyBits.NODE_GEOMETRY);
 876             setNeedsLayout(true);
 877             requestParentLayout();
 878         }
 879     }
 880 
 881     public final double getWidth() { return width == null ? _width : width.get(); }
 882 
 883     public final ReadOnlyDoubleProperty widthProperty() {
 884         if (width == null) {
 885             width = new ReadOnlyDoubleWrapper(_width) {
 886                 @Override protected void invalidated() { widthChanged(get()); }
 887                 @Override public Object getBean() { return Region.this; }
 888                 @Override public String getName() { return "width"; }
 889             };
 890         }
 891         return width.getReadOnlyProperty();
 892     }
 893 
 894     /**
 895      * The height of this resizable node.  This property is set by the region's parent
 896      * during layout and may not be set by the application.  If an application
 897      * needs to explicitly control the size of a region, it should override its
 898      * preferred size range by setting the <code>minHeight</code>, <code>prefHeight</code>,
 899      * and <code>maxHeight</code> properties.
 900      */
 901     private ReadOnlyDoubleWrapper height;
 902 
 903     /**
 904      * Because the height is very often set and very often read but only sometimes
 905      * listened to, it is beneficial to use the super-lazy pattern property, where we
 906      * only inflate the property object when heightProperty() is explicitly invoked.
 907      */
 908     private double _height;
 909 
 910     // Note that it is OK for this method to be protected so long as the height
 911     // property is never bound. Only Region could do so because only Region has
 912     // access to a writable property for "height", but since there is now a protected
 913     // set method, it is impossible for Region to ever bind this property.
 914     protected void setHeight(double value) {
 915         if (height == null) {
 916             heightChanged(value);
 917         } else {
 918             height.set(value);
 919         }
 920     }
 921 
 922     private void heightChanged(double value) {
 923         if (_height != value) {
 924             _height = value;
 925             // It is possible that somebody sets the height of the region to a value which
 926             // it previously held. If this is the case, we want to avoid excessive layouts.
 927             // Note that I have biased this for layout over binding, because the heightProperty
 928             // is now going to recompute the height eagerly. The cost of excessive and
 929             // unnecessary bounds changes, however, is relatively high.
 930             boundingBox = null;
 931             // Scaled shape might depend on width/height if it's centered or scaled.
 932             scaledShape = null;
 933             // Note: although impl_geomChanged will usually also invalidate the
 934             // layout bounds, that is not the case for Regions, and both must
 935             // be called separately.
 936             impl_geomChanged();
 937             impl_layoutBoundsChanged();
 938             // We use "NODE_GEOMETRY" to mean that the bounds have changed and
 939             // need to be sync'd with the render tree
 940             impl_markDirty(DirtyBits.NODE_GEOMETRY);
 941             // Change of the height (or width) won't change the preferred size.
 942             // So we don't need to flush the cache. We should however mark this node
 943             // as needs layout to be internally layouted.
 944             setNeedsLayout(true);
 945             // This call is only needed when this was not called from the parent during the layout.
 946             // Otherwise it would flush the cache of the parent, which is not necessary
 947             requestParentLayout();
 948         }
 949     }
 950 
 951     public final double getHeight() { return height == null ? _height : height.get(); }
 952 
 953     public final ReadOnlyDoubleProperty heightProperty() {
 954         if (height == null) {
 955             height = new ReadOnlyDoubleWrapper(_height) {
 956                 @Override protected void invalidated() { heightChanged(get()); }
 957                 @Override public Object getBean() { return Region.this; }
 958                 @Override public String getName() { return "height"; }
 959             };
 960         }
 961         return height.getReadOnlyProperty();
 962     }
 963 
 964     /**
 965      * This class is reused for the min, pref, and max properties since
 966      * they all performed the same function (to call requestParentLayout).
 967      */
 968     private final class MinPrefMaxProperty extends StyleableDoubleProperty {
 969         private final String name;
 970         private final CssMetaData<? extends Styleable, Number> cssMetaData;
 971 
 972         MinPrefMaxProperty(String name, double initialValue, CssMetaData<? extends Styleable, Number> cssMetaData) {
 973             super(initialValue);
 974             this.name = name;
 975             this.cssMetaData = cssMetaData;
 976         }
 977 
 978         @Override public void invalidated() { requestParentLayout(); }
 979         @Override public Object getBean() { return Region.this; }
 980         @Override public String getName() { return name; }
 981 
 982         @Override
 983         public CssMetaData<? extends Styleable, Number> getCssMetaData() {
 984             return cssMetaData;
 985         }
 986     }
 987 
 988     /**
 989      * Property for overriding the region's computed minimum width.
 990      * This should only be set if the region's internally computed minimum width
 991      * doesn't meet the application's layout needs.
 992      * <p>
 993      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 994      * <code>minWidth(forHeight)</code> will return the region's internally
 995      * computed minimum width.
 996      * <p>
 997      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 998      * <code>minWidth(forHeight)</code> to return the region's preferred width,
 999      * enabling applications to easily restrict the resizability of the region.
1000      */
1001     private DoubleProperty minWidth;
1002     private double _minWidth = USE_COMPUTED_SIZE;
1003     public final void setMinWidth(double value) {
1004         if (minWidth == null) {
1005             _minWidth = value;
1006             requestParentLayout();
1007         } else {
1008             minWidth.set(value);
1009         }
1010     }
1011     public final double getMinWidth() { return minWidth == null ? _minWidth : minWidth.get(); }
1012     public final DoubleProperty minWidthProperty() {
1013         if (minWidth == null) minWidth = new MinPrefMaxProperty("minWidth", _minWidth, StyleableProperties.MIN_WIDTH);
1014         return minWidth;
1015     }
1016 
1017     /**
1018      * Property for overriding the region's computed minimum height.
1019      * This should only be set if the region's internally computed minimum height
1020      * doesn't meet the application's layout needs.
1021      * <p>
1022      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
1023      * <code>minHeight(forWidth)</code> will return the region's internally
1024      * computed minimum height.
1025      * <p>
1026      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
1027      * <code>minHeight(forWidth)</code> to return the region's preferred height,
1028      * enabling applications to easily restrict the resizability of the region.
1029      *
1030      */
1031     private DoubleProperty minHeight;
1032     private double _minHeight = USE_COMPUTED_SIZE;
1033     public final void setMinHeight(double value) {
1034         if (minHeight == null) {
1035             _minHeight = value;
1036             requestParentLayout();
1037         } else {
1038             minHeight.set(value);
1039         }
1040     }
1041     public final double getMinHeight() { return minHeight == null ? _minHeight : minHeight.get(); }
1042     public final DoubleProperty minHeightProperty() {
1043         if (minHeight == null) minHeight = new MinPrefMaxProperty("minHeight", _minHeight, StyleableProperties.MIN_HEIGHT);
1044         return minHeight;
1045     }
1046 
1047     /**
1048      * Convenience method for overriding the region's computed minimum width and height.
1049      * This should only be called if the region's internally computed minimum size
1050      * doesn't meet the application's layout needs.
1051      *
1052      * @see #setMinWidth
1053      * @see #setMinHeight
1054      * @param minWidth  the override value for minimum width
1055      * @param minHeight the override value for minimum height
1056      */
1057     public void setMinSize(double minWidth, double minHeight) {
1058         setMinWidth(minWidth);
1059         setMinHeight(minHeight);
1060     }
1061 
1062     /**
1063      * Property for overriding the region's computed preferred width.
1064      * This should only be set if the region's internally computed preferred width
1065      * doesn't meet the application's layout needs.
1066      * <p>
1067      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
1068      * <code>getPrefWidth(forHeight)</code> will return the region's internally
1069      * computed preferred width.
1070      */
1071     private DoubleProperty prefWidth;
1072     private double _prefWidth = USE_COMPUTED_SIZE;
1073     public final void setPrefWidth(double value) {
1074         if (prefWidth == null) {
1075             _prefWidth = value;
1076             requestParentLayout();
1077         } else {
1078             prefWidth.set(value);
1079         }
1080     }
1081     public final double getPrefWidth() { return prefWidth == null ? _prefWidth : prefWidth.get(); }
1082     public final DoubleProperty prefWidthProperty() {
1083         if (prefWidth == null) prefWidth = new MinPrefMaxProperty("prefWidth", _prefWidth, StyleableProperties.PREF_WIDTH);
1084         return prefWidth;
1085     }
1086 
1087     /**
1088      * Property for overriding the region's computed preferred height.
1089      * This should only be set if the region's internally computed preferred height
1090      * doesn't meet the application's layout needs.
1091      * <p>
1092      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
1093      * <code>getPrefHeight(forWidth)</code> will return the region's internally
1094      * computed preferred width.
1095      */
1096     private DoubleProperty prefHeight;
1097     private double _prefHeight = USE_COMPUTED_SIZE;
1098     public final void setPrefHeight(double value) {
1099         if (prefHeight == null) {
1100             _prefHeight = value;
1101             requestParentLayout();
1102         } else {
1103             prefHeight.set(value);
1104         }
1105     }
1106     public final double getPrefHeight() { return prefHeight == null ? _prefHeight : prefHeight.get(); }
1107     public final DoubleProperty prefHeightProperty() {
1108         if (prefHeight == null) prefHeight = new MinPrefMaxProperty("prefHeight", _prefHeight, StyleableProperties.PREF_HEIGHT);
1109         return prefHeight;
1110     }
1111 
1112     /**
1113      * Convenience method for overriding the region's computed preferred width and height.
1114      * This should only be called if the region's internally computed preferred size
1115      * doesn't meet the application's layout needs.
1116      *
1117      * @see #setPrefWidth
1118      * @see #setPrefHeight
1119      * @param prefWidth the override value for preferred width
1120      * @param prefHeight the override value for preferred height
1121      */
1122     public void setPrefSize(double prefWidth, double prefHeight) {
1123         setPrefWidth(prefWidth);
1124         setPrefHeight(prefHeight);
1125     }
1126 
1127     /**
1128      * Property for overriding the region's computed maximum width.
1129      * This should only be set if the region's internally computed maximum width
1130      * doesn't meet the application's layout needs.
1131      * <p>
1132      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
1133      * <code>getMaxWidth(forHeight)</code> will return the region's internally
1134      * computed maximum width.
1135      * <p>
1136      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
1137      * <code>getMaxWidth(forHeight)</code> to return the region's preferred width,
1138      * enabling applications to easily restrict the resizability of the region.
1139      */
1140     private DoubleProperty maxWidth;
1141     private double _maxWidth = USE_COMPUTED_SIZE;
1142     public final void setMaxWidth(double value) {
1143         if (maxWidth == null) {
1144             _maxWidth = value;
1145             requestParentLayout();
1146         } else {
1147             maxWidth.set(value);
1148         }
1149     }
1150     public final double getMaxWidth() { return maxWidth == null ? _maxWidth : maxWidth.get(); }
1151     public final DoubleProperty maxWidthProperty() {
1152         if (maxWidth == null) maxWidth = new MinPrefMaxProperty("maxWidth", _maxWidth, StyleableProperties.MAX_WIDTH);
1153         return maxWidth;
1154     }
1155 
1156     /**
1157      * Property for overriding the region's computed maximum height.
1158      * This should only be set if the region's internally computed maximum height
1159      * doesn't meet the application's layout needs.
1160      * <p>
1161      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
1162      * <code>getMaxHeight(forWidth)</code> will return the region's internally
1163      * computed maximum height.
1164      * <p>
1165      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
1166      * <code>getMaxHeight(forWidth)</code> to return the region's preferred height,
1167      * enabling applications to easily restrict the resizability of the region.
1168      */
1169     private DoubleProperty maxHeight;
1170     private double _maxHeight = USE_COMPUTED_SIZE;
1171     public final void setMaxHeight(double value) {
1172         if (maxHeight == null) {
1173             _maxHeight = value;
1174             requestParentLayout();
1175         } else {
1176             maxHeight.set(value);
1177         }
1178     }
1179     public final double getMaxHeight() { return maxHeight == null ? _maxHeight : maxHeight.get(); }
1180     public final DoubleProperty maxHeightProperty() {
1181         if (maxHeight == null) maxHeight = new MinPrefMaxProperty("maxHeight", _maxHeight, StyleableProperties.MAX_HEIGHT);
1182         return maxHeight;
1183     }
1184 
1185     /**
1186      * Convenience method for overriding the region's computed maximum width and height.
1187      * This should only be called if the region's internally computed maximum size
1188      * doesn't meet the application's layout needs.
1189      *
1190      * @see #setMaxWidth
1191      * @see #setMaxHeight
1192      * @param maxWidth  the override value for maximum width
1193      * @param maxHeight the override value for maximum height
1194      */
1195     public void setMaxSize(double maxWidth, double maxHeight) {
1196         setMaxWidth(maxWidth);
1197         setMaxHeight(maxHeight);
1198     }
1199 
1200     /**
1201      * When specified, the {@code shape} will cause the region to be
1202      * rendered as the specified shape rather than as a rounded rectangle.
1203      * When null, the Region is rendered as a rounded rectangle. When rendered
1204      * as a Shape, any Background is used to fill the shape, although any
1205      * background insets are ignored as are background radii. Any BorderStrokes
1206      * defined are used for stroking the shape. Any BorderImages are ignored.
1207      *
1208      * @default null
1209      * @css shape SVG shape string
1210      * @since JavaFX 8.0
1211      */
1212     private ObjectProperty<Shape> shape = null;
1213     private Shape _shape;
1214     public final Shape getShape() { return shape == null ? _shape : shape.get(); }
1215     public final void setShape(Shape value) { shapeProperty().set(value); }
1216     public final ObjectProperty<Shape> shapeProperty() {
1217         if (shape == null) {
1218             shape = new ShapeProperty();
1219         }
1220         return shape;
1221     }
1222 
1223     /**
1224      * An implementation for the ShapeProperty. This is also a ShapeChangeListener.
1225      */
1226     private final class ShapeProperty extends StyleableObjectProperty<Shape> implements Runnable {
1227         @Override public Object getBean() { return Region.this; }
1228         @Override public String getName() { return "shape"; }
1229         @Override public CssMetaData<Region, Shape> getCssMetaData() {
1230             return StyleableProperties.SHAPE;
1231         }
1232         @Override protected void invalidated() {
1233             final Shape value = get();
1234             if (_shape != value) {
1235                 // The shape has changed. We need to add/remove listeners
1236                 if (_shape != null) _shape.impl_setShapeChangeListener(null);
1237                 if (value != null) value.impl_setShapeChangeListener(this);
1238                 // Invalidate the bounds and such
1239                 run();
1240                 if (_shape == null || value == null) {
1241                     // It either was null before, or is null now. In either case,
1242                     // the result of the insets computation will have changed, and
1243                     // we therefore need to fire that the insets value may have changed.
1244                     insets.fireValueChanged();
1245                 }
1246                 // Update our reference to the old shape
1247                 _shape = value;
1248                 scaledShape = null;
1249             }
1250         }
1251 
1252         @Override public void run() {
1253             impl_geomChanged();
1254             scaledShape = null;
1255             impl_markDirty(DirtyBits.REGION_SHAPE);
1256         }
1257     };
1258 
1259     /**
1260      * Specifies whether the shape, if defined, is scaled to match the size of the Region.
1261      * {@code true} means the shape is scaled to fit the size of the Region, {@code false}
1262      * means the shape is at its source size, its positioning depends on the value of
1263      * {@code centerShape}.
1264      *
1265      * @default true
1266      * @css shape-size      true | false
1267      * @since JavaFX 8.0
1268      */
1269     private BooleanProperty scaleShape = null;
1270     public final void setScaleShape(boolean value) { scaleShapeProperty().set(value); }
1271     public final boolean isScaleShape() { return scaleShape == null ? true : scaleShape.get(); }
1272     public final BooleanProperty scaleShapeProperty() {
1273         if (scaleShape == null) {
1274             scaleShape = new StyleableBooleanProperty(true) {
1275                 @Override public Object getBean() { return Region.this; }
1276                 @Override public String getName() { return "scaleShape"; }
1277                 @Override public CssMetaData<Region, Boolean> getCssMetaData() {
1278                     return StyleableProperties.SCALE_SHAPE;
1279                 }
1280                 @Override public void invalidated() {
1281                     impl_geomChanged();
1282                     scaledShape = null;
1283                     impl_markDirty(DirtyBits.REGION_SHAPE);
1284                 }
1285             };
1286         }
1287         return scaleShape;
1288     }
1289 
1290     /**
1291      * Defines whether the shape is centered within the Region's width or height.
1292      * {@code true} means the shape centered within the Region's width and height,
1293      * {@code false} means the shape is positioned at its source position.
1294      *
1295      * @default true
1296      * @css position-shape      true | false
1297      * @since JavaFX 8.0
1298      */
1299     private BooleanProperty centerShape = null;
1300     public final void setCenterShape(boolean value) { centerShapeProperty().set(value); }
1301     public final boolean isCenterShape() { return centerShape == null ? true : centerShape.get(); }
1302     public final BooleanProperty centerShapeProperty() {
1303         if (centerShape == null) {
1304             centerShape = new StyleableBooleanProperty(true) {
1305                 @Override public Object getBean() { return Region.this; }
1306                 @Override public String getName() { return "centerShape"; }
1307                 @Override public CssMetaData<Region, Boolean> getCssMetaData() {
1308                     return StyleableProperties.POSITION_SHAPE;
1309                 }
1310                 @Override public void invalidated() {
1311                     impl_geomChanged();
1312                     scaledShape = null;
1313                     impl_markDirty(DirtyBits.REGION_SHAPE);
1314                 }
1315             };
1316         }
1317         return centerShape;
1318     }
1319 
1320     /**
1321      * Defines a hint to the system indicating that the Shape used to define the region's
1322      * background is stable and would benefit from caching.
1323      *
1324      * @default true
1325      * @css -fx-cache-shape      true | false
1326      * @since JavaFX 8.0
1327      */
1328     private BooleanProperty cacheShape = null;
1329     public final void setCacheShape(boolean value) { cacheShapeProperty().set(value); }
1330     public final boolean isCacheShape() { return cacheShape == null ? true : cacheShape.get(); }
1331     public final BooleanProperty cacheShapeProperty() {
1332         if (cacheShape == null) {
1333             cacheShape = new StyleableBooleanProperty(true) {
1334                 @Override public Object getBean() { return Region.this; }
1335                 @Override public String getName() { return "cacheShape"; }
1336                 @Override public CssMetaData<Region, Boolean> getCssMetaData() {
1337                     return StyleableProperties.CACHE_SHAPE;
1338                 }
1339             };
1340         }
1341         return cacheShape;
1342     }
1343 
1344     /***************************************************************************
1345      *                                                                         *
1346      * Layout                                                                  *
1347      *                                                                         *
1348      **************************************************************************/
1349 
1350     /**
1351      * Returns <code>true</code> since all Regions are resizable.
1352      * @return whether this node can be resized by its parent during layout
1353      */
1354     @Override public boolean isResizable() {
1355         return true;
1356     }
1357 
1358     /**
1359      * Invoked by the region's parent during layout to set the region's
1360      * width and height.  <b>Applications should not invoke this method directly</b>.
1361      * If an application needs to directly set the size of the region, it should
1362      * override its size constraints by calling <code>setMinSize()</code>,
1363      *  <code>setPrefSize()</code>, or <code>setMaxSize()</code> and it's parent
1364      * will honor those overrides during layout.
1365      *
1366      * @param width the target layout bounds width
1367      * @param height the target layout bounds height
1368      */
1369     @Override public void resize(double width, double height) {
1370         setWidth(width);
1371         setHeight(height);
1372         PlatformLogger logger = Logging.getLayoutLogger();
1373         if (logger.isLoggable(Level.FINER)) {
1374             logger.finer(this.toString() + " resized to " + width + " x " + height);
1375         }
1376     }
1377 
1378     /**
1379      * Called during layout to determine the minimum width for this node.
1380      * Returns the value from <code>computeMinWidth(forHeight)</code> unless
1381      * the application overrode the minimum width by setting the minWidth property.
1382      *
1383      * @see #setMinWidth(double)
1384      * @return the minimum width that this node should be resized to during layout
1385      */
1386     @Override public final double minWidth(double height) {
1387         final double override = getMinWidth();
1388         if (override == USE_COMPUTED_SIZE) {
1389             return super.minWidth(height);
1390         } else if (override == USE_PREF_SIZE) {
1391             return prefWidth(height);
1392         }
1393         return Double.isNaN(override) || override < 0 ? 0 : override;
1394     }
1395 
1396     /**
1397      * Called during layout to determine the minimum height for this node.
1398      * Returns the value from <code>computeMinHeight(forWidth)</code> unless
1399      * the application overrode the minimum height by setting the minHeight property.
1400      *
1401      * @see #setMinHeight
1402      * @return the minimum height that this node should be resized to during layout
1403      */
1404     @Override public final double minHeight(double width) {
1405         final double override = getMinHeight();
1406         if (override == USE_COMPUTED_SIZE) {
1407             return super.minHeight(width);
1408         } else if (override == USE_PREF_SIZE) {
1409             return prefHeight(width);
1410         }
1411         return Double.isNaN(override) || override < 0 ? 0 : override;
1412     }
1413 
1414     /**
1415      * Called during layout to determine the preferred width for this node.
1416      * Returns the value from <code>computePrefWidth(forHeight)</code> unless
1417      * the application overrode the preferred width by setting the prefWidth property.
1418      *
1419      * @see #setPrefWidth
1420      * @return the preferred width that this node should be resized to during layout
1421      */
1422     @Override public final double prefWidth(double height) {
1423         final double override = getPrefWidth();
1424         if (override == USE_COMPUTED_SIZE) {
1425             return super.prefWidth(height);
1426         }
1427         return Double.isNaN(override) || override < 0 ? 0 : override;
1428     }
1429 
1430     /**
1431      * Called during layout to determine the preferred height for this node.
1432      * Returns the value from <code>computePrefHeight(forWidth)</code> unless
1433      * the application overrode the preferred height by setting the prefHeight property.
1434      *
1435      * @see #setPrefHeight
1436      * @return the preferred height that this node should be resized to during layout
1437      */
1438     @Override public final double prefHeight(double width) {
1439         final double override = getPrefHeight();
1440         if (override == USE_COMPUTED_SIZE) {
1441             return super.prefHeight(width);
1442         }
1443         return Double.isNaN(override) || override < 0 ? 0 : override;
1444     }
1445 
1446     /**
1447      * Called during layout to determine the maximum width for this node.
1448      * Returns the value from <code>computeMaxWidth(forHeight)</code> unless
1449      * the application overrode the maximum width by setting the maxWidth property.
1450      *
1451      * @see #setMaxWidth
1452      * @return the maximum width that this node should be resized to during layout
1453      */
1454     @Override public final double maxWidth(double height) {
1455         final double override = getMaxWidth();
1456         if (override == USE_COMPUTED_SIZE) {
1457             return computeMaxWidth(height);
1458         } else if (override == USE_PREF_SIZE) {
1459             return prefWidth(height);
1460         }
1461         return Double.isNaN(override) || override < 0 ? 0 : override;
1462     }
1463 
1464     /**
1465      * Called during layout to determine the maximum height for this node.
1466      * Returns the value from <code>computeMaxHeight(forWidth)</code> unless
1467      * the application overrode the maximum height by setting the maxHeight property.
1468      *
1469      * @see #setMaxHeight
1470      * @return the maximum height that this node should be resized to during layout
1471      */
1472     @Override public final double maxHeight(double width) {
1473         final double override = getMaxHeight();
1474         if (override == USE_COMPUTED_SIZE) {
1475             return computeMaxHeight(width);
1476         } else if (override == USE_PREF_SIZE) {
1477             return prefHeight(width);
1478         }
1479         return Double.isNaN(override) || override < 0 ? 0 : override;
1480     }
1481 
1482     /**
1483      * Computes the minimum width of this region.
1484      * Returns the sum of the left and right insets by default.
1485      * region subclasses should override this method to return an appropriate
1486      * value based on their content and layout strategy.  If the subclass
1487      * doesn't have a VERTICAL content bias, then the height parameter can be
1488      * ignored.
1489      *
1490      * @return the computed minimum width of this region
1491      */
1492     @Override protected double computeMinWidth(double height) {
1493         return getInsets().getLeft() + getInsets().getRight();
1494     }
1495 
1496     /**
1497      * Computes the minimum height of this region.
1498      * Returns the sum of the top and bottom insets by default.
1499      * Region subclasses should override this method to return an appropriate
1500      * value based on their content and layout strategy.  If the subclass
1501      * doesn't have a HORIZONTAL content bias, then the width parameter can be
1502      * ignored.
1503      *
1504      * @return the computed minimum height for this region
1505      */
1506     @Override protected double computeMinHeight(double width) {
1507         return getInsets().getTop() + getInsets().getBottom();
1508     }
1509 
1510     /**
1511      * Computes the preferred width of this region for the given height.
1512      * Region subclasses should override this method to return an appropriate
1513      * value based on their content and layout strategy.  If the subclass
1514      * doesn't have a VERTICAL content bias, then the height parameter can be
1515      * ignored.
1516      *
1517      * @return the computed preferred width for this region
1518      */
1519     @Override protected double computePrefWidth(double height) {
1520         final double w = super.computePrefWidth(height);
1521         return getInsets().getLeft() + w + getInsets().getRight();
1522     }
1523 
1524     /**
1525      * Computes the preferred height of this region for the given width;
1526      * Region subclasses should override this method to return an appropriate
1527      * value based on their content and layout strategy.  If the subclass
1528      * doesn't have a HORIZONTAL content bias, then the width parameter can be
1529      * ignored.
1530      *
1531      * @return the computed preferred height for this region
1532      */
1533     @Override protected double computePrefHeight(double width) {
1534         final double h = super.computePrefHeight(width);
1535         return getInsets().getTop() + h + getInsets().getBottom();
1536     }
1537 
1538     /**
1539      * Computes the maximum width for this region.
1540      * Returns Double.MAX_VALUE by default.
1541      * Region subclasses may override this method to return an different
1542      * value based on their content and layout strategy.  If the subclass
1543      * doesn't have a VERTICAL content bias, then the height parameter can be
1544      * ignored.
1545      *
1546      * @return the computed maximum width for this region
1547      */
1548     protected double computeMaxWidth(double height) {
1549         return Double.MAX_VALUE;
1550     }
1551 
1552     /**
1553      * Computes the maximum height of this region.
1554      * Returns Double.MAX_VALUE by default.
1555      * Region subclasses may override this method to return a different
1556      * value based on their content and layout strategy.  If the subclass
1557      * doesn't have a HORIZONTAL content bias, then the width parameter can be
1558      * ignored.
1559      *
1560      * @return the computed maximum height for this region
1561      */
1562     protected double computeMaxHeight(double width) {
1563         return Double.MAX_VALUE;
1564     }
1565 
1566     /**
1567      * If this region's snapToPixel property is true, returns a value rounded
1568      * to the nearest pixel, else returns the same value.
1569      * @param value the space value to be snapped
1570      * @return value rounded to nearest pixel
1571      */
1572     protected double snapSpace(double value) {
1573         return snapSpace(value, isSnapToPixel());
1574     }
1575 
1576     /**
1577      * If this region's snapToPixel property is true, returns a value ceiled
1578      * to the nearest pixel, else returns the same value.
1579      * @param value the size value to be snapped
1580      * @return value ceiled to nearest pixel
1581      */
1582     protected double snapSize(double value) {
1583         return snapSize(value, isSnapToPixel());
1584     }
1585 
1586     /**
1587      * If this region's snapToPixel property is true, returns a value rounded
1588      * to the nearest pixel, else returns the same value.
1589      * @param value the position value to be snapped
1590      * @return value rounded to nearest pixel
1591      */
1592     protected double snapPosition(double value) {
1593         return snapPosition(value, isSnapToPixel());
1594     }
1595 
1596     double snapPortion(double value) {
1597         return snapPortion(value, isSnapToPixel());
1598     }
1599 
1600 
1601     /**
1602      * Utility method to get the top inset which includes padding and border
1603      * inset. Then snapped to whole pixels if isSnapToPixel() is true.
1604      *
1605      * @since JavaFX 8.0
1606      * @return Rounded up insets top
1607      */
1608     public final double snappedTopInset() {
1609         return snappedTopInset;
1610     }
1611 
1612     /**
1613      * Utility method to get the bottom inset which includes padding and border
1614      * inset. Then snapped to whole pixels if isSnapToPixel() is true.
1615      *
1616      * @since JavaFX 8.0
1617      * @return Rounded up insets bottom
1618      */
1619     public final double snappedBottomInset() {
1620         return snappedBottomInset;
1621     }
1622 
1623     /**
1624      * Utility method to get the left inset which includes padding and border
1625      * inset. Then snapped to whole pixels if isSnapToPixel() is true.
1626      *
1627      * @since JavaFX 8.0
1628      * @return Rounded up insets left
1629      */
1630     public final double snappedLeftInset() {
1631         return snappedLeftInset;
1632     }
1633 
1634     /**
1635      * Utility method to get the right inset which includes padding and border
1636      * inset. Then snapped to whole pixels if isSnapToPixel() is true.
1637      *
1638      * @since JavaFX 8.0
1639      * @return Rounded up insets right
1640      */
1641     public final double snappedRightInset() {
1642         return snappedRightInset;
1643     }
1644 
1645 
1646     double computeChildMinAreaWidth(Node child, Insets margin) {
1647         return computeChildMinAreaWidth(child, -1, margin, -1, false);
1648     }
1649 
1650     double computeChildMinAreaWidth(Node child, double baselineComplement, Insets margin, double height, boolean fillHeight) {
1651         final boolean snap = isSnapToPixel();
1652         double left = margin != null? snapSpace(margin.getLeft(), snap) : 0;
1653         double right = margin != null? snapSpace(margin.getRight(), snap) : 0;
1654         double alt = -1;
1655         if (height != -1 && child.isResizable() && child.getContentBias() == Orientation.VERTICAL) { // width depends on height
1656             double top = margin != null? snapSpace(margin.getTop(), snap) : 0;
1657             double bottom = (margin != null? snapSpace(margin.getBottom(), snap) : 0);
1658             double bo = child.getBaselineOffset();
1659             final double contentHeight = bo == BASELINE_OFFSET_SAME_AS_HEIGHT && baselineComplement != -1 ?
1660                     height - top - bottom - baselineComplement :
1661                      height - top - bottom;
1662             if (fillHeight) {
1663                 alt = snapSize(boundedSize(
1664                         child.minHeight(-1), contentHeight,
1665                         child.maxHeight(-1)));
1666             } else {
1667                 alt = snapSize(boundedSize(
1668                         child.minHeight(-1),
1669                         child.prefHeight(-1),
1670                         Math.min(child.maxHeight(-1), contentHeight)));
1671             }
1672         }
1673         return left + snapSize(child.minWidth(alt)) + right;
1674     }
1675 
1676     double computeChildMinAreaHeight(Node child, Insets margin) {
1677         return computeChildMinAreaHeight(child, -1, margin, -1);
1678     }
1679 
1680     double computeChildMinAreaHeight(Node child, double minBaselineComplement, Insets margin, double width) {
1681         final boolean snap = isSnapToPixel();
1682         double top =margin != null? snapSpace(margin.getTop(), snap) : 0;
1683         double bottom = margin != null? snapSpace(margin.getBottom(), snap) : 0;
1684 
1685         double alt = -1;
1686         if (child.isResizable() && child.getContentBias() == Orientation.HORIZONTAL) { // height depends on width
1687             double left = margin != null? snapSpace(margin.getLeft(), snap) : 0;
1688             double right = margin != null? snapSpace(margin.getRight(), snap) : 0;
1689             alt = snapSize(width != -1? boundedSize(child.minWidth(-1), width - left - right, child.maxWidth(-1)) :
1690                     child.maxWidth(-1));
1691         }
1692 
1693         // For explanation, see computeChildPrefAreaHeight
1694         if (minBaselineComplement != -1) {
1695             double baseline = child.getBaselineOffset();
1696             if (child.isResizable() && baseline == BASELINE_OFFSET_SAME_AS_HEIGHT) {
1697                 return top + snapSize(child.minHeight(alt)) + bottom
1698                         + minBaselineComplement;
1699             } else {
1700                 return baseline + minBaselineComplement;
1701             }
1702         } else {
1703             return top + snapSize(child.minHeight(alt)) + bottom;
1704         }
1705     }
1706 
1707     double computeChildPrefAreaWidth(Node child, Insets margin) {
1708         return computeChildPrefAreaWidth(child, -1, margin, -1, false);
1709     }
1710 
1711     double computeChildPrefAreaWidth(Node child, double baselineComplement, Insets margin, double height, boolean fillHeight) {
1712         final boolean snap = isSnapToPixel();
1713         double left = margin != null? snapSpace(margin.getLeft(), snap) : 0;
1714         double right = margin != null? snapSpace(margin.getRight(), snap) : 0;
1715         double alt = -1;
1716         if (height != -1 && child.isResizable() && child.getContentBias() == Orientation.VERTICAL) { // width depends on height
1717             double top = margin != null? snapSpace(margin.getTop(), snap) : 0;
1718             double bottom = margin != null? snapSpace(margin.getBottom(), snap) : 0;
1719             double bo = child.getBaselineOffset();
1720             final double contentHeight = bo == BASELINE_OFFSET_SAME_AS_HEIGHT && baselineComplement != -1 ?
1721                     height - top - bottom - baselineComplement :
1722                      height - top - bottom;
1723             if (fillHeight) {
1724                 alt = snapSize(boundedSize(
1725                         child.minHeight(-1), contentHeight,
1726                         child.maxHeight(-1)));
1727             } else {
1728                 alt = snapSize(boundedSize(
1729                         child.minHeight(-1),
1730                         child.prefHeight(-1),
1731                         Math.min(child.maxHeight(-1), contentHeight)));
1732             }
1733         }
1734         return left + snapSize(boundedSize(child.minWidth(alt), child.prefWidth(alt), child.maxWidth(alt))) + right;
1735     }
1736 
1737     double computeChildPrefAreaHeight(Node child, Insets margin) {
1738         return computeChildPrefAreaHeight(child, -1, margin, -1);
1739     }
1740 
1741     double computeChildPrefAreaHeight(Node child, double prefBaselineComplement, Insets margin, double width) {
1742         final boolean snap = isSnapToPixel();
1743         double top = margin != null? snapSpace(margin.getTop(), snap) : 0;
1744         double bottom = margin != null? snapSpace(margin.getBottom(), snap) : 0;
1745 
1746         double alt = -1;
1747         if (child.isResizable() && child.getContentBias() == Orientation.HORIZONTAL) { // height depends on width
1748             double left = margin != null ? snapSpace(margin.getLeft(), snap) : 0;
1749             double right = margin != null ? snapSpace(margin.getRight(), snap) : 0;
1750             alt = snapSize(boundedSize(
1751                     child.minWidth(-1), width != -1 ? width - left - right
1752                     : child.prefWidth(-1), child.maxWidth(-1)));
1753         }
1754 
1755         if (prefBaselineComplement != -1) {
1756             double baseline = child.getBaselineOffset();
1757             if (child.isResizable() && baseline == BASELINE_OFFSET_SAME_AS_HEIGHT) {
1758                 // When baseline is same as height, the preferred height of the node will be above the baseline, so we need to add
1759                 // the preferred complement to it
1760                 return top + snapSize(boundedSize(child.minHeight(alt), child.prefHeight(alt), child.maxHeight(alt))) + bottom
1761                         + prefBaselineComplement;
1762             } else {
1763                 // For all other Nodes, it's just their baseline and the complement.
1764                 // Note that the complement already contain the Node's preferred (or fixed) height
1765                 return top + baseline + prefBaselineComplement + bottom;
1766             }
1767         } else {
1768             return top + snapSize(boundedSize(child.minHeight(alt), child.prefHeight(alt), child.maxHeight(alt))) + bottom;
1769         }
1770     }
1771 
1772     double computeChildMaxAreaWidth(Node child, double baselineComplement, Insets margin, double height, boolean fillHeight) {
1773         double max = child.maxWidth(-1);
1774         if (max == Double.MAX_VALUE) {
1775             return max;
1776         }
1777         final boolean snap = isSnapToPixel();
1778         double left = margin != null? snapSpace(margin.getLeft(), snap) : 0;
1779         double right = margin != null? snapSpace(margin.getRight(), snap) : 0;
1780         double alt = -1;
1781         if (height != -1 && child.isResizable() && child.getContentBias() == Orientation.VERTICAL) { // width depends on height
1782             double top = margin != null? snapSpace(margin.getTop(), snap) : 0;
1783             double bottom = (margin != null? snapSpace(margin.getBottom(), snap) : 0);
1784             double bo = child.getBaselineOffset();
1785             final double contentHeight = bo == BASELINE_OFFSET_SAME_AS_HEIGHT && baselineComplement != -1 ?
1786                     height - top - bottom - baselineComplement :
1787                      height - top - bottom;
1788             if (fillHeight) {
1789                 alt = snapSize(boundedSize(
1790                         child.minHeight(-1), contentHeight,
1791                         child.maxHeight(-1)));
1792             } else {
1793                 alt = snapSize(boundedSize(
1794                         child.minHeight(-1),
1795                         child.prefHeight(-1),
1796                         Math.min(child.maxHeight(-1), contentHeight)));
1797             }
1798             max = child.maxWidth(alt);
1799         }
1800         // if min > max, min wins, so still need to call boundedSize()
1801         return left + snapSize(boundedSize(child.minWidth(alt), max, Double.MAX_VALUE)) + right;
1802     }
1803 
1804     double computeChildMaxAreaHeight(Node child, double maxBaselineComplement, Insets margin, double width) {
1805         double max = child.maxHeight(-1);
1806         if (max == Double.MAX_VALUE) {
1807             return max;
1808         }
1809 
1810         final boolean snap = isSnapToPixel();
1811         double top = margin != null? snapSpace(margin.getTop(), snap) : 0;
1812         double bottom = margin != null? snapSpace(margin.getBottom(), snap) : 0;
1813         double alt = -1;
1814         if (child.isResizable() && child.getContentBias() == Orientation.HORIZONTAL) { // height depends on width
1815             double left = margin != null? snapSpace(margin.getLeft(), snap) : 0;
1816             double right = margin != null? snapSpace(margin.getRight(), snap) : 0;
1817             alt = snapSize(width != -1? boundedSize(child.minWidth(-1), width - left - right, child.maxWidth(-1)) :
1818                 child.minWidth(-1));
1819             max = child.maxHeight(alt);
1820         }
1821         // For explanation, see computeChildPrefAreaHeight
1822         if (maxBaselineComplement != -1) {
1823             double baseline = child.getBaselineOffset();
1824             if (child.isResizable() && baseline == BASELINE_OFFSET_SAME_AS_HEIGHT) {
1825                 return top + snapSize(boundedSize(child.minHeight(alt), child.maxHeight(alt), Double.MAX_VALUE)) + bottom
1826                         + maxBaselineComplement;
1827             } else {
1828                 return top + baseline + maxBaselineComplement + bottom;
1829             }
1830         } else {
1831             // if min > max, min wins, so still need to call boundedSize()
1832             return top + snapSize(boundedSize(child.minHeight(alt), max, Double.MAX_VALUE)) + bottom;
1833         }
1834     }
1835 
1836     /* Max of children's minimum area widths */
1837 
1838     double computeMaxMinAreaWidth(List<Node> children, Callback<Node, Insets> margins) {
1839         return getMaxAreaWidth(children, margins, new double[] { -1 }, false, true);
1840     }
1841 
1842     double computeMaxMinAreaWidth(List<Node> children, Callback<Node, Insets> margins, double height, boolean fillHeight) {
1843         return getMaxAreaWidth(children, margins, new double[] { height }, fillHeight, true);
1844     }
1845 
1846     double computeMaxMinAreaWidth(List<Node> children, Callback<Node, Insets> childMargins, double childHeights[], boolean fillHeight) {
1847         return getMaxAreaWidth(children, childMargins, childHeights, fillHeight, true);
1848     }
1849 
1850     /* Max of children's minimum area heights */
1851 
1852     double computeMaxMinAreaHeight(List<Node>children, Callback<Node, Insets> margins, VPos valignment) {
1853         return getMaxAreaHeight(children, margins, null, valignment, true);
1854     }
1855 
1856     double computeMaxMinAreaHeight(List<Node>children, Callback<Node, Insets> margins, VPos valignment, double width) {
1857         return getMaxAreaHeight(children, margins, new double[] { width }, valignment, true);
1858     }
1859 
1860     double computeMaxMinAreaHeight(List<Node>children, Callback<Node, Insets> childMargins, double childWidths[], VPos valignment) {
1861         return getMaxAreaHeight(children, childMargins, childWidths, valignment, true);
1862     }
1863 
1864     /* Max of children's pref area widths */
1865 
1866     double computeMaxPrefAreaWidth(List<Node>children, Callback<Node, Insets> margins) {
1867         return getMaxAreaWidth(children, margins, new double[] { -1 }, false, false);
1868     }
1869 
1870     double computeMaxPrefAreaWidth(List<Node>children, Callback<Node, Insets> margins, double height,
1871             boolean fillHeight) {
1872         return getMaxAreaWidth(children, margins, new double[] { height }, fillHeight, false);
1873     }
1874 
1875     double computeMaxPrefAreaWidth(List<Node>children, Callback<Node, Insets> childMargins,
1876             double childHeights[], boolean fillHeight) {
1877         return getMaxAreaWidth(children, childMargins, childHeights, fillHeight, false);
1878     }
1879 
1880     /* Max of children's pref area heights */
1881 
1882     double computeMaxPrefAreaHeight(List<Node>children, Callback<Node, Insets> margins, VPos valignment) {
1883         return getMaxAreaHeight(children, margins, null, valignment, false);
1884     }
1885 
1886     double computeMaxPrefAreaHeight(List<Node>children, Callback<Node, Insets> margins, double width, VPos valignment) {
1887         return getMaxAreaHeight(children, margins, new double[] { width }, valignment, false);
1888     }
1889 
1890     double computeMaxPrefAreaHeight(List<Node>children, Callback<Node, Insets> childMargins, double childWidths[], VPos valignment) {
1891         return getMaxAreaHeight(children, childMargins, childWidths, valignment, false);
1892     }
1893 
1894     /**
1895      * Returns the size of a Node that should be placed in an area of the specified size,
1896      * bounded in it's min/max size, respecting bias.
1897      *
1898      * @param node the node
1899      * @param areaWidth the width of the bounding area where the node is going to be placed
1900      * @param areaHeight the height of the bounding area where the node is going to be placed
1901      * @param fillWidth if Node should try to fill the area width
1902      * @param fillHeight if Node should try to fill the area height
1903      * @param result Vec2d object for the result or null if new one should be created
1904      * @return Vec2d object with width(x parameter) and height (y parameter)
1905      */
1906     static Vec2d boundedNodeSizeWithBias(Node node, double areaWidth, double areaHeight,
1907             boolean fillWidth, boolean fillHeight, Vec2d result) {
1908         if (result == null) {
1909             result = new Vec2d();
1910         }
1911 
1912         Orientation bias = node.getContentBias();
1913 
1914         double childWidth = 0;
1915         double childHeight = 0;
1916 
1917         if (bias == null) {
1918             childWidth = boundedSize(
1919                     node.minWidth(-1), fillWidth ? areaWidth
1920                     : Math.min(areaWidth, node.prefWidth(-1)),
1921                     node.maxWidth(-1));
1922             childHeight = boundedSize(
1923                     node.minHeight(-1), fillHeight ? areaHeight
1924                     : Math.min(areaHeight, node.prefHeight(-1)),
1925                     node.maxHeight(-1));
1926 
1927         } else if (bias == Orientation.HORIZONTAL) {
1928             childWidth = boundedSize(
1929                     node.minWidth(-1), fillWidth ? areaWidth
1930                     : Math.min(areaWidth, node.prefWidth(-1)),
1931                     node.maxWidth(-1));
1932             childHeight = boundedSize(
1933                     node.minHeight(childWidth), fillHeight ? areaHeight
1934                     : Math.min(areaHeight, node.prefHeight(childWidth)),
1935                     node.maxHeight(childWidth));
1936 
1937         } else { // bias == VERTICAL
1938             childHeight = boundedSize(
1939                     node.minHeight(-1), fillHeight ? areaHeight
1940                     : Math.min(areaHeight, node.prefHeight(-1)),
1941                     node.maxHeight(-1));
1942             childWidth = boundedSize(
1943                     node.minWidth(childHeight), fillWidth ? areaWidth
1944                     : Math.min(areaWidth, node.prefWidth(childHeight)),
1945                     node.maxWidth(childHeight));
1946         }
1947 
1948         result.set(childWidth, childHeight);
1949         return result;
1950     }
1951 
1952     /* utility method for computing the max of children's min or pref heights, taking into account baseline alignment */
1953     private double getMaxAreaHeight(List<Node> children, Callback<Node,Insets> childMargins,  double childWidths[], VPos valignment, boolean minimum) {
1954         final double singleChildWidth = childWidths == null ? -1 : childWidths.length == 1 ? childWidths[0] : Double.NaN;
1955         if (valignment == VPos.BASELINE) {
1956             double maxAbove = 0;
1957             double maxBelow = 0;
1958             for (int i = 0, maxPos = children.size(); i < maxPos; i++) {
1959                 final Node child = children.get(i);
1960                 final double childWidth = Double.isNaN(singleChildWidth) ? childWidths[i] : singleChildWidth;
1961                 Insets margin = childMargins.call(child);
1962                 final double top = margin != null? snapSpace(margin.getTop()) : 0;
1963                 final double bottom = margin != null? snapSpace(margin.getBottom()) : 0;
1964                 final double baseline = child.getBaselineOffset();
1965 
1966                 final double childHeight = minimum? snapSize(child.minHeight(childWidth)) : snapSize(child.prefHeight(childWidth));
1967                 if (baseline == BASELINE_OFFSET_SAME_AS_HEIGHT) {
1968                     maxAbove = Math.max(maxAbove, childHeight + top);
1969                 } else {
1970                     maxAbove = Math.max(maxAbove, baseline + top);
1971                     maxBelow = Math.max(maxBelow,
1972                             snapSpace(minimum?snapSize(child.minHeight(childWidth)) : snapSize(child.prefHeight(childWidth))) -
1973                             baseline + bottom);
1974                 }
1975             }
1976             return maxAbove + maxBelow; //remind(aim): ceil this value?
1977         } else {
1978             double max = 0;
1979             for (int i = 0, maxPos = children.size(); i < maxPos; i++) {
1980                 final Node child = children.get(i);
1981                 Insets margin = childMargins.call(child);
1982                 final double childWidth = Double.isNaN(singleChildWidth) ? childWidths[i] : singleChildWidth;
1983                 max = Math.max(max, minimum?
1984                     computeChildMinAreaHeight(child, -1, margin, childWidth) :
1985                         computeChildPrefAreaHeight(child, -1, margin, childWidth));
1986             }
1987             return max;
1988         }
1989     }
1990 
1991     /* utility method for computing the max of children's min or pref width, horizontal alignment is ignored for now */
1992     private double getMaxAreaWidth(List<javafx.scene.Node> children,
1993             Callback<Node, Insets> childMargins, double childHeights[], boolean fillHeight, boolean minimum) {
1994         final double singleChildHeight = childHeights == null ? -1 : childHeights.length == 1 ? childHeights[0] : Double.NaN;
1995 
1996         double max = 0;
1997         for (int i = 0, maxPos = children.size(); i < maxPos; i++) {
1998             final Node child = children.get(i);
1999             final Insets margin = childMargins.call(child);
2000             final double childHeight = Double.isNaN(singleChildHeight) ? childHeights[i] : singleChildHeight;
2001             max = Math.max(max, minimum?
2002                 computeChildMinAreaWidth(children.get(i), -1, margin, childHeight, fillHeight) :
2003                     computeChildPrefAreaWidth(child, -1, margin, childHeight, fillHeight));
2004         }
2005         return max;
2006     }
2007 
2008     /**
2009      * Utility method which positions the child within an area of this
2010      * region defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
2011      * with a baseline offset relative to that area.
2012      * <p>
2013      * This function does <i>not</i> resize the node and uses the node's layout bounds
2014      * width and height to determine how it should be positioned within the area.
2015      * <p>
2016      * If the vertical alignment is {@code VPos.BASELINE} then it
2017      * will position the node so that its own baseline aligns with the passed in
2018      * {@code baselineOffset},  otherwise the baseline parameter is ignored.
2019      * <p>
2020      * If {@code snapToPixel} is {@code true} for this region, then the x/y position
2021      * values will be rounded to their nearest pixel boundaries.
2022      *
2023      * @param child the child being positioned within this region
2024      * @param areaX the horizontal offset of the layout area relative to this region
2025      * @param areaY the vertical offset of the layout area relative to this region
2026      * @param areaWidth  the width of the layout area
2027      * @param areaHeight the height of the layout area
2028      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
2029      * @param halignment the horizontal alignment for the child within the area
2030      * @param valignment the vertical alignment for the child within the area
2031      *
2032      */
2033     protected void positionInArea(Node child, double areaX, double areaY, double areaWidth, double areaHeight,
2034                                double areaBaselineOffset, HPos halignment, VPos valignment) {
2035         positionInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
2036                 Insets.EMPTY, halignment, valignment, isSnapToPixel());
2037     }
2038 
2039     /**
2040      * Utility method which positions the child within an area of this
2041      * region defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
2042      * with a baseline offset relative to that area.
2043      * <p>
2044      * This function does <i>not</i> resize the node and uses the node's layout bounds
2045      * width and height to determine how it should be positioned within the area.
2046      * <p>
2047      * If the vertical alignment is {@code VPos.BASELINE} then it
2048      * will position the node so that its own baseline aligns with the passed in
2049      * {@code baselineOffset},  otherwise the baseline parameter is ignored.
2050      * <p>
2051      * If {@code snapToPixel} is {@code true} for this region, then the x/y position
2052      * values will be rounded to their nearest pixel boundaries.
2053      * <p>
2054      * If {@code margin} is non-null, then that space will be allocated around the
2055      * child within the layout area.  margin may be null.
2056      *
2057      * @param child the child being positioned within this region
2058      * @param areaX the horizontal offset of the layout area relative to this region
2059      * @param areaY the vertical offset of the layout area relative to this region
2060      * @param areaWidth  the width of the layout area
2061      * @param areaHeight the height of the layout area
2062      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
2063      * @param margin the margin of space to be allocated around the child
2064      * @param halignment the horizontal alignment for the child within the area
2065      * @param valignment the vertical alignment for the child within the area
2066      *
2067      * @since JavaFX 8.0
2068      */
2069     public static void positionInArea(Node child, double areaX, double areaY, double areaWidth, double areaHeight,
2070                                double areaBaselineOffset, Insets margin, HPos halignment, VPos valignment, boolean isSnapToPixel) {
2071         Insets childMargin = margin != null? margin : Insets.EMPTY;
2072 
2073         position(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
2074                 snapSpace(childMargin.getTop(), isSnapToPixel),
2075                 snapSpace(childMargin.getRight(), isSnapToPixel),
2076                 snapSpace(childMargin.getBottom(), isSnapToPixel),
2077                 snapSpace(childMargin.getLeft(), isSnapToPixel),
2078                 halignment, valignment, isSnapToPixel);
2079     }
2080 
2081     /**
2082      * Utility method which lays out the child within an area of this
2083      * region defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
2084      * with a baseline offset relative to that area.
2085      * <p>
2086      * If the child is resizable, this method will resize it to fill the specified
2087      * area unless the node's maximum size prevents it.  If the node's maximum
2088      * size preference is less than the area size, the maximum size will be used.
2089      * If node's maximum is greater than the area size, then the node will be
2090      * resized to fit within the area, unless its minimum size prevents it.
2091      * <p>
2092      * If the child has a non-null contentBias, then this method will use it when
2093      * resizing the child.  If the contentBias is horizontal, it will set its width
2094      * first to the area's width (up to the child's max width limit) and then pass
2095      * that value to compute the child's height.  If child's contentBias is vertical,
2096      * then it will set its height to the area height (up to child's max height limit)
2097      * and pass that height to compute the child's width.  If the child's contentBias
2098      * is null, then it's width and height have no dependencies on each other.
2099      * <p>
2100      * If the child is not resizable (Shape, Group, etc) then it will only be
2101      * positioned and not resized.
2102      * <p>
2103      * If the child's resulting size differs from the area's size (either
2104      * because it was not resizable or it's sizing preferences prevented it), then
2105      * this function will align the node relative to the area using horizontal and
2106      * vertical alignment values.
2107      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
2108      * with the area baseline offset parameter, otherwise the baseline parameter
2109      * is ignored.
2110      * <p>
2111      * If {@code snapToPixel} is {@code true} for this region, then the resulting x,y
2112      * values will be rounded to their nearest pixel boundaries and the
2113      * width/height values will be ceiled to the next pixel boundary.
2114      *
2115      * @param child the child being positioned within this region
2116      * @param areaX the horizontal offset of the layout area relative to this region
2117      * @param areaY the vertical offset of the layout area relative to this region
2118      * @param areaWidth  the width of the layout area
2119      * @param areaHeight the height of the layout area
2120      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
2121      * @param halignment the horizontal alignment for the child within the area
2122      * @param valignment the vertical alignment for the child within the area
2123      *
2124      */
2125     protected void layoutInArea(Node child, double areaX, double areaY,
2126                                double areaWidth, double areaHeight,
2127                                double areaBaselineOffset,
2128                                HPos halignment, VPos valignment) {
2129         layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
2130                 Insets.EMPTY, halignment, valignment);
2131     }
2132 
2133     /**
2134      * Utility method which lays out the child within an area of this
2135      * region defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
2136      * with a baseline offset relative to that area.
2137      * <p>
2138      * If the child is resizable, this method will resize it to fill the specified
2139      * area unless the node's maximum size prevents it.  If the node's maximum
2140      * size preference is less than the area size, the maximum size will be used.
2141      * If node's maximum is greater than the area size, then the node will be
2142      * resized to fit within the area, unless its minimum size prevents it.
2143      * <p>
2144      * If the child has a non-null contentBias, then this method will use it when
2145      * resizing the child.  If the contentBias is horizontal, it will set its width
2146      * first to the area's width (up to the child's max width limit) and then pass
2147      * that value to compute the child's height.  If child's contentBias is vertical,
2148      * then it will set its height to the area height (up to child's max height limit)
2149      * and pass that height to compute the child's width.  If the child's contentBias
2150      * is null, then it's width and height have no dependencies on each other.
2151      * <p>
2152      * If the child is not resizable (Shape, Group, etc) then it will only be
2153      * positioned and not resized.
2154      * <p>
2155      * If the child's resulting size differs from the area's size (either
2156      * because it was not resizable or it's sizing preferences prevented it), then
2157      * this function will align the node relative to the area using horizontal and
2158      * vertical alignment values.
2159      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
2160      * with the area baseline offset parameter, otherwise the baseline parameter
2161      * is ignored.
2162      * <p>
2163      * If {@code margin} is non-null, then that space will be allocated around the
2164      * child within the layout area.  margin may be null.
2165      * <p>
2166      * If {@code snapToPixel} is {@code true} for this region, then the resulting x,y
2167      * values will be rounded to their nearest pixel boundaries and the
2168      * width/height values will be ceiled to the next pixel boundary.
2169      *
2170      * @param child the child being positioned within this region
2171      * @param areaX the horizontal offset of the layout area relative to this region
2172      * @param areaY the vertical offset of the layout area relative to this region
2173      * @param areaWidth  the width of the layout area
2174      * @param areaHeight the height of the layout area
2175      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
2176      * @param margin the margin of space to be allocated around the child
2177      * @param halignment the horizontal alignment for the child within the area
2178      * @param valignment the vertical alignment for the child within the area
2179      */
2180     protected void layoutInArea(Node child, double areaX, double areaY,
2181                                double areaWidth, double areaHeight,
2182                                double areaBaselineOffset,
2183                                Insets margin,
2184                                HPos halignment, VPos valignment) {
2185         layoutInArea(child, areaX, areaY, areaWidth, areaHeight,
2186                 areaBaselineOffset, margin, true, true, halignment, valignment);
2187     }
2188 
2189     /**
2190      * Utility method which lays out the child within an area of this
2191      * region defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
2192      * with a baseline offset relative to that area.
2193      * <p>
2194      * If the child is resizable, this method will use {@code fillWidth} and {@code fillHeight}
2195      * to determine whether to resize it to fill the area or keep the child at its
2196      * preferred dimension.  If fillWidth/fillHeight are true, then this method
2197      * will only resize the child up to its max size limits.  If the node's maximum
2198      * size preference is less than the area size, the maximum size will be used.
2199      * If node's maximum is greater than the area size, then the node will be
2200      * resized to fit within the area, unless its minimum size prevents it.
2201      * <p>
2202      * If the child has a non-null contentBias, then this method will use it when
2203      * resizing the child.  If the contentBias is horizontal, it will set its width
2204      * first and then pass that value to compute the child's height.  If child's
2205      * contentBias is vertical, then it will set its height first
2206      * and pass that value to compute the child's width.  If the child's contentBias
2207      * is null, then it's width and height have no dependencies on each other.
2208      * <p>
2209      * If the child is not resizable (Shape, Group, etc) then it will only be
2210      * positioned and not resized.
2211      * <p>
2212      * If the child's resulting size differs from the area's size (either
2213      * because it was not resizable or it's sizing preferences prevented it), then
2214      * this function will align the node relative to the area using horizontal and
2215      * vertical alignment values.
2216      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
2217      * with the area baseline offset parameter, otherwise the baseline parameter
2218      * is ignored.
2219      * <p>
2220      * If {@code margin} is non-null, then that space will be allocated around the
2221      * child within the layout area.  margin may be null.
2222      * <p>
2223      * If {@code snapToPixel} is {@code true} for this region, then the resulting x,y
2224      * values will be rounded to their nearest pixel boundaries and the
2225      * width/height values will be ceiled to the next pixel boundary.
2226      *
2227      * @param child the child being positioned within this region
2228      * @param areaX the horizontal offset of the layout area relative to this region
2229      * @param areaY the vertical offset of the layout area relative to this region
2230      * @param areaWidth  the width of the layout area
2231      * @param areaHeight the height of the layout area
2232      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
2233      * @param margin the margin of space to be allocated around the child
2234      * @param fillWidth whether or not the child should be resized to fill the area width or kept to its preferred width
2235      * @param fillHeight whether or not the child should e resized to fill the area height or kept to its preferred height
2236      * @param halignment the horizontal alignment for the child within the area
2237      * @param valignment the vertical alignment for the child within the area
2238      */
2239     protected void layoutInArea(Node child, double areaX, double areaY,
2240                                double areaWidth, double areaHeight,
2241                                double areaBaselineOffset,
2242                                Insets margin, boolean fillWidth, boolean fillHeight,
2243                                HPos halignment, VPos valignment) {
2244         layoutInArea(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset, margin, fillWidth, fillHeight, halignment, valignment, isSnapToPixel());
2245     }
2246 
2247     /**
2248      * Utility method which lays out the child within an area of it's
2249      * parent defined by {@code areaX}, {@code areaY}, {@code areaWidth} x {@code areaHeight},
2250      * with a baseline offset relative to that area.
2251      * <p>
2252      * If the child is resizable, this method will use {@code fillWidth} and {@code fillHeight}
2253      * to determine whether to resize it to fill the area or keep the child at its
2254      * preferred dimension.  If fillWidth/fillHeight are true, then this method
2255      * will only resize the child up to its max size limits.  If the node's maximum
2256      * size preference is less than the area size, the maximum size will be used.
2257      * If node's maximum is greater than the area size, then the node will be
2258      * resized to fit within the area, unless its minimum size prevents it.
2259      * <p>
2260      * If the child has a non-null contentBias, then this method will use it when
2261      * resizing the child.  If the contentBias is horizontal, it will set its width
2262      * first and then pass that value to compute the child's height.  If child's
2263      * contentBias is vertical, then it will set its height first
2264      * and pass that value to compute the child's width.  If the child's contentBias
2265      * is null, then it's width and height have no dependencies on each other.
2266      * <p>
2267      * If the child is not resizable (Shape, Group, etc) then it will only be
2268      * positioned and not resized.
2269      * <p>
2270      * If the child's resulting size differs from the area's size (either
2271      * because it was not resizable or it's sizing preferences prevented it), then
2272      * this function will align the node relative to the area using horizontal and
2273      * vertical alignment values.
2274      * If valignment is {@code VPos.BASELINE} then the node's baseline will be aligned
2275      * with the area baseline offset parameter, otherwise the baseline parameter
2276      * is ignored.
2277      * <p>
2278      * If {@code margin} is non-null, then that space will be allocated around the
2279      * child within the layout area.  margin may be null.
2280      * <p>
2281      * If {@code snapToPixel} is {@code true} for this region, then the resulting x,y
2282      * values will be rounded to their nearest pixel boundaries and the
2283      * width/height values will be ceiled to the next pixel boundary.
2284      *
2285      * @param child the child being positioned within this region
2286      * @param areaX the horizontal offset of the layout area relative to this region
2287      * @param areaY the vertical offset of the layout area relative to this region
2288      * @param areaWidth  the width of the layout area
2289      * @param areaHeight the height of the layout area
2290      * @param areaBaselineOffset the baseline offset to be used if VPos is BASELINE
2291      * @param margin the margin of space to be allocated around the child
2292      * @param fillWidth whether or not the child should be resized to fill the area width or kept to its preferred width
2293      * @param fillHeight whether or not the child should e resized to fill the area height or kept to its preferred height
2294      * @param halignment the horizontal alignment for the child within the area
2295      * @param valignment the vertical alignment for the child within the area
2296      * @param isSnapToPixel whether to snap size and position to pixels
2297      * @since JavaFX 8.0
2298      */
2299     public static void layoutInArea(Node child, double areaX, double areaY,
2300                                double areaWidth, double areaHeight,
2301                                double areaBaselineOffset,
2302                                Insets margin, boolean fillWidth, boolean fillHeight,
2303                                HPos halignment, VPos valignment, boolean isSnapToPixel) {
2304 
2305         Insets childMargin = margin != null ? margin : Insets.EMPTY;
2306 
2307         double top = snapSpace(childMargin.getTop(), isSnapToPixel);
2308         double bottom = snapSpace(childMargin.getBottom(), isSnapToPixel);
2309         double left = snapSpace(childMargin.getLeft(), isSnapToPixel);
2310         double right = snapSpace(childMargin.getRight(), isSnapToPixel);
2311 
2312         if (valignment == VPos.BASELINE) {
2313             double bo = child.getBaselineOffset();
2314             if (bo == BASELINE_OFFSET_SAME_AS_HEIGHT) {
2315                 if (child.isResizable()) {
2316                     // Everything below the baseline is like an "inset". The Node with BASELINE_OFFSET_SAME_AS_HEIGHT cannot
2317                     // be resized to this area
2318                     bottom += snapSpace(areaHeight - areaBaselineOffset, isSnapToPixel);
2319                 } else {
2320                     top = snapSpace(areaBaselineOffset - child.getLayoutBounds().getHeight(), isSnapToPixel);
2321                 }
2322             } else {
2323                 top = snapSpace(areaBaselineOffset - bo, isSnapToPixel);
2324             }
2325         }
2326 
2327 
2328         if (child.isResizable()) {
2329             Vec2d size = boundedNodeSizeWithBias(child, areaWidth - left - right, areaHeight - top - bottom,
2330                     fillWidth, fillHeight, TEMP_VEC2D);
2331             child.resize(snapSize(size.x, isSnapToPixel),snapSize(size.y, isSnapToPixel));
2332         }
2333         position(child, areaX, areaY, areaWidth, areaHeight, areaBaselineOffset,
2334                 top, right, bottom, left, halignment, valignment, isSnapToPixel);
2335     }
2336 
2337     private static void position(Node child, double areaX, double areaY, double areaWidth, double areaHeight,
2338                           double areaBaselineOffset,
2339                           double topMargin, double rightMargin, double bottomMargin, double leftMargin,
2340                           HPos hpos, VPos vpos, boolean isSnapToPixel) {
2341         final double xoffset = leftMargin + computeXOffset(areaWidth - leftMargin - rightMargin,
2342                                                      child.getLayoutBounds().getWidth(), hpos);
2343         final double yoffset;
2344         if (vpos == VPos.BASELINE) {
2345             double bo = child.getBaselineOffset();
2346             if (bo == BASELINE_OFFSET_SAME_AS_HEIGHT) {
2347                 // We already know the layout bounds at this stage, so we can use them
2348                 yoffset = areaBaselineOffset - child.getLayoutBounds().getHeight();
2349             } else {
2350                 yoffset = areaBaselineOffset - bo;
2351             }
2352         } else {
2353             yoffset = topMargin + computeYOffset(areaHeight - topMargin - bottomMargin,
2354                                          child.getLayoutBounds().getHeight(), vpos);
2355         }
2356         final double x = snapPosition(areaX + xoffset, isSnapToPixel);
2357         final double y = snapPosition(areaY + yoffset, isSnapToPixel);
2358 
2359         child.relocate(x,y);
2360     }
2361 
2362      /**************************************************************************
2363      *                                                                         *
2364      * PG Implementation                                                       *
2365      *                                                                         *
2366      **************************************************************************/
2367 
2368     /** @treatAsPrivate */
2369     @Override public void impl_updatePeer() {
2370         // TODO I think we have a bug, where if you create a Region with an Image that hasn't
2371         // been loaded, we have no listeners on that image so as to cause a pulse & repaint
2372         // to happen once the image is loaded. We just assume the image has been loaded
2373         // (since when the image is created using new Image(url) or CSS it happens eagerly).
2374         super.impl_updatePeer();
2375         if (_shape != null) _shape.impl_syncPeer();
2376         NGRegion pg = impl_getPeer();
2377 
2378         final boolean sizeChanged = impl_isDirty(DirtyBits.NODE_GEOMETRY);
2379         if (sizeChanged) {
2380             pg.setSize((float)getWidth(), (float)getHeight());
2381         }
2382 
2383         // NOTE: The order here is very important. There is logic in NGRegion which determines
2384         // whether we can cache an image representing this region, and for this to work correctly,
2385         // the shape must be specified before the background which is before the border.
2386         final boolean shapeChanged = impl_isDirty(DirtyBits.REGION_SHAPE);
2387         if (shapeChanged) {
2388             pg.updateShape(_shape, isScaleShape(), isCenterShape(), isCacheShape());
2389         }
2390 
2391         final boolean backgroundChanged = impl_isDirty(DirtyBits.SHAPE_FILL);
2392         final Background bg = getBackground();
2393         if (backgroundChanged) {
2394             pg.updateBackground(bg);
2395         }
2396 
2397         // This will be true if an image that makes up the background or border of this
2398         // region has changed, such that we need to redraw the region.
2399         if (impl_isDirty(DirtyBits.NODE_CONTENTS)) {
2400             pg.imagesUpdated();
2401         }
2402 
2403         if (impl_isDirty(DirtyBits.SHAPE_STROKE)) {
2404             pg.updateBorder(getBorder());
2405         }
2406 
2407         // TODO given the note above, this *must* be called when an image which makes up the
2408         // background images and border images changes (is loaded) if it was being loaded asynchronously
2409         // Also note, one day we can add support for automatic opaque insets determination for border images.
2410         // However right now it is impractical because the image pixel format is almost undoubtedly going
2411         // to have alpha, and so without inspecting the source image's actual pixels for the filled center
2412         // we can't automatically determine whether the interior is filled.
2413         if (sizeChanged || backgroundChanged || shapeChanged) {
2414             // These are the opaque insets, as specified by the developer in code or CSS. If null,
2415             // then we must compute the opaque insets. If not null, then we will still compute the
2416             // opaque insets and combine them with these insets, as appropriate. We do ignore these
2417             // developer specified insets in cases where we know without a doubt that the developer
2418             // gave us bad data.
2419             final Insets i = getOpaqueInsets();
2420 
2421             // If the background is determined by a shape, then we don't attempt to calculate the
2422             // opaque insets. If the developer specified opaque insets, we will use them, otherwise
2423             // we will make sure the opaque insets are cleared
2424             if (_shape != null) {
2425                 if (i != null) {
2426                     pg.setOpaqueInsets((float) i.getTop(), (float) i.getRight(),
2427                                        (float) i.getBottom(), (float) i.getLeft());
2428                 } else {
2429                     pg.setOpaqueInsets(Float.NaN, Float.NaN, Float.NaN, Float.NaN);
2430                 }
2431             } else {
2432                 // This is a rectangle (not shape) region. The opaque insets must be calculated,
2433                 // even if the developer has supplied their own opaque insets. The first (and cheapest)
2434                 // check is whether the region has any backgrounds at all. If not, then
2435                 // we will ignore the developer supplied insets because they are clearly wrong.
2436                 if (bg == null || bg.isEmpty()) {
2437                     pg.setOpaqueInsets(Float.NaN, Float.NaN, Float.NaN, Float.NaN);
2438                 } else {
2439                     // There is a background, so it is conceivable that there are
2440                     // opaque insets. From this point on, we have to honor the developer's supplied
2441                     // insets, only expanding them if we know for certain the opaque insets are
2442                     // bigger than what was supplied by the developer. Start by defining our initial
2443                     // values for top, right, bottom, and left. If the developer supplied us
2444                     // insets, use those. Otherwise initialize to NaN. Note that the developer may
2445                     // also have given us NaN values (so we'd have to check for these anyway). We use
2446                     // NaN to mean "not defined".
2447                     final double[] trbl = new double[4];
2448                     bg.computeOpaqueInsets(getWidth(), getHeight(), trbl);
2449 
2450                     if (i != null) {
2451                         trbl[0] = Double.isNaN(trbl[0]) ? i.getTop() : Double.isNaN(i.getTop()) ? trbl[0] : Math.min(trbl[0], i.getTop());
2452                         trbl[1] = Double.isNaN(trbl[1]) ? i.getRight() : Double.isNaN(i.getRight()) ? trbl[1] : Math.min(trbl[1], i.getRight());
2453                         trbl[2] = Double.isNaN(trbl[2]) ? i.getBottom() : Double.isNaN(i.getBottom()) ? trbl[2] : Math.min(trbl[2], i.getBottom());
2454                         trbl[3] = Double.isNaN(trbl[3]) ? i.getLeft() : Double.isNaN(i.getLeft()) ? trbl[3] : Math.min(trbl[3], i.getLeft());
2455                     }
2456 
2457                     // Now set the insets onto the peer. Passing NaN here is perfectly
2458                     // acceptable (even encouraged, to mean "unknown" or "disabled").
2459                     pg.setOpaqueInsets((float) trbl[0], (float) trbl[1], (float) trbl[2], (float) trbl[3]);
2460                 }
2461             }
2462         }
2463     }
2464 
2465     /** @treatAsPrivate */
2466     @Override public NGNode impl_createPeer() {
2467         return new NGRegion();
2468     }
2469     
2470     private com.sun.javafx.geom.Shape scaledShape;
2471     
2472     private com.sun.javafx.geom.Shape getShapeScaledAndPositioned() {
2473         if (scaledShape == null) {
2474              scaledShape = NGRegion.resizeShape(_shape.impl_configShape(), isScaleShape(), isCenterShape(),
2475                     (float)getWidth(), (float)getHeight(), 0f,0f,0f,0f);
2476         }
2477         return scaledShape;
2478     }
2479     
2480     /**
2481      * @treatAsPrivate implementation detail
2482      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2483      */
2484     @Deprecated
2485     @Override protected boolean impl_computeContains(double localX, double localY) {
2486         // NOTE: This method only gets called if a quick check of bounds has already
2487         // occurred, so there is no need to test against bound again. We know that the
2488         // point (localX, localY) falls within the bounds of this node, now we need
2489         // to determine if it falls within the geometry of this node.
2490         // Also note that because Region defaults pickOnBounds to true, this code is
2491         // not usually executed. It will only be executed if pickOnBounds is set to false.
2492 
2493         final double x2 = getWidth();
2494         final double y2 = getHeight();
2495         // Figure out what the maximum possible radius value is.
2496         final double maxRadius = Math.min(x2 / 2.0, y2 / 2.0);
2497 
2498         // First check the shape. Shape could be impacted by scaleShape & positionShape properties.
2499         // We are calling NGRegion static method here because we lack a better place for the code.
2500         if (_shape != null) {
2501             return getShapeScaledAndPositioned().contains((float)localX, (float)localY);
2502         }
2503 
2504         // OK, there was no background shape, so I'm going to work on the principle of
2505         // nested rounded rectangles. We'll start by checking the backgrounds. The
2506         // first background which passes the test is good enough for us!
2507         final Background background = getBackground();
2508         if (background != null) {
2509             final List<BackgroundFill> fills = background.getFills();
2510             for (int i = 0, max = fills.size(); i < max; i++) {
2511                 final BackgroundFill bgFill = fills.get(i);
2512                 if (contains(localX, localY, 0, 0, x2, y2, bgFill.getInsets(), bgFill.getRadii(), maxRadius)) {
2513                     return true;
2514                 }
2515             }
2516         }
2517 
2518         // If we are here then either there were no background fills or there were no background
2519         // fills which contained the point, and the region is not defined by a shape.
2520         final Border border = getBorder();
2521         if (border != null) {
2522             // Check all the stroke borders first. If the pick occurs on any stroke border
2523             // then we consider the contains test to have passed. Semantically we will treat a Region
2524             // with a border as if it were a rectangle with a stroke but no fill.
2525             final List<BorderStroke> strokes = border.getStrokes();
2526             for (int i=0, max=strokes.size(); i<max; i++) {
2527                 final BorderStroke strokeBorder = strokes.get(i);
2528                 if (contains(localX, localY, 0, 0, x2, y2, strokeBorder.getWidths(), false, strokeBorder.getInsets(),
2529                              strokeBorder.getRadii(), maxRadius)) {
2530                     return true;
2531                 }
2532             }
2533 
2534             // Check the image borders. We treat the image border as though it is opaque.
2535             final List<BorderImage> images = border.getImages();
2536             for (int i = 0, max = images.size(); i < max; i++) {
2537                 final BorderImage borderImage = images.get(i);
2538                 if (contains(localX, localY, 0, 0, x2, y2, borderImage.getWidths(), borderImage.isFilled(),
2539                              borderImage.getInsets(), CornerRadii.EMPTY, maxRadius)) {
2540                     return true;
2541                 }
2542             }
2543         }
2544         return false;
2545     }
2546 
2547     /**
2548      * Basically we will perform two contains tests. For a point to be on the stroke, it must
2549      * be within the outermost edge of the stroke, but outside the innermost edge of the stroke.
2550      * Unless it is filled, in which case it is really just a normal contains test.
2551      *
2552      * @param px        The x position of the point to test
2553      * @param py        The y position of the point to test
2554      * @param x1        The x1 position of the bounds to test
2555      * @param y1        The y1 position of the bounds to test
2556      * @param x2        The x2 position of the bounds to test
2557      * @param y2        The y2 position of the bounds to test
2558      * @param widths    The widths of the stroke on each side
2559      * @param filled    Whether the area is filled or is just stroked
2560      * @param insets    The insets to apply to (x1,y1)-(x2,y2) to get the final bounds to test
2561      * @param rad       The corner radii to test with. Must not be null.
2562      * @param maxRadius The maximum possible radius value
2563      * @return True if (px, py) is within the stroke, taking into account insets and corner radii.
2564      */
2565     private boolean contains(final double px, final double py,
2566                              final double x1, final double y1, final double x2, final double y2,
2567                              BorderWidths widths, boolean filled,
2568                              final Insets insets, final CornerRadii rad, final double maxRadius) {
2569         if (filled) {
2570             if (contains(px, py, x1, y1, x2, y2, insets, rad, maxRadius)) {
2571                 return true;
2572             }
2573         } else {
2574             boolean insideOuterEdge = contains(px, py, x1, y1, x2, y2, insets, rad, maxRadius);
2575             if (insideOuterEdge) {
2576                 boolean outsideInnerEdge = !contains(px, py,
2577                     x1 + (widths.isLeftAsPercentage() ? getWidth() * widths.getLeft() : widths.getLeft()),
2578                     y1 + (widths.isTopAsPercentage() ? getHeight() * widths.getTop() : widths.getTop()),
2579                     x2 - (widths.isRightAsPercentage() ? getWidth() * widths.getRight() : widths.getRight()),
2580                     y2 - (widths.isBottomAsPercentage() ? getHeight() * widths.getBottom() : widths.getBottom()),
2581                     insets, rad, maxRadius);
2582                 if (outsideInnerEdge) return true;
2583             }
2584         }
2585         return false;
2586     }
2587 
2588     /**
2589      * Determines whether the point (px, py) is contained within the the bounds (x1, y1)-(x2, y2),
2590      * after taking into account the insets and the corner radii.
2591      *
2592      * @param px        The x position of the point to test
2593      * @param py        The y position of the point to test
2594      * @param x1        The x1 position of the bounds to test
2595      * @param y1        The y1 position of the bounds to test
2596      * @param x2        The x2 position of the bounds to test
2597      * @param y2        The y2 position of the bounds to test
2598      * @param insets    The insets to apply to (x1,y1)-(x2,y2) to get the final bounds to test
2599      * @param rad       The corner radii to test with. Must not be null.
2600      * @param maxRadius The maximum possible radius value
2601      * @return True if (px, py) is within the bounds, taking into account insets and corner radii.
2602      */
2603     private boolean contains(final double px, final double py,
2604                              final double x1, final double y1, final double x2, final double y2,
2605                              final Insets insets, CornerRadii rad, final double maxRadius) {
2606         // These four values are the x0, y0, x1, y1 bounding box after
2607         // having taken into account the insets of this particular
2608         // background fill.
2609         final double rrx0 = x1 + insets.getLeft();
2610         final double rry0 = y1 + insets.getTop();
2611         final double rrx1 = x2 - insets.getRight();
2612         final double rry1 = y2 - insets.getBottom();
2613 
2614         // Adjust based on whether it is % based radii
2615         rad = normalize(rad);
2616 
2617         // Check for trivial rejection - point is inside bounding rectangle
2618         if (px >= rrx0 && py >= rry0 && px <= rrx1 && py <= rry1) {
2619             // The point was within the index bounding box. Now we need to analyze the
2620             // corner radii to see if the point lies within the corners or not. If the
2621             // point is within a corner then we reject this one.
2622             final double tlhr = Math.min(rad.getTopLeftHorizontalRadius(), maxRadius);
2623             if (rad.isUniform() && tlhr == 0) {
2624                 // This is a simple square! Since we know the point is already within
2625                 // the insets of this fill, we can simply return true.
2626                 return true;
2627             } else {
2628                 final double tlvr = Math.min(rad.getTopLeftVerticalRadius(), maxRadius);
2629                 final double trhr = Math.min(rad.getTopRightHorizontalRadius(), maxRadius);
2630                 final double trvr = Math.min(rad.getTopRightVerticalRadius(), maxRadius);
2631                 final double blhr = Math.min(rad.getBottomLeftHorizontalRadius(), maxRadius);
2632                 final double blvr = Math.min(rad.getBottomLeftVerticalRadius(), maxRadius);
2633                 final double brhr = Math.min(rad.getBottomRightHorizontalRadius(), maxRadius);
2634                 final double brvr = Math.min(rad.getBottomRightVerticalRadius(), maxRadius);
2635 
2636                 // The four corners can each be described as a quarter of an ellipse
2637                 double centerX, centerY, a, b;
2638 
2639                 if (px <= rrx0 + tlhr && py <= rry0 + tlvr) {
2640                     // Point is in the top left corner
2641                     centerX = rrx0 + tlhr;
2642                     centerY = rry0 + tlvr;
2643                     a = tlhr;
2644                     b = tlvr;
2645                 } else if (px >= rrx1 - trhr && py <= rry0 + trvr) {
2646                     // Point is in the top right corner
2647                     centerX = rrx1 - trhr;
2648                     centerY = rry0 + trvr;
2649                     a = trhr;
2650                     b = trvr;
2651                 } else if (px >= rrx1 - brhr && py >= rry1 - brvr) {
2652                     // Point is in the bottom right corner
2653                     centerX = rrx1 - brhr;
2654                     centerY = rry1 - brvr;
2655                     a = brhr;
2656                     b = brvr;
2657                 } else if (px <= rrx0 + blhr && py >= rry1 - blvr) {
2658                     // Point is in the bottom left corner
2659                     centerX = rrx0 + blhr;
2660                     centerY = rry1 - blvr;
2661                     a = blhr;
2662                     b = blvr;
2663                 } else {
2664                     // The point must have been in the solid body someplace
2665                     return true;
2666                 }
2667 
2668                 double x = px - centerX;
2669                 double y = py - centerY;
2670                 double result = ((x*x)/(a*a) + (y*y)/(b*b));
2671                 // The .0000001 is fudge to help in cases where double arithmetic isn't quite right
2672                 if (result - .0000001 <= 1) return true;
2673             }
2674         }
2675         return false;
2676     }
2677 
2678     /**
2679      * Direct copy of a method in NGRegion. If NGRegion were part of the core graphics module (coming!)
2680      * then this method could be removed.
2681      *
2682      * @param radii    The radii.
2683      * @return Normalized radii.
2684      */
2685     private CornerRadii normalize(CornerRadii radii) {
2686         final double width = getWidth();
2687         final double height = getHeight();
2688         final double tlvr = radii.isTopLeftVerticalRadiusAsPercentage() ? height * radii.getTopLeftVerticalRadius() : radii.getTopLeftVerticalRadius();
2689         final double tlhr = radii.isTopLeftHorizontalRadiusAsPercentage() ? width * radii.getTopLeftHorizontalRadius() : radii.getTopLeftHorizontalRadius();
2690         final double trvr = radii.isTopRightVerticalRadiusAsPercentage() ? height * radii.getTopRightVerticalRadius() : radii.getTopRightVerticalRadius();
2691         final double trhr = radii.isTopRightHorizontalRadiusAsPercentage() ? width * radii.getTopRightHorizontalRadius() : radii.getTopRightHorizontalRadius();
2692         final double brvr = radii.isBottomRightVerticalRadiusAsPercentage() ? height * radii.getBottomRightVerticalRadius() : radii.getBottomRightVerticalRadius();
2693         final double brhr = radii.isBottomRightHorizontalRadiusAsPercentage() ? width * radii.getBottomRightHorizontalRadius() : radii.getBottomRightHorizontalRadius();
2694         final double blvr = radii.isBottomLeftVerticalRadiusAsPercentage() ? height * radii.getBottomLeftVerticalRadius() : radii.getBottomLeftVerticalRadius();
2695         final double blhr = radii.isBottomLeftHorizontalRadiusAsPercentage() ? width * radii.getBottomLeftHorizontalRadius() : radii.getBottomLeftHorizontalRadius();
2696         return new CornerRadii(tlhr, tlvr, trvr, trhr, brhr, brvr, blvr, blhr, false, false, false, false, false, false, false, false);
2697     }
2698 
2699     /**
2700      * Some skins relying on this
2701      * @treatAsPrivate implementation detail
2702      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2703      */
2704     @Deprecated
2705     @Override protected void impl_pickNodeLocal(PickRay pickRay, PickResultChooser result) {
2706 
2707         double boundsDistance = impl_intersectsBounds(pickRay);
2708 
2709         if (!Double.isNaN(boundsDistance)) {
2710             ObservableList<Node> children = getChildren();
2711             for (int i = children.size()-1; i >= 0; i--) {
2712                 children.get(i).impl_pickNode(pickRay, result);
2713                 if (result.isClosed()) {
2714                     return;
2715                 }
2716             }
2717 
2718             impl_intersects(pickRay, result);
2719         }
2720     }
2721 
2722     private Bounds boundingBox;
2723 
2724     /**
2725      * The layout bounds of this region: {@code 0, 0  width x height}
2726      *
2727      * @treatAsPrivate implementation detail
2728      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2729      */
2730     @Deprecated
2731     @Override protected final Bounds impl_computeLayoutBounds() {
2732         if (boundingBox == null) {
2733             // we reuse the bounding box if the width and height haven't changed.
2734             boundingBox = new BoundingBox(0, 0, 0, getWidth(), getHeight(), 0);
2735         }
2736         return boundingBox;
2737     }
2738 
2739     /**
2740      * @treatAsPrivate implementation detail
2741      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2742      */
2743     @Deprecated
2744     @Override final protected void impl_notifyLayoutBoundsChanged() {
2745         // override Node's default behavior of having a geometric bounds change
2746         // trigger a change in layoutBounds. For Resizable nodes, layoutBounds
2747         // is unrelated to geometric bounds.
2748     }
2749 
2750     /**
2751      * @treatAsPrivate implementation detail
2752      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
2753      */
2754     @Deprecated
2755     @Override public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
2756         // Unlike Group, a Region has its own intrinsic geometric bounds, even if it has no children.
2757         // The bounds of the Region must take into account any backgrounds and borders and how
2758         // they are used to draw the Region. The geom bounds must always take into account
2759         // all pixels drawn (because the geom bounds forms the basis of the dirty regions).
2760         // Note that the layout bounds of a Region is not based on the geom bounds.
2761 
2762         // Define some variables to hold the top-left and bottom-right corners of the bounds
2763         double bx1 = 0;
2764         double by1 = 0;
2765         double bx2 = getWidth();
2766         double by2 = getHeight();
2767 
2768         // If the shape is defined, then the top-left and bottom-right corner positions
2769         // need to be redefined
2770         if (_shape != null && isScaleShape() == false) {
2771             final Bounds layoutBounds = _shape.getLayoutBounds();
2772             final double shapeWidth = layoutBounds.getWidth();
2773             final double shapeHeight = layoutBounds.getHeight();
2774             if (isCenterShape()) {
2775                 bx1 = (bx2 - shapeWidth) / 2;
2776                 by1 = (by2 - shapeHeight) / 2;
2777                 bx2 = bx1 + shapeWidth;
2778                 by2 = by1 + shapeHeight;
2779             } else {
2780                 bx1 = layoutBounds.getMinX();
2781                 by1 = layoutBounds.getMinY();
2782                 bx2 = layoutBounds.getMaxX();
2783                 by2 = layoutBounds.getMaxY();
2784             }
2785         }
2786 
2787         // Expand the bounds to include the outsets from the background and border.
2788         // The outsets are the opposite of insets -- a measure of distance from the
2789         // edge of the Region outward. The outsets cannot, however, be negative.
2790         final Background background = getBackground();
2791         final Border border = getBorder();
2792         final Insets backgroundOutsets = background == null ? Insets.EMPTY : background.getOutsets();
2793         final Insets borderOutsets = border == null ? Insets.EMPTY : border.getOutsets();
2794         bx1 -= Math.max(backgroundOutsets.getLeft(), borderOutsets.getLeft());
2795         by1 -= Math.max(backgroundOutsets.getTop(), borderOutsets.getTop());
2796         bx2 += Math.max(backgroundOutsets.getRight(), borderOutsets.getRight());
2797         by2 += Math.max(backgroundOutsets.getBottom(), borderOutsets.getBottom());
2798 
2799         // NOTE: Okay to call impl_computeGeomBounds with tx even in the 3D case
2800         // since Parent.impl_computeGeomBounds does handle 3D correctly.
2801         BaseBounds cb = super.impl_computeGeomBounds(bounds, tx);
2802         /*
2803          * This is a work around for RT-7680. Parent returns invalid bounds from
2804          * impl_computeGeomBounds when it has no children or if all its children
2805          * have invalid bounds. If RT-7680 were fixed, then we could omit this
2806          * first branch of the if and only use the else since the correct value
2807          * would be computed.
2808          */
2809         if (cb.isEmpty()) {
2810             // There are no children bounds, so
2811             bounds = bounds.deriveWithNewBounds(
2812                     (float)bx1, (float)by1, 0.0f,
2813                     (float)bx2, (float)by2, 0.0f);
2814             bounds = tx.transform(bounds, bounds);
2815             return bounds;
2816         } else {
2817             // Union with children's bounds
2818             BaseBounds tempBounds = TempState.getInstance().bounds;
2819             tempBounds = tempBounds.deriveWithNewBounds(
2820                     (float)bx1, (float)by1, 0.0f,
2821                     (float)bx2, (float)by2, 0.0f);
2822             BaseBounds bb = tx.transform(tempBounds, tempBounds);
2823             cb = cb.deriveWithUnion(bb);
2824             return cb;
2825         }
2826     }
2827 
2828     /***************************************************************************
2829      *                                                                         *
2830      * CSS                                                                     *
2831      *                                                                         *
2832      **************************************************************************/
2833 
2834      /**
2835       * Super-lazy instantiation pattern from Bill Pugh.
2836       * @treatAsPrivate implementation detail
2837       */
2838      private static class StyleableProperties {
2839          private static final CssMetaData<Region,Insets> PADDING =
2840              new CssMetaData<Region,Insets>("-fx-padding",
2841                  InsetsConverter.getInstance(), Insets.EMPTY) {
2842 
2843             @Override public boolean isSettable(Region node) {
2844                 return node.padding == null || !node.padding.isBound();
2845             }
2846 
2847             @Override public StyleableProperty<Insets> getStyleableProperty(Region node) {
2848                 return (StyleableProperty<Insets>)node.paddingProperty();
2849             }
2850          };
2851 
2852          private static final CssMetaData<Region,Insets> OPAQUE_INSETS =
2853                  new CssMetaData<Region,Insets>("-fx-opaque-insets",
2854                          InsetsConverter.getInstance(), null) {
2855 
2856                      @Override
2857                      public boolean isSettable(Region node) {
2858                          return node.opaqueInsets == null || !node.opaqueInsets.isBound();
2859                      }
2860 
2861                      @Override
2862                      public StyleableProperty<Insets> getStyleableProperty(Region node) {
2863                          return (StyleableProperty<Insets>)node.opaqueInsetsProperty();
2864                      }
2865 
2866                  };
2867 
2868          private static final CssMetaData<Region,Background> BACKGROUND =
2869              new CssMetaData<Region,Background>("-fx-region-background",
2870                  BackgroundConverter.INSTANCE,
2871                  null,
2872                  false,
2873                  Background.getClassCssMetaData()) {
2874 
2875             @Override public boolean isSettable(Region node) {
2876                 return !node.background.isBound();
2877             }
2878 
2879             @Override public StyleableProperty<Background> getStyleableProperty(Region node) {
2880                 return (StyleableProperty<Background>)node.background;
2881             }
2882          };
2883 
2884          private static final CssMetaData<Region,Border> BORDER =
2885              new CssMetaData<Region,Border>("-fx-region-border",
2886                      BorderConverter.getInstance(),
2887                      null,
2888                      false,
2889                      Border.getClassCssMetaData()) {
2890 
2891                  @Override public boolean isSettable(Region node) {
2892                      return !node.background.isBound();
2893                  }
2894 
2895                  @Override public StyleableProperty<Border> getStyleableProperty(Region node) {
2896                      return (StyleableProperty<Border>)node.border;
2897                  }
2898              };
2899 
2900          private static final CssMetaData<Region,Shape> SHAPE =
2901              new CssMetaData<Region,Shape>("-fx-shape",
2902                  ShapeConverter.getInstance()) {
2903 
2904             @Override public boolean isSettable(Region node) {
2905                 // isSettable depends on node.shape, not node.shapeContent
2906                 return node.shape == null || !node.shape.isBound();
2907             }
2908 
2909             @Override public StyleableProperty<Shape> getStyleableProperty(Region node) {
2910                 return (StyleableProperty<Shape>)node.shapeProperty();
2911             }
2912          };
2913 
2914          private static final CssMetaData<Region, Boolean> SCALE_SHAPE =
2915              new CssMetaData<Region,Boolean>("-fx-scale-shape",
2916                  BooleanConverter.getInstance(), Boolean.TRUE){
2917 
2918             @Override public boolean isSettable(Region node) {
2919                 return node.scaleShape == null || !node.scaleShape.isBound();
2920             }
2921 
2922             @Override public StyleableProperty<Boolean> getStyleableProperty(Region node) {
2923                 return (StyleableProperty<Boolean>)node.scaleShapeProperty();
2924             }
2925         };
2926 
2927          private static final CssMetaData<Region,Boolean> POSITION_SHAPE =
2928              new CssMetaData<Region,Boolean>("-fx-position-shape",
2929                  BooleanConverter.getInstance(), Boolean.TRUE){
2930 
2931             @Override public boolean isSettable(Region node) {
2932                 return node.centerShape == null || !node.centerShape.isBound();
2933             }
2934 
2935             @Override public StyleableProperty<Boolean> getStyleableProperty(Region node) {
2936                 return (StyleableProperty<Boolean>)node.centerShapeProperty();
2937             }
2938         };
2939 
2940          private static final CssMetaData<Region,Boolean> CACHE_SHAPE =
2941              new CssMetaData<Region,Boolean>("-fx-cache-shape",
2942                  BooleanConverter.getInstance(), Boolean.TRUE){
2943 
2944             @Override public boolean isSettable(Region node) {
2945                 return node.cacheShape == null || !node.cacheShape.isBound();
2946             }
2947 
2948             @Override public StyleableProperty<Boolean> getStyleableProperty(Region node) {
2949                 return (StyleableProperty<Boolean>)node.cacheShapeProperty();
2950             }
2951         };
2952 
2953          private static final CssMetaData<Region, Boolean> SNAP_TO_PIXEL =
2954              new CssMetaData<Region,Boolean>("-fx-snap-to-pixel",
2955                  BooleanConverter.getInstance(), Boolean.TRUE){
2956 
2957             @Override public boolean isSettable(Region node) {
2958                 return node.snapToPixel == null ||
2959                         !node.snapToPixel.isBound();
2960             }
2961 
2962             @Override public StyleableProperty<Boolean> getStyleableProperty(Region node) {
2963                 return (StyleableProperty<Boolean>)node.snapToPixelProperty();
2964             }
2965         };
2966 
2967          private static final CssMetaData<Region, Number> MIN_HEIGHT =
2968              new CssMetaData<Region,Number>("-fx-min-height",
2969                  SizeConverter.getInstance(), USE_COMPUTED_SIZE){
2970 
2971             @Override public boolean isSettable(Region node) {
2972                 return node.minHeight == null ||
2973                         !node.minHeight.isBound();
2974             }
2975 
2976             @Override public StyleableProperty<Number> getStyleableProperty(Region node) {
2977                 return (StyleableProperty<Number>)node.minHeightProperty();
2978             }
2979         };
2980 
2981          private static final CssMetaData<Region, Number> PREF_HEIGHT =
2982              new CssMetaData<Region,Number>("-fx-pref-height",
2983                  SizeConverter.getInstance(), USE_COMPUTED_SIZE){
2984 
2985             @Override public boolean isSettable(Region node) {
2986                 return node.prefHeight == null ||
2987                         !node.prefHeight.isBound();
2988             }
2989 
2990             @Override public StyleableProperty<Number> getStyleableProperty(Region node) {
2991                 return (StyleableProperty<Number>)node.prefHeightProperty();
2992             }
2993         };
2994 
2995          private static final CssMetaData<Region, Number> MAX_HEIGHT =
2996              new CssMetaData<Region,Number>("-fx-max-height",
2997                  SizeConverter.getInstance(), USE_COMPUTED_SIZE){
2998 
2999             @Override public boolean isSettable(Region node) {
3000                 return node.maxHeight == null ||
3001                         !node.maxHeight.isBound();
3002             }
3003 
3004             @Override public StyleableProperty<Number> getStyleableProperty(Region node) {
3005                 return (StyleableProperty<Number>)node.maxHeightProperty();
3006             }
3007         };
3008 
3009          private static final CssMetaData<Region, Number> MIN_WIDTH =
3010              new CssMetaData<Region,Number>("-fx-min-width",
3011                  SizeConverter.getInstance(), USE_COMPUTED_SIZE){
3012 
3013             @Override public boolean isSettable(Region node) {
3014                 return node.minWidth == null ||
3015                         !node.minWidth.isBound();
3016             }
3017 
3018             @Override public StyleableProperty<Number> getStyleableProperty(Region node) {
3019                 return (StyleableProperty<Number>)node.minWidthProperty();
3020             }
3021         };
3022 
3023          private static final CssMetaData<Region, Number> PREF_WIDTH =
3024              new CssMetaData<Region,Number>("-fx-pref-width",
3025                  SizeConverter.getInstance(), USE_COMPUTED_SIZE){
3026 
3027             @Override public boolean isSettable(Region node) {
3028                 return node.prefWidth == null ||
3029                         !node.prefWidth.isBound();
3030             }
3031 
3032             @Override public StyleableProperty<Number> getStyleableProperty(Region node) {
3033                 return (StyleableProperty<Number>)node.prefWidthProperty();
3034             }
3035         };
3036 
3037          private static final CssMetaData<Region, Number> MAX_WIDTH =
3038              new CssMetaData<Region,Number>("-fx-max-width",
3039                  SizeConverter.getInstance(), USE_COMPUTED_SIZE){
3040 
3041             @Override public boolean isSettable(Region node) {
3042                 return node.maxWidth == null ||
3043                         !node.maxWidth.isBound();
3044             }
3045 
3046             @Override public StyleableProperty<Number> getStyleableProperty(Region node) {
3047                 return (StyleableProperty<Number>)node.maxWidthProperty();
3048             }
3049         };
3050 
3051          private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
3052          static {
3053 
3054             final List<CssMetaData<? extends Styleable, ?>> styleables =
3055                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Parent.getClassCssMetaData());
3056             styleables.add(PADDING);
3057             styleables.add(BACKGROUND);
3058             styleables.add(BORDER);
3059             styleables.add(OPAQUE_INSETS);
3060             styleables.add(SHAPE);
3061             styleables.add(SCALE_SHAPE);
3062             styleables.add(POSITION_SHAPE);
3063             styleables.add(SNAP_TO_PIXEL);
3064             styleables.add(MIN_WIDTH);
3065             styleables.add(PREF_WIDTH);
3066             styleables.add(MAX_WIDTH);
3067             styleables.add(MIN_HEIGHT);
3068             styleables.add(PREF_HEIGHT);
3069             styleables.add(MAX_HEIGHT);
3070             STYLEABLES = Collections.unmodifiableList(styleables);
3071          }
3072     }
3073 
3074     /**
3075      * @return The CssMetaData associated with this class, which may include the
3076      * CssMetaData of its super classes.
3077      * @since JavaFX 8.0
3078      */
3079     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
3080         return StyleableProperties.STYLEABLES;
3081     }
3082 
3083     /**
3084      * {@inheritDoc}
3085      *
3086      * @since JavaFX 8.0
3087      */
3088 
3089 
3090     @Override
3091     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
3092         return getClassCssMetaData();
3093     }
3094 
3095 }