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 com.sun.javafx.geom.Vec2d; 29 import java.util.List; 30 import javafx.beans.property.ObjectProperty; 31 import javafx.beans.property.ObjectPropertyBase; 32 import javafx.collections.ListChangeListener; 33 import javafx.geometry.HPos; 34 import javafx.geometry.Insets; 35 import javafx.geometry.Orientation; 36 import javafx.geometry.Pos; 37 import javafx.geometry.VPos; 38 import javafx.scene.Node; 39 import static javafx.scene.layout.Region.positionInArea; 40 41 42 /** 43 * BorderPane lays out children in top, left, right, bottom, and center positions. 44 * 45 * <p> <img src="doc-files/borderpane.png" alt="A diagram that shows the position 46 * of each child"> </p> 47 * 48 * The top and bottom children will be resized to their preferred heights and 49 * extend the width of the border pane. The left and right children will be resized 50 * to their preferred widths and extend the length between the top and bottom nodes. 51 * And the center node will be resized to fill the available space in the middle. 52 * Any of the positions may be null. 53 * 54 * Example: 55 * <pre><code> <b>BorderPane borderPane = new BorderPane();</b> 56 * ToolBar toolbar = new ToolBar(); 57 * HBox statusbar = new HBox(); 58 * Node appContent = new AppContentNode(); 59 * <b>borderPane.setTop(toolbar); 60 * borderPane.setCenter(appContent); 61 * borderPane.setBottom(statusbar);</b> 62 * </code></pre> 63 * <p> 64 * Borderpanes may be styled with backgrounds and borders using CSS. See 65 * {@link javafx.scene.layout.Region Region} superclass for details.</p> 66 * 67 * <p> 68 * BorderPane honors the minimum, preferred, and maximum sizes of its children. 69 * If the child's resizable range prevents it from be resized to fit within its 70 * position, it will be aligned relative to the space using a default alignment 71 * as follows: 72 * <ul> 73 * <li>top: Pos.TOP_LEFT</li> 74 * <li>bottom: Pos.BOTTOM_LEFT</li> 75 * <li>left: Pos.TOP_LEFT</li> 76 * <li>right: Pos.TOP_RIGHT</li> 77 * <li>center: Pos.CENTER</li> 78 * </ul> 79 * See "Optional Layout Constraints" on how to customize these alignments. 80 * 81 * <p> 82 * BorderPane lays out each child set in the five positions regardless of the child's 83 * visible property value; unmanaged children are ignored.</p> 84 * 85 * <h3>Resizable Range</h3> 86 * <p> 87 * BorderPane is commonly used as the root of a {@link javafx.scene.Scene Scene}, 88 * in which case its size will track the size of the scene. If the scene or stage 89 * size has not been directly set by the application, the scene size will be 90 * initialized to the border pane's preferred size. However, if a border pane 91 * has a parent other than the scene, that parent will resize the border pane within 92 * the border pane's resizable range during layout. By default the border pane 93 * computes this range based on its content as outlined in the table below. 94 * </p> 95 * 96 * <table border="1"> 97 * <caption>BorderPane Resize Table</caption> 98 * <tr><td></td><th scope="col">width</th><th scope="col">height</th></tr> 99 * <tr><th scope="row">minimum</th> 100 * <td>left/right insets plus width required to display right/left children at their pref widths and top/bottom/center with at least their min widths</td> 101 * <td>top/bottom insets plus height required to display top/bottom children at their pref heights and left/right/center with at least their min heights</td></tr> 102 * <tr><th scope="row">preferred</th> 103 * <td>left/right insets plus width required to display top/right/bottom/left/center children with at least their pref widths</td> 104 * <td>top/bottom insets plus height required to display top/right/bottom/left/center children with at least their pref heights</td></tr> 105 * <tr><th scope="row">maximum</th> 106 * <td>Double.MAX_VALUE</td><td>Double.MAX_VALUE</td></tr> 107 * </table> 108 * <p> 109 * A border pane's unbounded maximum width and height are an indication to the parent that 110 * it may be resized beyond its preferred size to fill whatever space is assigned to it. 111 * <p> 112 * BorderPane provides properties for setting the size range directly. These 113 * properties default to the sentinel value Region.USE_COMPUTED_SIZE, however the 114 * application may set them to other values as needed: 115 * <pre><code> 116 * <b>borderPane.setPrefSize(500,400);</b> 117 * </code></pre> 118 * Applications may restore the computed values by setting these properties back 119 * to Region.USE_COMPUTED_SIZE. 120 * <p> 121 * BorderPane does not clip its content by default, so it is possible that children's 122 * bounds may extend outside its own bounds if a child's min size prevents it from 123 * being fit within it space.</p> 124 * 125 * <h4>Optional Layout Constraints</h4> 126 * 127 * <p> 128 * An application may set constraints on individual children to customize BorderPane's layout. 129 * For each constraint, BorderPane provides a static method for setting it on the child. 130 * </p> 131 * 132 * <table border="1"> 133 * <caption>BorderPane Constraint Table</caption> 134 * <tr><th scope="col">Constraint</th><th scope="col">Type</th><th scope="col">Description</th></tr> 135 * <tr><th scope="row">alignment</th><td>javafx.geometry.Pos</td><td>The alignment of the child within its area of the border pane.</td></tr> 136 * <tr><th scope="row">margin</th><td>javafx.geometry.Insets</td><td>Margin space around the outside of the child.</td></tr> 137 * </table> 138 * <p> 139 * Example: 140 * <pre><code> ListView list = new ListView(); 141 * <b>BorderPane.setAlignment(list, Pos.TOP_LEFT); 142 * BorderPane.setMargin(list, new Insets(12,12,12,12));</b> 143 * borderPane.setCenter(list); 144 * </code></pre> 145 * 146 * @since JavaFX 2.0 147 */ 148 public class BorderPane extends Pane { 149 /******************************************************************** 150 * BEGIN static methods 151 ********************************************************************/ 152 153 private static final String MARGIN = "borderpane-margin"; 154 private static final String ALIGNMENT = "borderpane-alignment"; 155 156 /** 157 * Sets the alignment for the child when contained by a border pane. 158 * If set, will override the border pane's default alignment for the child's position. 159 * Setting the value to null will remove the constraint. 160 * @param child the child node of a border pane 161 * @param value the alignment position for the child 162 */ 163 public static void setAlignment(Node child, Pos value) { 164 setConstraint(child, ALIGNMENT, value); 165 } 166 167 /** 168 * Returns the child's alignment constraint if set. 169 * @param child the child node of a border pane 170 * @return the alignment position for the child or null if no alignment was set 171 */ 172 public static Pos getAlignment(Node child) { 173 return (Pos)getConstraint(child, ALIGNMENT); 174 } 175 176 /** 177 * Sets the margin for the child when contained by a border pane. 178 * If set, the border pane will lay it out with the margin space around it. 179 * Setting the value to null will remove the constraint. 180 * @param child the child node of a border pane 181 * @param value the margin of space around the child 182 */ 183 public static void setMargin(Node child, Insets value) { 184 setConstraint(child, MARGIN, value); 185 } 186 187 /** 188 * Returns the child's margin constraint if set. 189 * @param child the child node of a border pane 190 * @return the margin for the child or null if no margin was set 191 */ 192 public static Insets getMargin(Node child) { 193 return (Insets)getConstraint(child, MARGIN); 194 } 195 196 // convenience for handling null margins 197 private static Insets getNodeMargin(Node child) { 198 Insets margin = getMargin(child); 199 return margin != null ? margin : Insets.EMPTY; 200 } 201 202 /** 203 * Removes all border pane constraints from the child node. 204 * @param child the child node 205 */ 206 public static void clearConstraints(Node child) { 207 setAlignment(child, null); 208 setMargin(child, null); 209 } 210 211 /******************************************************************** 212 * END static methods 213 ********************************************************************/ 214 215 /** 216 * Creates a BorderPane layout. 217 */ 218 public BorderPane() { 219 super(); 220 } 221 222 /** 223 * Creates an BorderPane layout with the given Node as the center of the BorderPane. 224 * @param center The node to set as the center of the BorderPane. 225 * @since JavaFX 8.0 226 */ 227 public BorderPane(Node center) { 228 super(); 229 setCenter(center); 230 } 231 232 /** 233 * Creates an BorderPane layout with the given Nodes to use for each of the main 234 * layout areas of the Border Pane. The top, right, bottom, and left nodes are listed 235 * in clockwise order. 236 * @param center The node to set as the center of the BorderPane. 237 * @param top The node to set as the top of the BorderPane. 238 * @param right The node to set as the right of the BorderPane. 239 * @param bottom The node to set as the bottom of the BorderPane. 240 * @param left The node to set as the left of the BorderPane. 241 * @since JavaFX 8.0 242 */ 243 public BorderPane(Node center, Node top, Node right, Node bottom, Node left) { 244 super(); 245 setCenter(center); 246 setTop(top); 247 setRight(right); 248 setBottom(bottom); 249 setLeft(left); 250 } 251 252 /** 253 * The node placed in the center of this border pane. 254 * If resizable, it will be resized fill the center of the border pane 255 * between the top, bottom, left, and right nodes. If the node cannot be 256 * resized to fill the center space (it's not resizable or its max size prevents 257 * it) then it will be center aligned unless the child's alignment constraint 258 * has been set. 259 * @return the node placed in the center of this border pane 260 */ 261 public final ObjectProperty<Node> centerProperty() { 262 if (center == null) { 263 center = new BorderPositionProperty("center"); 264 } 265 return center; 266 } 267 private ObjectProperty<Node> center; 268 public final void setCenter(Node value) { centerProperty().set(value); } 269 public final Node getCenter() { return center == null ? null : center.get(); } 270 271 /** 272 * The node placed on the top edge of this border pane. 273 * If resizable, it will be resized to its preferred height and it's width 274 * will span the width of the border pane. If the node cannot be 275 * resized to fill the top space (it's not resizable or its max size prevents 276 * it) then it will be aligned top-left within the space unless the child's 277 * alignment constraint has been set. 278 * @return the node placed on the top edge of this border pane 279 */ 280 public final ObjectProperty<Node> topProperty() { 281 if (top == null) { 282 top = new BorderPositionProperty("top"); 283 } 284 return top; 285 } 286 private ObjectProperty<Node> top; 287 public final void setTop(Node value) { topProperty().set(value); } 288 public final Node getTop() { return top == null ? null : top.get(); } 289 290 /** 291 * The node placed on the bottom edge of this border pane. 292 * If resizable, it will be resized to its preferred height and it's width 293 * will span the width of the border pane. If the node cannot be 294 * resized to fill the bottom space (it's not resizable or its max size prevents 295 * it) then it will be aligned bottom-left within the space unless the child's 296 * alignment constraint has been set. 297 * @return the node placed on the bottom edge of this border pane 298 */ 299 public final ObjectProperty<Node> bottomProperty() { 300 if (bottom == null) { 301 bottom = new BorderPositionProperty("bottom"); 302 } 303 return bottom; 304 } 305 private ObjectProperty<Node> bottom; 306 public final void setBottom(Node value) { bottomProperty().set(value); } 307 public final Node getBottom() { return bottom == null ? null : bottom.get(); } 308 309 /** 310 * The node placed on the left edge of this border pane. 311 * If resizable, it will be resized to its preferred width and it's height 312 * will span the height of the border pane between the top and bottom nodes. 313 * If the node cannot be resized to fill the left space (it's not resizable 314 * or its max size prevents it) then it will be aligned top-left within the space 315 * unless the child's alignment constraint has been set. 316 * @return the node placed on the left edge of this border pane 317 */ 318 public final ObjectProperty<Node> leftProperty() { 319 if (left == null) { 320 left = new BorderPositionProperty("left"); 321 } 322 return left; 323 } 324 private ObjectProperty<Node> left; 325 public final void setLeft(Node value) { leftProperty().set(value); } 326 public final Node getLeft() { return left == null ? null : left.get(); } 327 328 /** 329 * The node placed on the right edge of this border pane. 330 * If resizable, it will be resized to its preferred width and it's height 331 * will span the height of the border pane between the top and bottom nodes. 332 * If the node cannot be resized to fill the right space (it's not resizable 333 * or its max size prevents it) then it will be aligned top-right within the space 334 * unless the child's alignment constraint has been set. 335 * @return the node placed on the right edge of this border pane 336 */ 337 public final ObjectProperty<Node> rightProperty() { 338 if (right == null) { 339 right = new BorderPositionProperty("right"); 340 } 341 return right; 342 } 343 private ObjectProperty<Node> right; 344 public final void setRight(Node value) { rightProperty().set(value); } 345 public final Node getRight() { return right == null ? null : right.get(); } 346 347 /** 348 * @return null unless the center, right, bottom, left or top has a content bias. 349 */ 350 @Override public Orientation getContentBias() { 351 final Node c = getCenter(); 352 if (c != null && c.isManaged() && c.getContentBias() != null) { 353 return c.getContentBias(); 354 } 355 356 final Node r = getRight(); 357 if (r != null && r.isManaged() && r.getContentBias() == Orientation.VERTICAL) { 358 return r.getContentBias(); 359 } 360 361 final Node l = getLeft(); 362 if (l != null && l.isManaged() && l.getContentBias() == Orientation.VERTICAL) { 363 return l.getContentBias(); 364 } 365 final Node b = getBottom(); 366 if (b != null && b.isManaged() && b.getContentBias() == Orientation.HORIZONTAL) { 367 return b.getContentBias(); 368 } 369 370 final Node t = getTop(); 371 if (t != null && t.isManaged() && t.getContentBias() == Orientation.HORIZONTAL) { 372 return t.getContentBias(); 373 } 374 375 376 return null; 377 } 378 379 @Override protected double computeMinWidth(double height) { 380 double topMinWidth = getAreaWidth(getTop(), -1, true); 381 double bottomMinWidth = getAreaWidth(getBottom(), -1, true); 382 383 double leftPrefWidth; 384 double rightPrefWidth; 385 double centerMinWidth; 386 387 if (height != -1 && (childHasContentBias(getLeft(), Orientation.VERTICAL) || 388 childHasContentBias(getRight(), Orientation.VERTICAL) || 389 childHasContentBias(getCenter(), Orientation.VERTICAL))) { 390 double topPrefHeight = getAreaHeight(getTop(), -1, false); 391 double bottomPrefHeight = getAreaHeight(getBottom(), -1, false); 392 393 double middleAreaHeight = Math.max(0, height - topPrefHeight - bottomPrefHeight); 394 395 leftPrefWidth = getAreaWidth(getLeft(), middleAreaHeight, false); 396 rightPrefWidth = getAreaWidth(getRight(), middleAreaHeight, false); 397 centerMinWidth = getAreaWidth(getCenter(), middleAreaHeight, true); 398 } else { 399 leftPrefWidth = getAreaWidth(getLeft(), -1, false); 400 rightPrefWidth = getAreaWidth(getRight(), -1, false); 401 centerMinWidth = getAreaWidth(getCenter(), -1, true); 402 } 403 404 final Insets insets = getInsets(); 405 return insets.getLeft() + 406 Math.max(leftPrefWidth + centerMinWidth + rightPrefWidth, Math.max(topMinWidth,bottomMinWidth)) + 407 insets.getRight(); 408 } 409 410 @Override protected double computeMinHeight(double width) { 411 final Insets insets = getInsets(); 412 413 // Bottom and top are always at their pref height 414 double topPrefHeight = getAreaHeight(getTop(), width, false); 415 double bottomPrefHeight = getAreaHeight(getBottom(), width, false); 416 417 double leftMinHeight = getAreaHeight(getLeft(), -1, true); 418 double rightMinHeight = getAreaHeight(getRight(), -1, true); 419 420 double centerMinHeight; 421 if (width != -1 && childHasContentBias(getCenter(), Orientation.HORIZONTAL)) { 422 double leftPrefWidth = getAreaWidth(getLeft(), -1, false); 423 double rightPrefWidth = getAreaWidth(getRight(), -1, false); 424 centerMinHeight = getAreaHeight(getCenter(), 425 Math.max(0, width - leftPrefWidth - rightPrefWidth) , true); 426 } else { 427 centerMinHeight = getAreaHeight(getCenter(), -1, true); 428 } 429 430 double middleAreaMinHeigh = Math.max(centerMinHeight, Math.max(rightMinHeight, leftMinHeight)); 431 432 return insets.getTop() + topPrefHeight + middleAreaMinHeigh + bottomPrefHeight + insets.getBottom(); 433 } 434 435 @Override protected double computePrefWidth(double height) { 436 double topPrefWidth = getAreaWidth(getTop(), -1, false); 437 double bottomPrefWidth = getAreaWidth(getBottom(), -1, false); 438 439 double leftPrefWidth; 440 double rightPrefWidth; 441 double centerPrefWidth; 442 443 if ( height != -1 && (childHasContentBias(getLeft(), Orientation.VERTICAL) || 444 childHasContentBias(getRight(), Orientation.VERTICAL) || 445 childHasContentBias(getCenter(), Orientation.VERTICAL))) { 446 double topPrefHeight = getAreaHeight(getTop(), -1, false); 447 double bottomPrefHeight = getAreaHeight(getBottom(), -1, false); 448 449 double middleAreaHeight = Math.max(0, height - topPrefHeight - bottomPrefHeight); 450 451 leftPrefWidth = getAreaWidth(getLeft(), middleAreaHeight, false); 452 rightPrefWidth = getAreaWidth(getRight(), middleAreaHeight, false); 453 centerPrefWidth = getAreaWidth(getCenter(), middleAreaHeight, false); 454 } else { 455 leftPrefWidth = getAreaWidth(getLeft(), -1, false); 456 rightPrefWidth = getAreaWidth(getRight(), -1, false); 457 centerPrefWidth = getAreaWidth(getCenter(), -1, false); 458 } 459 460 final Insets insets = getInsets(); 461 return insets.getLeft() + 462 Math.max(leftPrefWidth + centerPrefWidth + rightPrefWidth, Math.max(topPrefWidth,bottomPrefWidth)) + 463 insets.getRight(); 464 } 465 466 @Override protected double computePrefHeight(double width) { 467 final Insets insets = getInsets(); 468 469 double topPrefHeight = getAreaHeight(getTop(), width, false); 470 double bottomPrefHeight = getAreaHeight(getBottom(), width, false); 471 double leftPrefHeight = getAreaHeight(getLeft(), -1, false); 472 double rightPrefHeight = getAreaHeight(getRight(), -1, false); 473 474 double centerPrefHeight; 475 if (width != -1 && childHasContentBias(getCenter(), Orientation.HORIZONTAL)) { 476 double leftPrefWidth = getAreaWidth(getLeft(), -1, false); 477 double rightPrefWidth = getAreaWidth(getRight(), -1, false); 478 centerPrefHeight = getAreaHeight(getCenter(), 479 Math.max(0, width - leftPrefWidth - rightPrefWidth) , false); 480 } else { 481 centerPrefHeight = getAreaHeight(getCenter(), -1, false); 482 } 483 484 double middleAreaPrefHeigh = Math.max(centerPrefHeight, Math.max(rightPrefHeight, leftPrefHeight)); 485 486 return insets.getTop() + topPrefHeight + middleAreaPrefHeigh + bottomPrefHeight + insets.getBottom(); 487 } 488 489 @Override protected void layoutChildren() { 490 final Insets insets = getInsets(); 491 double width = getWidth(); 492 double height = getHeight(); 493 final Orientation bias = getContentBias(); 494 495 if (bias == null) { 496 final double minWidth = minWidth(-1); 497 final double minHeight = minHeight(-1); 498 width = width < minWidth ? minWidth : width; 499 height = height < minHeight ? minHeight : height; 500 } else if (bias == Orientation.HORIZONTAL) { 501 final double minWidth = minWidth(-1); 502 width = width < minWidth ? minWidth : width; 503 final double minHeight = minHeight(width); 504 height = height < minHeight ? minHeight : height; 505 } else { 506 final double minHeight = minHeight(-1); 507 height = height < minHeight ? minHeight : height; 508 final double minWidth = minWidth(height); 509 width = width < minWidth ? minWidth : width; 510 } 511 512 final double insideX = insets.getLeft(); 513 final double insideY = insets.getTop(); 514 final double insideWidth = width - insideX - insets.getRight(); 515 final double insideHeight = height - insideY - insets.getBottom(); 516 final Node c = getCenter(); 517 final Node r = getRight(); 518 final Node b = getBottom(); 519 final Node l = getLeft(); 520 final Node t = getTop(); 521 522 double topHeight = 0; 523 if (t != null && t.isManaged()) { 524 Insets topMargin = getNodeMargin(t); 525 double adjustedWidth = adjustWidthByMargin(insideWidth, topMargin); 526 double adjustedHeight = adjustHeightByMargin(insideHeight, topMargin); 527 topHeight = snapSizeY(t.prefHeight(adjustedWidth)); 528 topHeight = Math.min(topHeight, adjustedHeight); 529 Vec2d result = boundedNodeSizeWithBias(t, adjustedWidth, 530 topHeight, true, true, TEMP_VEC2D); 531 topHeight = snapSizeY(result.y); 532 t.resize(snapSizeX(result.x), topHeight); 533 534 topHeight = snapSpaceY(topMargin.getBottom()) + topHeight + snapSpaceY(topMargin.getTop()); 535 Pos alignment = getAlignment(t); 536 positionInArea(t, insideX, insideY, insideWidth, topHeight, 0/*ignore baseline*/, 537 topMargin, 538 alignment != null? alignment.getHpos() : HPos.LEFT, 539 alignment != null? alignment.getVpos() : VPos.TOP, isSnapToPixel()); 540 } 541 542 double bottomHeight = 0; 543 if (b != null && b.isManaged()) { 544 Insets bottomMargin = getNodeMargin(b); 545 double adjustedWidth = adjustWidthByMargin(insideWidth, bottomMargin); 546 double adjustedHeight = adjustHeightByMargin(insideHeight - topHeight, bottomMargin); 547 bottomHeight = snapSizeY(b.prefHeight(adjustedWidth)); 548 bottomHeight = Math.min(bottomHeight, adjustedHeight); 549 Vec2d result = boundedNodeSizeWithBias(b, adjustedWidth, 550 bottomHeight, true, true, TEMP_VEC2D); 551 bottomHeight = snapSizeY(result.y); 552 b.resize(snapSizeX(result.x), bottomHeight); 553 554 bottomHeight = snapSpaceY(bottomMargin.getBottom()) + bottomHeight + snapSpaceY(bottomMargin.getTop()); 555 Pos alignment = getAlignment(b); 556 positionInArea(b, insideX, insideY + insideHeight - bottomHeight, 557 insideWidth, bottomHeight, 0/*ignore baseline*/, 558 bottomMargin, 559 alignment != null? alignment.getHpos() : HPos.LEFT, 560 alignment != null? alignment.getVpos() : VPos.BOTTOM, isSnapToPixel()); 561 } 562 563 double leftWidth = 0; 564 if (l != null && l.isManaged()) { 565 Insets leftMargin = getNodeMargin(l); 566 double adjustedWidth = adjustWidthByMargin(insideWidth, leftMargin); 567 double adjustedHeight = adjustHeightByMargin(insideHeight - topHeight - bottomHeight, leftMargin); // ???? 568 leftWidth = snapSizeX(l.prefWidth(adjustedHeight)); 569 leftWidth = Math.min(leftWidth, adjustedWidth); 570 Vec2d result = boundedNodeSizeWithBias(l, leftWidth, adjustedHeight, 571 true, true, TEMP_VEC2D); 572 leftWidth = snapSizeX(result.x); 573 l.resize(leftWidth, snapSizeY(result.y)); 574 575 leftWidth = snapSpaceX(leftMargin.getLeft()) + leftWidth + snapSpaceX(leftMargin.getRight()); 576 Pos alignment = getAlignment(l); 577 positionInArea(l, insideX, insideY + topHeight, 578 leftWidth, insideHeight - topHeight - bottomHeight, 0/*ignore baseline*/, 579 leftMargin, 580 alignment != null? alignment.getHpos() : HPos.LEFT, 581 alignment != null? alignment.getVpos() : VPos.TOP, isSnapToPixel()); 582 } 583 584 double rightWidth = 0; 585 if (r != null && r.isManaged()) { 586 Insets rightMargin = getNodeMargin(r); 587 double adjustedWidth = adjustWidthByMargin(insideWidth - leftWidth, rightMargin); 588 double adjustedHeight = adjustHeightByMargin(insideHeight - topHeight - bottomHeight, rightMargin); 589 590 rightWidth = snapSizeX(r.prefWidth(adjustedHeight)); 591 rightWidth = Math.min(rightWidth, adjustedWidth); 592 Vec2d result = boundedNodeSizeWithBias(r, rightWidth, adjustedHeight, 593 true, true, TEMP_VEC2D); 594 rightWidth = snapSizeX(result.x); 595 r.resize(rightWidth, snapSizeY(result.y)); 596 597 rightWidth = snapSpaceX(rightMargin.getLeft()) + rightWidth + snapSpaceX(rightMargin.getRight()); 598 Pos alignment = getAlignment(r); 599 positionInArea(r, insideX + insideWidth - rightWidth, insideY + topHeight, 600 rightWidth, insideHeight - topHeight - bottomHeight, 0/*ignore baseline*/, 601 rightMargin, 602 alignment != null? alignment.getHpos() : HPos.RIGHT, 603 alignment != null? alignment.getVpos() : VPos.TOP, isSnapToPixel()); 604 } 605 606 if (c != null && c.isManaged()) { 607 Pos alignment = getAlignment(c); 608 609 layoutInArea(c, insideX + leftWidth, insideY + topHeight, 610 insideWidth - leftWidth - rightWidth, 611 insideHeight - topHeight - bottomHeight, 0/*ignore baseline*/, 612 getNodeMargin(c), 613 alignment != null? alignment.getHpos() : HPos.CENTER, 614 alignment != null? alignment.getVpos() : VPos.CENTER); 615 } 616 } 617 618 private double getAreaWidth(Node child, double height, boolean minimum) { 619 if (child != null && child.isManaged()) { 620 Insets margin = getNodeMargin(child); 621 return minimum ? computeChildMinAreaWidth(child, -1, margin, height, false): 622 computeChildPrefAreaWidth(child, -1, margin, height, false); 623 } 624 return 0; 625 } 626 627 private double getAreaHeight(Node child, double width, boolean minimum) { 628 if (child != null && child.isManaged()) { 629 Insets margin = getNodeMargin(child); 630 return minimum ? computeChildMinAreaHeight(child, -1, margin, width): 631 computeChildPrefAreaHeight(child, -1, margin, width); 632 } 633 return 0; 634 } 635 636 private boolean childHasContentBias(Node child, Orientation orientation) { 637 if (child != null && child.isManaged()) { 638 return child.getContentBias() == orientation; 639 } 640 return false; 641 } 642 643 /*************************************************************************** 644 * * 645 * Private Inner Class * 646 * * 647 **************************************************************************/ 648 649 private final class BorderPositionProperty extends ObjectPropertyBase<Node> { 650 private Node oldValue = null; 651 private final String propertyName; 652 private boolean isBeingInvalidated; 653 654 BorderPositionProperty(String propertyName) { 655 this.propertyName = propertyName; 656 getChildren().addListener(new ListChangeListener<Node>() { 657 658 @Override 659 public void onChanged(ListChangeListener.Change<? extends Node> c) { 660 if (oldValue == null || isBeingInvalidated) { 661 return; 662 } 663 while (c.next()) { 664 if (c.wasRemoved()) { 665 List<? extends Node> removed = c.getRemoved(); 666 for (int i = 0, sz = removed.size(); i < sz; ++i) { 667 if (removed.get(i) == oldValue) { 668 oldValue = null; // Do not remove again in invalidated 669 set(null); 670 } 671 } 672 } 673 } 674 } 675 }); 676 } 677 678 @Override 679 protected void invalidated() { 680 final List<Node> children = getChildren(); 681 682 isBeingInvalidated = true; 683 try { 684 if (oldValue != null) { 685 children.remove(oldValue); 686 } 687 688 final Node _value = get(); 689 this.oldValue = _value; 690 691 if (_value != null) { 692 children.add(_value); 693 } 694 } finally { 695 isBeingInvalidated = false; 696 } 697 } 698 699 @Override 700 public Object getBean() { 701 return BorderPane.this; 702 } 703 704 @Override 705 public String getName() { 706 return propertyName; 707 } 708 } 709 }