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