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.Insets;
  40 import javafx.geometry.Orientation;
  41 import javafx.geometry.Pos;
  42 import javafx.geometry.VPos;
  43 import javafx.scene.Node;
  44 import javafx.css.converter.BooleanConverter;
  45 import javafx.css.converter.EnumConverter;
  46 import javafx.css.converter.SizeConverter;
  47 import javafx.css.Styleable;
  48 import javafx.geometry.HPos;
  49 import javafx.util.Callback;
  50 
  51 
  52 
  53 /**
  54  * HBox lays out its children in a single horizontal row.
  55  * If the hbox has a border and/or padding set, then the contents will be layed
  56  * out within those insets.
  57  * <p>
  58  * HBox example:
  59  * <pre><code>
  60  *     HBox hbox = new HBox(8); // spacing = 8
  61  *     hbox.getChildren().addAll(new Label("Name:), new TextBox());
  62  * </code></pre>
  63  *
  64  * HBox will resize children (if resizable) to their preferred widths and uses its
  65  * fillHeight property to determine whether to resize their heights to
  66  * fill its own height or keep their heights to their preferred (fillHeight defaults to true).
  67  * The alignment of the content is controlled by the alignment property,
  68  * which defaults to Pos.TOP_LEFT.
  69  * <p>
  70  * If an hbox is resized larger than its preferred width, by default it will keep
  71  * children to their preferred widths, leaving the extra space unused.  If an
  72  * application wishes to have one or more children be allocated that extra space
  73  * it may optionally set an hgrow constraint on the child.  See "Optional Layout
  74  * Constraints" for details.
  75  * <p>
  76  * HBox lays out each managed child regardless of the child's
  77  * visible property value; unmanaged children are ignored.</p>
  78  *
  79  * <h4>Resizable Range</h4>
  80  *
  81  * An hbox's parent will resize the hbox within the hbox's resizable range
  82  * during layout.   By default the hbox computes this range based on its content
  83  * as outlined in the table below.
  84  * <table border="1">
  85  * <tr><td></td><th>width</th><th>height</th></tr>
  86  * <tr><th>minimum</th>
  87  * <td>left/right insets plus the sum of each child's min width plus spacing between each child.</td>
  88  * <td>top/bottom insets plus the largest of the children's min heights.</td></tr>
  89  * <tr><th>preferred</th>
  90  * <td>left/right insets plus the sum of each child's pref width plus spacing between each child.</td>
  91  * <td>top/bottom insets plus the largest of the children's pref heights.</td></tr>
  92  * <tr><th>maximum</th>
  93  * <td>Double.MAX_VALUE</td><td>Double.MAX_VALUE</td></tr>
  94  * </table>
  95  * <p>
  96  * An hbox'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  * HBox 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>hbox.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  * HBox 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 hbox.</p>
 112  *
 113  * <h4>Optional Layout Constraints</h4>
 114  *
 115  * An application may set constraints on individual children to customize HBox's layout.
 116  * For each constraint, HBox 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>hgrow</td><td>javafx.scene.layout.Priority</td><td>The horizontal 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 an hbox needs the TextField to be allocated all extra space:
 125  * <pre><code>
 126  *     HBox hbox = new HBox();
 127  *     TextField field = new TextField();
 128  *     <b>HBox.setHgrow(field, Priority.ALWAYS);</b>
 129  *     hbox.getChildren().addAll(new Label("Search:"), field, new Button("Go"));
 130  * </code></pre>
 131  *
 132  * If more than one child has the same grow priority set, then the hbox will
 133  * allocate equal amounts of space to each.  HBox will only grow a child up to
 134  * its maximum width, so if the child has a max width other than Double.MAX_VALUE,
 135  * the application may need to override the max to allow it to grow.
 136  * For example:
 137  * <pre><code>
 138  *     HBox hbox = new HBox();
 139  *     Button button1 = new Button("Add");
 140  *     Button button2 = new Button("Remove");
 141  *     <b>HBox.setHgrow(button1, Priority.ALWAYS);
 142  *     HBox.setHgrow(button2, Priority.ALWAYS);
 143  *     button1.setMaxWidth(Double.MAX_VALUE);
 144  *     button2.setMaxWidth(Double.MAX_VALUE);</b>
 145  *     hbox.getChildren().addAll(button1, button2);
 146  * </code></pre>
 147  * @since JavaFX 2.0
 148  */
 149 public class HBox extends Pane {
 150 
 151     private boolean biasDirty = true;
 152     private boolean performingLayout = false;
 153     private double minBaselineComplement = Double.NaN;
 154     private double prefBaselineComplement = Double.NaN;
 155     private Orientation bias;
 156     private double[][] tempArray;
 157 
 158     /********************************************************************
 159      *  BEGIN static methods
 160      ********************************************************************/
 161     private static final String MARGIN_CONSTRAINT = "hbox-margin";
 162     private static final String HGROW_CONSTRAINT = "hbox-hgrow";
 163 
 164     /**
 165      * Sets the horizontal grow priority for the child when contained by an hbox.
 166      * If set, the hbox will use the priority to allocate additional space if the
 167      * hbox is resized larger than it's preferred width.
 168      * If multiple hbox children have the same horizontal grow priority, then the
 169      * extra space will be split evening between them.
 170      * If no horizontal grow priority is set on a child, the hbox will never
 171      * allocate it additional horizontal space if available.
 172      * Setting the value to null will remove the constraint.
 173      * @param child the child of an hbox
 174      * @param value the horizontal grow priority for the child
 175      */
 176     public static void setHgrow(Node child, Priority value) {
 177         setConstraint(child, HGROW_CONSTRAINT, value);
 178     }
 179 
 180     /**
 181      * Returns the child's hgrow constraint if set.
 182      * @param child the child node of an hbox
 183      * @return the horizontal grow priority for the child or null if no priority was set
 184      */
 185     public static Priority getHgrow(Node child) {
 186         return (Priority)getConstraint(child, HGROW_CONSTRAINT);
 187     }
 188 
 189     /**
 190      * Sets the margin for the child when contained by an hbox.
 191      * If set, the hbox will layout the child with the margin space around it.
 192      * Setting the value to null will remove the constraint.
 193      * @param child the child mode of the hbox
 194      * @param value the margin of space around the child
 195      */
 196     public static void setMargin(Node child, Insets value) {
 197         setConstraint(child, MARGIN_CONSTRAINT, value);
 198     }
 199 
 200     /**
 201      * Returns the child's margin constraint if set.
 202      * @param child the child node of an hbox
 203      * @return the margin for the child or null if no margin was set
 204      */
 205     public static Insets getMargin(Node child) {
 206         return (Insets)getConstraint(child, MARGIN_CONSTRAINT);
 207     }
 208 
 209     private static final Callback<Node, Insets> marginAccessor = n -> getMargin(n);
 210 
 211     /**
 212      * Removes all hbox constraints from the child node.
 213      * @param child the child node
 214      */
 215     public static void clearConstraints(Node child) {
 216         setHgrow(child, null);
 217         setMargin(child, null);
 218     }
 219 
 220     /********************************************************************
 221      *  END static methods
 222      ********************************************************************/
 223 
 224     /**
 225      * Creates an HBox layout with spacing = 0.
 226      */
 227     public HBox() {
 228         super();
 229     }
 230 
 231     /**
 232      * Creates an HBox layout with the specified spacing between children.
 233      * @param spacing the amount of horizontal space between each child
 234      */
 235     public HBox(double spacing) {
 236         this();
 237         setSpacing(spacing);
 238     }
 239 
 240     /**
 241      * Creates an HBox layout with spacing = 0.
 242      * @param children The initial set of children for this pane.
 243      * @since JavaFX 8.0
 244      */
 245     public HBox(Node... children) {
 246         super();
 247         getChildren().addAll(children);
 248     }
 249 
 250     /**
 251      * Creates an HBox layout with the specified spacing between children.
 252      * @param spacing the amount of horizontal space between each child
 253      * @param children The initial set of children for this pane.
 254      * @since JavaFX 8.0
 255      */
 256     public HBox(double spacing, Node... children) {
 257         this();
 258         setSpacing(spacing);
 259         getChildren().addAll(children);
 260     }
 261 
 262     /**
 263      * The amount of horizontal space between each child in the hbox.
 264      */
 265     public final DoubleProperty spacingProperty() {
 266         if (spacing == null) {
 267             spacing = new StyleableDoubleProperty() {
 268                 @Override
 269                 public void invalidated() {
 270                     requestLayout();
 271                 }
 272 
 273                 @Override
 274                 public CssMetaData getCssMetaData () {
 275                     return StyleableProperties.SPACING;
 276                 }
 277 
 278                 @Override
 279                 public Object getBean() {
 280                     return HBox.this;
 281                 }
 282 
 283                 @Override
 284                 public String getName() {
 285                     return "spacing";
 286                 }
 287             };
 288         }
 289         return spacing;
 290     }
 291 
 292     private DoubleProperty spacing;
 293     public final void setSpacing(double value) { spacingProperty().set(value); }
 294     public final double getSpacing() { return spacing == null ? 0 : spacing.get(); }
 295 
 296     /**
 297      * The overall alignment of children within the hbox's width and height.
 298      */
 299     public final ObjectProperty<Pos> alignmentProperty() {
 300         if (alignment == null) {
 301             alignment = new StyleableObjectProperty<Pos>(Pos.TOP_LEFT) {
 302                 @Override
 303                 public void invalidated() {
 304                     requestLayout();
 305                 }
 306 
 307                 @Override
 308                 public CssMetaData<HBox, Pos> getCssMetaData() {
 309                     return StyleableProperties.ALIGNMENT;
 310                 }
 311 
 312                 @Override
 313                 public Object getBean() {
 314                     return HBox.this;
 315                 }
 316 
 317                 @Override
 318                 public String getName() {
 319                     return "alignment";
 320                 }
 321             };
 322         }
 323         return alignment;
 324     }
 325 
 326     private ObjectProperty<Pos> alignment;
 327     public final void setAlignment(Pos value) { alignmentProperty().set(value); }
 328     public final Pos getAlignment() { return alignment == null ? Pos.TOP_LEFT : alignment.get(); }
 329     private Pos getAlignmentInternal() {
 330         Pos localPos = getAlignment();
 331         return localPos == null ? Pos.TOP_LEFT : localPos;
 332     }
 333 
 334     /**
 335      * Whether or not resizable children will be resized to fill the full height of the hbox
 336      * or be resized to their preferred height and aligned according to the <code>alignment</code>
 337      * vpos value.   Note that if the hbox vertical alignment is set to BASELINE, then this
 338      * property will be ignored and children will be resized to their preferred heights.
 339      */
 340     public final BooleanProperty fillHeightProperty() {
 341         if (fillHeight == null) {
 342             fillHeight = new StyleableBooleanProperty(true) {
 343                 @Override
 344                 public void invalidated() {
 345                     requestLayout();
 346                 }
 347 
 348                 @Override
 349                 public CssMetaData<HBox, Boolean> getCssMetaData() {
 350                     return StyleableProperties.FILL_HEIGHT;
 351                 }
 352 
 353                 @Override
 354                 public Object getBean() {
 355                     return HBox.this;
 356                 }
 357 
 358                 @Override
 359                 public String getName() {
 360                     return "fillHeight";
 361                 }
 362             };
 363         }
 364         return fillHeight;
 365     }
 366 
 367     private BooleanProperty fillHeight;
 368     public final void setFillHeight(boolean value) { fillHeightProperty().set(value); }
 369     public final boolean isFillHeight() { return fillHeight == null ? true : fillHeight.get(); }
 370 
 371     private boolean shouldFillHeight() {
 372         return isFillHeight() && getAlignmentInternal().getVpos() != VPos.BASELINE;
 373     }
 374 
 375     /**
 376      *
 377      * @return null unless one of its children has a content bias.
 378      */
 379     @Override public Orientation getContentBias() {
 380         if (biasDirty) {
 381             bias = null;
 382             final List<Node> children = getManagedChildren();
 383             for (Node child : children) {
 384                 Orientation contentBias = child.getContentBias();
 385                 if (contentBias != null) {
 386                     bias = contentBias;
 387                     if (contentBias == Orientation.HORIZONTAL) {
 388                         break;
 389                     }
 390                 }
 391             }
 392             biasDirty = false;
 393         }
 394         return bias;
 395     }
 396 
 397     @Override protected double computeMinWidth(double height) {
 398         Insets insets = getInsets();
 399         return snapSpaceX(insets.getLeft()) +
 400                computeContentWidth(getManagedChildren(), height, true) +
 401                snapSpaceX(insets.getRight());
 402     }
 403 
 404     @Override protected double computeMinHeight(double width) {
 405         Insets insets = getInsets();
 406         List<Node>managed = getManagedChildren();
 407         double contentHeight = 0;
 408         if (width != -1 && getContentBias() != null) {
 409             double prefWidths[][] = getAreaWidths(managed, -1, false);
 410             adjustAreaWidths(managed, prefWidths, width, -1);
 411             contentHeight = computeMaxMinAreaHeight(managed, marginAccessor, prefWidths[0], getAlignmentInternal().getVpos());
 412         } else {
 413             contentHeight = computeMaxMinAreaHeight(managed, marginAccessor, getAlignmentInternal().getVpos());
 414         }
 415         return snapSpaceY(insets.getTop()) +
 416                contentHeight +
 417                snapSpaceY(insets.getBottom());
 418     }
 419 
 420     @Override protected double computePrefWidth(double height) {
 421         Insets insets = getInsets();
 422         return snapSpaceX(insets.getLeft()) +
 423                computeContentWidth(getManagedChildren(), height, false) +
 424                snapSpaceX(insets.getRight());
 425     }
 426 
 427     @Override protected double computePrefHeight(double width) {
 428         Insets insets = getInsets();
 429         List<Node>managed = getManagedChildren();
 430         double contentHeight = 0;
 431         if (width != -1 && getContentBias() != null) {
 432             double prefWidths[][] = getAreaWidths(managed, -1, false);
 433             adjustAreaWidths(managed, prefWidths, width, -1);
 434             contentHeight = computeMaxPrefAreaHeight(managed, marginAccessor, prefWidths[0], getAlignmentInternal().getVpos());
 435         } else {
 436             contentHeight = computeMaxPrefAreaHeight(managed, marginAccessor, getAlignmentInternal().getVpos());
 437         }
 438         return snapSpaceY(insets.getTop()) +
 439                contentHeight +
 440                snapSpaceY(insets.getBottom());
 441     }
 442 
 443     private double[][] getAreaWidths(List<Node>managed, double height, boolean minimum) {
 444         // height could be -1
 445         double[][] temp = getTempArray(managed.size());
 446         final double insideHeight = height == -1? -1 : height -
 447                                      snapSpaceY(getInsets().getTop()) - snapSpaceY(getInsets().getBottom());
 448         final boolean shouldFillHeight = shouldFillHeight();
 449         for (int i = 0, size = managed.size(); i < size; i++) {
 450             Node child = managed.get(i);
 451             Insets margin = getMargin(child);
 452             if (minimum) {
 453                 temp[0][i] = computeChildMinAreaWidth(child, getMinBaselineComplement(), margin, insideHeight, shouldFillHeight);
 454             } else {
 455                 temp[0][i] = computeChildPrefAreaWidth(child, getPrefBaselineComplement(), margin, insideHeight, shouldFillHeight);
 456             }
 457         }
 458         return temp;
 459     }
 460 
 461     private double adjustAreaWidths(List<Node>managed, double areaWidths[][], double width, double height) {
 462         Insets insets = getInsets();
 463         double top = snapSpaceY(insets.getTop());
 464         double bottom = snapSpaceY(insets.getBottom());
 465 
 466         double contentWidth = sum(areaWidths[0], managed.size()) + (managed.size()-1)*snapSpaceX(getSpacing());
 467         double extraWidth = width -
 468                 snapSpaceX(insets.getLeft()) - snapSpaceX(insets.getRight()) - contentWidth;
 469 
 470         if (extraWidth != 0) {
 471             final double refHeight = shouldFillHeight() && height != -1? height - top - bottom : -1;
 472             double remaining = growOrShrinkAreaWidths(managed, areaWidths, Priority.ALWAYS, extraWidth, refHeight);
 473             remaining = growOrShrinkAreaWidths(managed, areaWidths, Priority.SOMETIMES, remaining, refHeight);
 474             contentWidth += (extraWidth - remaining);
 475         }
 476         return contentWidth;
 477     }
 478 
 479     private double growOrShrinkAreaWidths(List<Node>managed, double areaWidths[][], Priority priority, double extraWidth, double height) {
 480         final boolean shrinking = extraWidth < 0;
 481         int adjustingNumber = 0;
 482 
 483         double[] usedWidths = areaWidths[0];
 484         double[] temp = areaWidths[1];
 485         final boolean shouldFillHeight = shouldFillHeight();
 486 
 487         if (shrinking) {
 488             adjustingNumber = managed.size();
 489             for (int i = 0, size = managed.size(); i < size; i++) {
 490                 final Node child = managed.get(i);
 491                 temp[i] = computeChildMinAreaWidth(child, getMinBaselineComplement(), getMargin(child), height, shouldFillHeight);
 492             }
 493         } else {
 494             for (int i = 0, size = managed.size(); i < size; i++) {
 495                 final Node child = managed.get(i);
 496                 if (getHgrow(child) == priority) {
 497                     temp[i] = computeChildMaxAreaWidth(child, getMinBaselineComplement(), getMargin(child), height, shouldFillHeight);
 498                     adjustingNumber++;
 499                 } else {
 500                     temp[i] = -1;
 501                 }
 502             }
 503         }
 504 
 505         double available = extraWidth; // will be negative in shrinking case
 506         outer:while (Math.abs(available) > 1 && adjustingNumber > 0) {
 507             final double portion = snapPortionX(available / adjustingNumber); // negative in shrinking case
 508             for (int i = 0, size = managed.size(); i < size; i++) {
 509                 if (temp[i] == -1) {
 510                     continue;
 511                 }
 512                 final double limit = temp[i] - usedWidths[i]; // negative in shrinking case
 513                 final double change = Math.abs(limit) <= Math.abs(portion)? limit : portion;
 514                 usedWidths[i] += change;
 515                 available -= change;
 516                 if (Math.abs(available) < 1) {
 517                     break outer;
 518                 }
 519                 if (Math.abs(change) < Math.abs(portion)) {
 520                     temp[i] = -1;
 521                     adjustingNumber--;
 522                 }
 523             }
 524         }
 525 
 526         return available; // might be negative in shrinking case
 527     }
 528 
 529     private double computeContentWidth(List<Node> managedChildren, double height, boolean minimum) {
 530         return sum(getAreaWidths(managedChildren, height, minimum)[0], managedChildren.size())
 531                 + (managedChildren.size()-1)*snapSpaceX(getSpacing());
 532     }
 533 
 534     private static double sum(double[] array, int size) {
 535         int i = 0;
 536         double res = 0;
 537         while (i != size) {
 538             res += array[i++];
 539         }
 540         return res;
 541     }
 542 
 543     @Override public void requestLayout() {
 544         if (performingLayout) {
 545             return;
 546         }
 547         biasDirty = true;
 548         bias = null;
 549         minBaselineComplement = Double.NaN;
 550         prefBaselineComplement = Double.NaN;
 551         baselineOffset = Double.NaN;
 552         super.requestLayout();
 553     }
 554 
 555     private double getMinBaselineComplement() {
 556         if (Double.isNaN(minBaselineComplement)) {
 557             if (getAlignmentInternal().getVpos() == VPos.BASELINE) {
 558                 minBaselineComplement = getMinBaselineComplement(getManagedChildren());
 559             } else {
 560                 minBaselineComplement = -1;
 561             }
 562         }
 563         return minBaselineComplement;
 564     }
 565 
 566     private double getPrefBaselineComplement() {
 567         if (Double.isNaN(prefBaselineComplement)) {
 568             if (getAlignmentInternal().getVpos() == VPos.BASELINE) {
 569                 prefBaselineComplement = getPrefBaselineComplement(getManagedChildren());
 570             } else {
 571                 prefBaselineComplement = -1;
 572             }
 573         }
 574         return prefBaselineComplement;
 575     }
 576 
 577     private double baselineOffset = Double.NaN;
 578 
 579     @Override
 580     public double getBaselineOffset() {
 581         List<Node> managed = getManagedChildren();
 582         if (managed.isEmpty()) {
 583             return BASELINE_OFFSET_SAME_AS_HEIGHT;
 584         }
 585         if (Double.isNaN(baselineOffset)) {
 586             VPos vpos = getAlignmentInternal().getVpos();
 587             if (vpos == VPos.BASELINE) {
 588                 double max = 0;
 589                 for (int i =0, sz = managed.size(); i < sz; ++i) {
 590                     final Node child = managed.get(i);
 591                     double offset = child.getBaselineOffset();
 592                     if (offset == BASELINE_OFFSET_SAME_AS_HEIGHT) {
 593                         baselineOffset = BASELINE_OFFSET_SAME_AS_HEIGHT;
 594                         break;
 595                     } else {
 596                         Insets margin = getMargin(child);
 597                         double top = margin != null ? margin.getTop() : 0;
 598                         max = Math.max(max, top + child.getLayoutBounds().getMinY() + offset);
 599                     }
 600                 }
 601                 baselineOffset = max + snappedTopInset();
 602             } else {
 603                 baselineOffset = BASELINE_OFFSET_SAME_AS_HEIGHT;
 604             }
 605         }
 606         return baselineOffset;
 607     }
 608 
 609     @Override protected void layoutChildren() {
 610         performingLayout = true;
 611         List<Node> managed = getManagedChildren();
 612         Insets insets = getInsets();
 613         Pos align = getAlignmentInternal();
 614         HPos alignHpos = align.getHpos();
 615         VPos alignVpos = align.getVpos();
 616         double width = getWidth();
 617         double height = getHeight();
 618         double top = snapSpaceY(insets.getTop());
 619         double left = snapSpaceX(insets.getLeft());
 620         double bottom = snapSpaceY(insets.getBottom());
 621         double right = snapSpaceX(insets.getRight());
 622         double space = snapSpaceX(getSpacing());
 623         boolean shouldFillHeight = shouldFillHeight();
 624 
 625         final double[][] actualAreaWidths = getAreaWidths(managed, height, false);
 626         double contentWidth = adjustAreaWidths(managed, actualAreaWidths, width, height);
 627         double contentHeight = height - top - bottom;
 628 
 629         double x = left + computeXOffset(width - left - right, contentWidth, align.getHpos());
 630         double y = top;
 631         double baselineOffset = -1;
 632         if (alignVpos == VPos.BASELINE) {
 633             double baselineComplement = getMinBaselineComplement();
 634             baselineOffset = getAreaBaselineOffset(managed, marginAccessor, i -> actualAreaWidths[0][i],
 635                     contentHeight, shouldFillHeight, baselineComplement);
 636         }
 637 
 638         for (int i = 0, size = managed.size(); i < size; i++) {
 639             Node child = managed.get(i);
 640             Insets margin = getMargin(child);
 641             layoutInArea(child, x, y, actualAreaWidths[0][i], contentHeight,
 642                     baselineOffset, margin, true, shouldFillHeight,
 643                     alignHpos, alignVpos);
 644             x += actualAreaWidths[0][i] + space;
 645         }
 646         performingLayout = false;
 647     }
 648 
 649     private double[][] getTempArray(int size) {
 650         if (tempArray == null) {
 651             tempArray = new double[2][size]; // First array for the result, second for temporary computations
 652         } else if (tempArray[0].length < size) {
 653             tempArray = new double[2][Math.max(tempArray.length * 3, size)];
 654         }
 655         return tempArray;
 656 
 657     }
 658 
 659     /***************************************************************************
 660      *                                                                         *
 661      *                         Stylesheet Handling                             *
 662      *                                                                         *
 663      **************************************************************************/
 664 
 665      /**
 666       * Super-lazy instantiation pattern from Bill Pugh.
 667       * @treatAsPrivate implementation detail
 668       */
 669      private static class StyleableProperties {
 670 
 671          private static final CssMetaData<HBox,Pos> ALIGNMENT =
 672              new CssMetaData<HBox,Pos>("-fx-alignment",
 673                  new EnumConverter<Pos>(Pos.class),
 674                  Pos.TOP_LEFT) {
 675 
 676             @Override
 677             public boolean isSettable(HBox node) {
 678                 return node.alignment == null || !node.alignment.isBound();
 679             }
 680 
 681             @Override
 682             public StyleableProperty<Pos> getStyleableProperty(HBox node) {
 683                 return (StyleableProperty<Pos>)node.alignmentProperty();
 684             }
 685 
 686          };
 687 
 688          private static final CssMetaData<HBox,Boolean> FILL_HEIGHT =
 689              new CssMetaData<HBox,Boolean>("-fx-fill-height",
 690                  BooleanConverter.getInstance(), Boolean.TRUE) {
 691 
 692             @Override
 693             public boolean isSettable(HBox node) {
 694                 return node.fillHeight == null ||
 695                         !node.fillHeight.isBound();
 696             }
 697 
 698             @Override
 699             public StyleableProperty<Boolean> getStyleableProperty(HBox node) {
 700                 return (StyleableProperty<Boolean>)node.fillHeightProperty();
 701             }
 702 
 703          };
 704 
 705          private static final CssMetaData<HBox,Number> SPACING =
 706              new CssMetaData<HBox,Number>("-fx-spacing",
 707                  SizeConverter.getInstance(), 0.0){
 708 
 709             @Override
 710             public boolean isSettable(HBox node) {
 711                 return node.spacing == null || !node.spacing.isBound();
 712             }
 713 
 714             @Override
 715             public StyleableProperty<Number> getStyleableProperty(HBox node) {
 716                 return (StyleableProperty<Number>)node.spacingProperty();
 717             }
 718 
 719          };
 720 
 721          private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
 722          static {
 723             final List<CssMetaData<? extends Styleable, ?>> styleables =
 724                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Pane.getClassCssMetaData());
 725             styleables.add(FILL_HEIGHT);
 726             styleables.add(ALIGNMENT);
 727             styleables.add(SPACING);
 728             STYLEABLES = Collections.unmodifiableList(styleables);
 729          }
 730     }
 731 
 732     /**
 733      * @return The CssMetaData associated with this class, which may include the
 734      * CssMetaData of its super classes.
 735      * @since JavaFX 8.0
 736      */
 737     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
 738         return StyleableProperties.STYLEABLES;
 739     }
 740 
 741     /**
 742      * {@inheritDoc}
 743      *
 744      * @since JavaFX 8.0
 745      */
 746 
 747 
 748     @Override
 749     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
 750         return getClassCssMetaData();
 751     }
 752 
 753 }