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