1 /*
   2  * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.layout;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.List;
  31 import javafx.beans.property.BooleanProperty;
  32 import javafx.beans.property.DoubleProperty;
  33 import javafx.beans.property.ObjectProperty;
  34 import javafx.css.CssMetaData;
  35 import javafx.css.StyleableBooleanProperty;
  36 import javafx.css.StyleableDoubleProperty;
  37 import javafx.css.StyleableObjectProperty;
  38 import javafx.css.StyleableProperty;
  39 import javafx.geometry.HPos;
  40 import javafx.geometry.Insets;
  41 import javafx.geometry.Orientation;
  42 import javafx.geometry.Pos;
  43 import javafx.geometry.VPos;
  44 import javafx.scene.Node;
  45 import javafx.css.converter.BooleanConverter;
  46 import javafx.css.converter.EnumConverter;
  47 import javafx.css.converter.SizeConverter;
  48 import javafx.css.Styleable;
  49 import javafx.util.Callback;
  50 
  51 /**
  52  * VBox lays out its children in a single vertical column.
  53  * If the vbox has a border and/or padding set, then the contents will be laid
  54  * out within those insets.
  55  * <p>
  56  * VBox example:
  57  * <pre>{@code
  58  *     VBox vbox = new VBox(8); // spacing = 8
  59  *     vbox.getChildren().addAll(new Button("Cut"), new Button("Copy"), new Button("Paste"));
  60  * }</pre>
  61  *
  62  * VBox will resize children (if resizable) to their preferred heights and uses its
  63  * {@link #fillWidthProperty() fillWidth} property to determine whether to resize their widths to
  64  * fill its own width or keep their widths to their preferred (fillWidth defaults to true).
  65  * The alignment of the content is controlled by the {@link #alignmentProperty() alignment} property,
  66  * which defaults to Pos.TOP_LEFT.
  67  * <p>
  68  * If a vbox is resized larger than its preferred height, by default it will keep
  69  * children to their preferred heights, leaving the extra space unused.  If an
  70  * application wishes to have one or more children be allocated that extra space
  71  * it may optionally set a vgrow constraint on the child.  See "Optional Layout
  72  * Constraints" for details.
  73  * <p>
  74  * VBox lays out each managed child regardless of the child's
  75  * visible property value; unmanaged children are ignored.</p>
  76  *
  77  * <h3>Resizable Range</h3>
  78  *
  79  * <p>
  80  * A vbox's parent will resize the vbox within the vbox's resizable range
  81  * during layout.   By default the vbox computes this range based on its content
  82  * as outlined in the table below.
  83  * </p>
  84  * <table border="1">
  85  * <caption>VBox Resize Table</caption>
  86  * <tr><td></td><th scope="col">width</th><th scope="col">height</th></tr>
  87  * <tr><th scope="row">minimum</th>
  88  * <td>left/right insets plus the largest of the children's min widths.</td>
  89  * <td>top/bottom insets plus the sum of each child's min height plus spacing between each child.</td>
  90  * </tr>
  91  * <tr><th scope="row">preferred</th>
  92  * <td>left/right insets plus the largest of the children's pref widths.</td>
  93  * <td>top/bottom insets plus the sum of each child's pref height plus spacing between each child.</td>
  94  * </tr>
  95  * <tr><th scope="row">maximum</th>
  96  * <td>Double.MAX_VALUE</td><td>Double.MAX_VALUE</td></tr>
  97  * </table>
  98  * <p>
  99  * A vbox's unbounded maximum width and height are an indication to the parent that
 100  * it may be resized beyond its preferred size to fill whatever space is assigned
 101  * to it.
 102  * <p>
 103  * VBox provides properties for setting the size range directly.  These
 104  * properties default to the sentinel value USE_COMPUTED_SIZE, however the
 105  * application may set them to other values as needed:
 106  * <pre><code>
 107  *     <b>vbox.setPrefWidth(400);</b>
 108  * </code></pre>
 109  * Applications may restore the computed values by setting these properties back
 110  * to USE_COMPUTED_SIZE.
 111  * <p>
 112  * VBox does not clip its content by default, so it is possible that children's
 113  * bounds may extend outside its own bounds if a child's min size prevents it from
 114  * being fit within the vbox.</p>
 115  *
 116  * <h3>Optional Layout Constraints</h3>
 117  *
 118  * <p>
 119  * An application may set constraints on individual children to customize VBox's layout.
 120  * For each constraint, VBox provides a static method for setting it on the child.
 121  * </p>
 122  *
 123  * <table border="1">
 124  * <caption>VBox Constraint Table</caption>
 125  * <tr><th scope="col">Constraint</th><th scope="col">Type</th><th scope="col">Description</th></tr>
 126  * <tr><th scope="row">vgrow</th><td>javafx.scene.layout.Priority</td><td>The vertical grow priority for the child.</td></tr>
 127  * <tr><th scope="row">margin</th><td>javafx.geometry.Insets</td><td>Margin space around the outside of the child.</td></tr>
 128  * </table>
 129  * <p>
 130  * For example, if a vbox needs the ListView to be allocated all extra space:
 131  * <pre><code>
 132  *     VBox vbox = new VBox();
 133  *     ListView list = new ListView();
 134  *     <b>VBox.setVgrow(list, Priority.ALWAYS);</b>
 135  *     vbox.getChildren().addAll(new Label("Names:"), list);
 136  * </code></pre>
 137  *
 138  * If more than one child has the same grow priority set, then the vbox will
 139  * allocate equal amounts of space to each.  VBox will only grow a child up to
 140  * its maximum height, so if the child has a max height other than Double.MAX_VALUE,
 141  * the application may need to override the max to allow it to grow.
 142  * @since JavaFX 2.0
 143  */
 144 public class VBox extends Pane {
 145 
 146     private boolean biasDirty = true;
 147     private Orientation bias;
 148     private double[][] tempArray;
 149 
 150 /********************************************************************
 151      *  BEGIN static methods
 152      ********************************************************************/
 153     private static final String MARGIN_CONSTRAINT = "vbox-margin";
 154     private static final String VGROW_CONSTRAINT = "vbox-vgrow";
 155 
 156     /**
 157      * Sets the vertical grow priority for the child when contained by an vbox.
 158      * If set, the vbox will use the priority to allocate additional space if the
 159      * vbox is resized larger than it's preferred height.
 160      * If multiple vbox children have the same vertical grow priority, then the
 161      * extra space will be split evenly between them.
 162      * If no vertical grow priority is set on a child, the vbox will never
 163      * allocate it additional vertical space if available.
 164      * Setting the value to null will remove the constraint.
 165      * @param child the child of a vbox
 166      * @param value the horizontal grow priority for the child
 167      */
 168     public static void setVgrow(Node child, Priority value) {
 169         setConstraint(child, VGROW_CONSTRAINT, value);
 170     }
 171 
 172     /**
 173      * Returns the child's vgrow property if set.
 174      * @param child the child node of a vbox
 175      * @return the vertical grow priority for the child or null if no priority was set
 176      */
 177     public static Priority getVgrow(Node child) {
 178         return (Priority)getConstraint(child, VGROW_CONSTRAINT);
 179     }
 180 
 181     /**
 182      * Sets the margin for the child when contained by a vbox.
 183      * If set, the vbox will layout the child so that it has the margin space around it.
 184      * Setting the value to null will remove the constraint.
 185      * @param child the child mode of a vbox
 186      * @param value the margin of space around the child
 187      */
 188     public static void setMargin(Node child, Insets value) {
 189         setConstraint(child, MARGIN_CONSTRAINT, value);
 190     }
 191 
 192     /**
 193      * Returns the child's margin property if set.
 194      * @param child the child node of a vbox
 195      * @return the margin for the child or null if no margin was set
 196      */
 197     public static Insets getMargin(Node child) {
 198         return (Insets)getConstraint(child, MARGIN_CONSTRAINT);
 199     }
 200 
 201     private static final Callback<Node, Insets> marginAccessor = n -> getMargin(n);
 202 
 203     /**
 204      * Removes all vbox constraints from the child node.
 205      * @param child the child node
 206      */
 207     public static void clearConstraints(Node child) {
 208         setVgrow(child, null);
 209         setMargin(child, null);
 210     }
 211 
 212     /********************************************************************
 213      *  END static methods
 214      ********************************************************************/
 215 
 216     /**
 217      * Creates a VBox layout with spacing = 0 and alignment at TOP_LEFT.
 218      */
 219     public VBox() {
 220         super();
 221     }
 222 
 223     /**
 224      * Creates a VBox layout with the specified spacing between children.
 225      * @param spacing the amount of vertical space between each child
 226      */
 227     public VBox(double spacing) {
 228         this();
 229         setSpacing(spacing);
 230     }
 231 
 232     /**
 233      * Creates an VBox layout with spacing = 0.
 234      * @param children The initial set of children for this pane.
 235      * @since JavaFX 8.0
 236      */
 237     public VBox(Node... children) {
 238         super();
 239         getChildren().addAll(children);
 240     }
 241 
 242     /**
 243      * Creates an VBox layout with the specified spacing between children.
 244      * @param spacing the amount of horizontal space between each child
 245      * @param children The initial set of children for this pane.
 246      * @since JavaFX 8.0
 247      */
 248     public VBox(double spacing, Node... children) {
 249         this();
 250         setSpacing(spacing);
 251         getChildren().addAll(children);
 252     }
 253 
 254     /**
 255      * The amount of vertical space between each child in the vbox.
 256      * @return the amount of vertical space between each child in the vbox
 257      */
 258     public final DoubleProperty spacingProperty() {
 259         if (spacing == null) {
 260             spacing = new StyleableDoubleProperty() {
 261                 @Override
 262                 public void invalidated() {
 263                     requestLayout();
 264                 }
 265 
 266                 @Override
 267                 public Object getBean() {
 268                     return VBox.this;
 269                 }
 270 
 271                 @Override
 272                 public String getName() {
 273                     return "spacing";
 274                 }
 275 
 276                 @Override
 277                 public CssMetaData<VBox, Number> getCssMetaData() {
 278                     return StyleableProperties.SPACING;
 279                 }
 280             };
 281         }
 282         return spacing;
 283     }
 284 
 285     private DoubleProperty spacing;
 286     public final void setSpacing(double value) { spacingProperty().set(value); }
 287     public final double getSpacing() { return spacing == null ? 0 : spacing.get(); }
 288 
 289     /**
 290      * The overall alignment of children within the vbox's width and height.
 291      * @return the overall alignment of children within the vbox's width and
 292      * height
 293      */
 294     public final ObjectProperty<Pos> alignmentProperty() {
 295         if (alignment == null) {
 296             alignment = new StyleableObjectProperty<Pos>(Pos.TOP_LEFT) {
 297                 @Override
 298                 public void invalidated() {
 299                     requestLayout();
 300                 }
 301 
 302                 @Override
 303                 public Object getBean() {
 304                     return VBox.this;
 305                 }
 306 
 307                 @Override
 308                 public String getName() {
 309                     return "alignment";
 310                 }
 311 
 312                 @Override
 313                 public CssMetaData<VBox, Pos> getCssMetaData() {
 314                     return StyleableProperties.ALIGNMENT;
 315                 }
 316             };
 317         }
 318         return alignment;
 319     }
 320 
 321     private ObjectProperty<Pos> alignment;
 322     public final void setAlignment(Pos value) { alignmentProperty().set(value); }
 323     public final Pos getAlignment() { return alignment == null ? Pos.TOP_LEFT : alignment.get(); }
 324     private Pos getAlignmentInternal() {
 325         Pos localPos = getAlignment();
 326         return localPos == null ? Pos.TOP_LEFT : localPos;
 327     }
 328 
 329     /**
 330      * Whether or not resizable children will be resized to fill the full width of the vbox
 331      * or be resized to their preferred width and aligned according to the <code>alignment</code>
 332      * hpos value.
 333      * @return true if resizable children will be resized to fill the full width
 334      * of the vbox
 335      */
 336     public final BooleanProperty fillWidthProperty() {
 337         if (fillWidth == null) {
 338             fillWidth = new StyleableBooleanProperty(true) {
 339                 @Override
 340                 public void invalidated() {
 341                     requestLayout();
 342                 }
 343 
 344                 @Override
 345                 public Object getBean() {
 346                     return VBox.this;
 347                 }
 348 
 349                 @Override
 350                 public String getName() {
 351                     return "fillWidth";
 352                 }
 353 
 354                 @Override
 355                 public CssMetaData<VBox, Boolean> getCssMetaData() {
 356                     return StyleableProperties.FILL_WIDTH;
 357                 }
 358             };
 359         }
 360         return fillWidth;
 361     }
 362 
 363     private BooleanProperty fillWidth;
 364     public final void setFillWidth(boolean value) { fillWidthProperty().set(value); }
 365     public final boolean isFillWidth() { return fillWidth == null ? true : fillWidth.get(); }
 366 
 367     /**
 368      *
 369      * @return null unless one of its children has a content bias.
 370      */
 371     @Override public Orientation getContentBias() {
 372         if (biasDirty) {
 373             bias = null;
 374             final List<Node> children = getManagedChildren();
 375             for (Node child : children) {
 376                 Orientation contentBias = child.getContentBias();
 377                 if (contentBias != null) {
 378                     bias = contentBias;
 379                     if (contentBias == Orientation.HORIZONTAL) {
 380                         break;
 381                     }
 382                 }
 383             }
 384             biasDirty = false;
 385         }
 386         return bias;
 387     }
 388 
 389     @Override protected double computeMinWidth(double height) {
 390         Insets insets = getInsets();
 391         List<Node>managed = getManagedChildren();
 392         double contentWidth = 0;
 393         if (height != -1 && getContentBias() != null) {
 394             double prefHeights[][] = getAreaHeights(managed, -1, false);
 395             adjustAreaHeights(managed, prefHeights, height, -1);
 396             contentWidth = computeMaxMinAreaWidth(managed, marginAccessor, prefHeights[0], false);
 397         } else {
 398             contentWidth = computeMaxMinAreaWidth(managed, marginAccessor);
 399         }
 400         return snapSpaceX(insets.getLeft()) + contentWidth + snapSpaceX(insets.getRight());
 401     }
 402 
 403     @Override protected double computeMinHeight(double width) {
 404         Insets insets = getInsets();
 405         return snapSpaceY(insets.getTop()) +
 406                computeContentHeight(getManagedChildren(), width, true) +
 407                snapSpaceY(insets.getBottom());
 408     }
 409 
 410     @Override protected double computePrefWidth(double height) {
 411         Insets insets = getInsets();
 412         List<Node>managed = getManagedChildren();
 413         double contentWidth = 0;
 414         if (height != -1 && getContentBias() != null) {
 415             double prefHeights[][] = getAreaHeights(managed, -1, false);
 416             adjustAreaHeights(managed, prefHeights, height, -1);
 417             contentWidth = computeMaxPrefAreaWidth(managed, marginAccessor, prefHeights[0], false);
 418         } else {
 419             contentWidth = computeMaxPrefAreaWidth(managed, marginAccessor);
 420         }
 421         return snapSpaceX(insets.getLeft()) + contentWidth + snapSpaceX(insets.getRight());
 422     }
 423 
 424     @Override protected double computePrefHeight(double width) {
 425         Insets insets = getInsets();
 426         double d = snapSpaceY(insets.getTop()) +
 427                computeContentHeight(getManagedChildren(), width, false) +
 428                snapSpaceY(insets.getBottom());
 429         return d;
 430     }
 431 
 432 
 433     private double[][] getAreaHeights(List<Node>managed, double width, boolean minimum) {
 434         // width could be -1
 435         double[][] temp = getTempArray(managed.size());
 436         final double insideWidth = width == -1? -1 : width -
 437                                      snapSpaceX(getInsets().getLeft()) - snapSpaceX(getInsets().getRight());
 438         final boolean isFillWidth = isFillWidth();
 439         for (int i = 0, size = managed.size(); i < size; i++) {
 440             Node child = managed.get(i);
 441             Insets margin = getMargin(child);
 442             if (minimum) {
 443                 if (insideWidth != -1 && isFillWidth) {
 444                     temp[0][i] = computeChildMinAreaHeight(child, -1, margin, insideWidth);
 445                 } else {
 446                     temp[0][i] = computeChildMinAreaHeight(child, -1, margin, -1);
 447                 }
 448             } else {
 449                 if (insideWidth != -1 && isFillWidth) {
 450                     temp[0][i] = computeChildPrefAreaHeight(child, -1, margin, insideWidth);
 451                 } else {
 452                     temp[0][i] = computeChildPrefAreaHeight(child, -1, margin, -1);
 453                 }
 454             }
 455         }
 456         return temp;
 457     }
 458 
 459     private double adjustAreaHeights(List<Node>managed, double areaHeights[][], double height, double width) {
 460         Insets insets = getInsets();
 461         double left = snapSpaceX(insets.getLeft());
 462         double right = snapSpaceX(insets.getRight());
 463 
 464         double contentHeight = sum(areaHeights[0], managed.size()) + (managed.size()-1)*snapSpaceY(getSpacing());
 465         double extraHeight = height -
 466                 snapSpaceY(insets.getTop()) - snapSpaceY(insets.getBottom()) - contentHeight;
 467 
 468         if (extraHeight != 0) {
 469             final double refWidth = isFillWidth()&& width != -1? width - left - right : -1;
 470             double remaining = growOrShrinkAreaHeights(managed, areaHeights, Priority.ALWAYS, extraHeight, refWidth);
 471             remaining = growOrShrinkAreaHeights(managed, areaHeights, Priority.SOMETIMES, remaining, refWidth);
 472             contentHeight += (extraHeight - remaining);
 473         }
 474 
 475         return contentHeight;
 476     }
 477 
 478     private double growOrShrinkAreaHeights(List<Node>managed, double areaHeights[][], Priority priority, double extraHeight, double width) {
 479         final boolean shrinking = extraHeight < 0;
 480         int adjustingNumber = 0;
 481 
 482         double[] usedHeights = areaHeights[0];
 483         double[] temp = areaHeights[1];
 484 
 485         if (shrinking) {
 486             adjustingNumber = managed.size();
 487             for (int i = 0, size = managed.size(); i < size; i++) {
 488                 final Node child = managed.get(i);
 489                 temp[i] = computeChildMinAreaHeight(child, -1, getMargin(child), width);
 490             }
 491         } else {
 492             for (int i = 0, size = managed.size(); i < size; i++) {
 493             final Node child = managed.get(i);
 494             if (getVgrow(child) == priority) {
 495                 temp[i] = computeChildMaxAreaHeight(child, -1, getMargin(child), width);
 496                 adjustingNumber++;
 497             } else {
 498                 temp[i] = -1;
 499             }
 500         }
 501         }
 502 
 503         double available = extraHeight; // will be negative in shrinking case
 504         outer: while (Math.abs(available) > 1 && adjustingNumber > 0) {
 505             final double portion = snapPortionY(available / adjustingNumber); // negative in shrinking case
 506             for (int i = 0, size = managed.size(); i < size; i++) {
 507                 if (temp[i] == -1) {
 508                     continue;
 509                 }
 510                 final double limit = temp[i] - usedHeights[i]; // negative in shrinking case
 511                 final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion;
 512                 usedHeights[i] += change;
 513                 available -= change;
 514                 if (Math.abs(available) < 1) {
 515                     break outer;
 516                 }
 517                 if (Math.abs(change) < Math.abs(portion)) {
 518                     temp[i] = -1;
 519                     adjustingNumber--;
 520                 }
 521             }
 522         }
 523 
 524         return available; // might be negative in shrinking case
 525     }
 526 
 527     private double computeContentHeight(List<Node> managedChildren, double width, boolean minimum) {
 528         return sum(getAreaHeights(managedChildren, width, minimum)[0], managedChildren.size())
 529                 + (managedChildren.size()-1)*snapSpaceY(getSpacing());
 530     }
 531 
 532     private static double sum(double[] array, int size) {
 533         int i = 0;
 534         double res = 0;
 535         while (i != size) {
 536             res += array[i++];
 537         }
 538         return res;
 539     }
 540 
 541     @Override public void requestLayout() {
 542         biasDirty = true;
 543         bias = null;
 544         super.requestLayout();
 545     }
 546 
 547     @Override protected void layoutChildren() {
 548         List<Node> managed = getManagedChildren();
 549         Insets insets = getInsets();
 550         double width = getWidth();
 551         double height = getHeight();
 552         double top = snapSpaceY(insets.getTop());
 553         double left = snapSpaceX(insets.getLeft());
 554         double bottom = snapSpaceY(insets.getBottom());
 555         double right = snapSpaceX(insets.getRight());
 556         double space = snapSpaceY(getSpacing());
 557         HPos hpos = getAlignmentInternal().getHpos();
 558         VPos vpos = getAlignmentInternal().getVpos();
 559         boolean isFillWidth = isFillWidth();
 560 
 561         double[][] actualAreaHeights = getAreaHeights(managed, width, false);
 562         double contentWidth = width - left - right;
 563         double contentHeight = adjustAreaHeights(managed, actualAreaHeights, height, width);
 564 
 565         double x = left;
 566         double y = top + computeYOffset(height - top - bottom, contentHeight, vpos);
 567 
 568         for (int i = 0, size = managed.size(); i < size; i++) {
 569             Node child = managed.get(i);
 570             layoutInArea(child, x, y, contentWidth, actualAreaHeights[0][i],
 571                        /* baseline shouldn't matter */actualAreaHeights[0][i],
 572                        getMargin(child), isFillWidth, true,
 573                        hpos, vpos);
 574             y += actualAreaHeights[0][i] + space;
 575         }
 576     }
 577 
 578     private double[][] getTempArray(int size) {
 579         if (tempArray == null) {
 580             tempArray = new double[2][size]; // First array for the result, second for temporary computations
 581         } else if (tempArray[0].length < size) {
 582             tempArray = new double[2][Math.max(tempArray.length * 3, size)];
 583         }
 584         return tempArray;
 585 
 586     }
 587 
 588     /***************************************************************************
 589      *                                                                         *
 590      *                         Stylesheet Handling                             *
 591      *                                                                         *
 592      **************************************************************************/
 593 
 594      /*
 595       * Super-lazy instantiation pattern from Bill Pugh.
 596       */
 597      private static class StyleableProperties {
 598          private static final CssMetaData<VBox,Pos> ALIGNMENT =
 599              new CssMetaData<VBox,Pos>("-fx-alignment",
 600                  new EnumConverter<Pos>(Pos.class), Pos.TOP_LEFT){
 601 
 602             @Override
 603             public boolean isSettable(VBox node) {
 604                 return node.alignment == null || !node.alignment.isBound();
 605             }
 606 
 607             @Override
 608             public StyleableProperty<Pos> getStyleableProperty(VBox node) {
 609                 return (StyleableProperty<Pos>)node.alignmentProperty();
 610             }
 611         };
 612 
 613          private static final CssMetaData<VBox,Boolean> FILL_WIDTH =
 614              new CssMetaData<VBox,Boolean>("-fx-fill-width",
 615                  BooleanConverter.getInstance(), Boolean.TRUE) {
 616 
 617             @Override
 618             public boolean isSettable(VBox node) {
 619                 return node.fillWidth == null || !node.fillWidth.isBound();
 620             }
 621 
 622             @Override
 623             public StyleableProperty<Boolean> getStyleableProperty(VBox node) {
 624                 return (StyleableProperty<Boolean>)node.fillWidthProperty();
 625             }
 626         };
 627 
 628          private static final CssMetaData<VBox,Number> SPACING =
 629              new CssMetaData<VBox,Number>("-fx-spacing",
 630                  SizeConverter.getInstance(), 0d) {
 631 
 632             @Override
 633             public boolean isSettable(VBox node) {
 634                 return node.spacing == null || !node.spacing.isBound();
 635             }
 636 
 637             @Override
 638             public StyleableProperty<Number> getStyleableProperty(VBox node) {
 639                 return (StyleableProperty<Number>)node.spacingProperty();
 640             }
 641         };
 642 
 643          private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 644          static {
 645             final List<CssMetaData<? extends Styleable, ?>> styleables =
 646                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Region.getClassCssMetaData());
 647             styleables.add(ALIGNMENT);
 648             styleables.add(FILL_WIDTH);
 649             styleables.add(SPACING);
 650             STYLEABLES = Collections.unmodifiableList(styleables);
 651          }
 652     }
 653 
 654     /**
 655      * @return The CssMetaData associated with this class, which may include the
 656      * CssMetaData of its superclasses.
 657      * @since JavaFX 8.0
 658      */
 659     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 660         return StyleableProperties.STYLEABLES;
 661     }
 662 
 663     /**
 664      * {@inheritDoc}
 665      *
 666      * @since JavaFX 8.0
 667      */
 668 
 669 
 670     @Override
 671     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
 672         return getClassCssMetaData();
 673     }
 674 
 675 }