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 }