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 laid 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 children's 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 }