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