Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.scene.layout;
027
028import com.sun.javafx.geom.Vec2d;
029import java.util.List;
030import javafx.beans.property.ObjectProperty;
031import javafx.beans.property.ObjectPropertyBase;
032import javafx.collections.ListChangeListener;
033import javafx.geometry.HPos;
034import javafx.geometry.Insets;
035import javafx.geometry.Orientation;
036import javafx.geometry.Pos;
037import javafx.geometry.VPos;
038import javafx.scene.Node;
039import static javafx.scene.layout.Region.positionInArea;
040
041
042/**
043 * BorderPane lays out children in top, left, right, bottom, and center positions.
044 *
045 * <p> <img src="doc-files/borderpane.png"/> </p>
046 *
047 * The top and bottom children will be resized to their preferred heights and
048 * extend the width of the border pane.  The left and right children will be resized
049 * to their preferred widths and extend the length between the top and bottom nodes.
050 * And the center node will be resized to fill the available space in the middle.
051 * Any of the positions may be null.
052 *
053 * Example:
054 * <pre><code>     <b>BorderPane borderPane = new BorderPane();</b>
055 *     ToolBar toolbar = new ToolBar();
056 *     HBox statusbar = new HBox();
057 *     Node appContent = new AppContentNode();
058 *     <b>borderPane.setTop(toolbar);
059 *     borderPane.setCenter(appContent);
060 *     borderPane.setBottom(statusbar);</b>
061 * </code></pre>
062 * <p>
063 * Borderpanes may be styled with backgrounds and borders using CSS.  See
064 * {@link javafx.scene.layout.Region Region} superclass for details.</p>
065 *
066 * <p>
067 * BorderPane honors the minimum, preferred, and maximum sizes of its children.
068 * If the child's resizable range prevents it from be resized to fit within its
069 * position, it will be aligned relative to the space using a default alignment
070 * as follows:
071 * <ul>
072 * <li>top: Pos.TOP_LEFT</li>
073 * <li>bottom: Pos.BOTTOM_LEFT</li>
074 * <li>left: Pos.TOP_LEFT</li>
075 * <li>right: Pos.TOP_RIGHT</li>
076 * <li>center: Pos.CENTER</li>
077 * </ul>
078 * See "Optional Layout Constraints" on how to customize these alignments.
079 *
080 * <p>
081 * BorderPane lays out each child set in the five positions regardless of the child's
082 * visible property value; unmanaged children are ignored.</p>
083 *
084 * <h4>Resizable Range</h4>
085 * BorderPane is commonly used as the root of a {@link javafx.scene.Scene Scene},
086 * in which case its size will track the size of the scene.  If the scene or stage
087 * size has not been directly set by the application, the scene size will be
088 * initialized to the border pane's preferred size.   However, if a border pane
089 * has a parent other than the scene, that parent will resize the border pane within
090 * the border pane's resizable range during layout.   By default the border pane
091 * computes this range based on its content as outlined in the table below.
092 * <p>
093 * <table border="1">
094 * <tr><td></td><th>width</th><th>height</th></tr>
095 * <tr><th>minimum</th>
096 * <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>
097 * <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>
098 * <tr><th>preferred</th>
099 * <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 */
140public class BorderPane extends Pane {
141    /********************************************************************
142     *  BEGIN static methods
143     ********************************************************************/
144
145    private static final String MARGIN = "borderpane-margin";
146    private static final String ALIGNMENT = "borderpane-alignment";
147
148    /**
149     * Sets the alignment for the child when contained by a border pane.
150     * If set, will override the border pane's default alignment for the child's position.
151     * Setting the value to null will remove the constraint.
152     * @param child the child node of a border pane
153     * @param value the alignment position for the child
154     */
155    public static void setAlignment(Node child, Pos value) {
156        setConstraint(child, ALIGNMENT, value);
157    }
158
159    /**
160     * Returns the child's alignment constraint if set.
161     * @param child the child node of a border pane
162     * @return the alignment position for the child or null if no alignment was set
163     */
164    public static Pos getAlignment(Node child) {
165        return (Pos)getConstraint(child, ALIGNMENT);
166    }
167
168    /**
169     * Sets the margin for the child when contained by a border pane.
170     * If set, the border pane will lay it out with the margin space around it.
171     * Setting the value to null will remove the constraint.
172     * @param child the child node of a border pane
173     * @param value the margin of space around the child
174     */
175    public static void setMargin(Node child, Insets value) {
176        setConstraint(child, MARGIN, value);
177    }
178
179    /**
180     * Returns the child's margin constraint if set.
181     * @param child the child node of a border pane
182     * @return the margin for the child or null if no margin was set
183     */
184    public static Insets getMargin(Node child) {
185        return (Insets)getConstraint(child, MARGIN);
186    }
187
188    // convenience for handling null margins
189    private static Insets getNodeMargin(Node child) {
190        Insets margin = getMargin(child);
191        return margin != null ? margin : Insets.EMPTY;
192    }
193
194    /**
195     * Removes all border pane constraints from the child node.
196     * @param child the child node
197     */
198    public static void clearConstraints(Node child) {
199        setAlignment(child, null);
200        setMargin(child, null);
201    }
202
203    /********************************************************************
204     *  END static methods
205     ********************************************************************/
206
207    /**
208     * Creates a BorderPane layout.
209     */
210    public BorderPane() {
211        super();
212    }
213
214    /**
215     * Creates an BorderPane layout with the given Node as the center of the BorderPane.
216     * @param center The node to set as the center of the BorderPane.
217     */
218    public BorderPane(Node center) {
219        super();
220        setCenter(center);
221    }
222
223    /**
224     * Creates an BorderPane layout with the given Nodes to use for each of the main
225     * layout areas of the Border Pane. The top, right, bottom, and left nodes are listed
226     * in clockwise order.
227     * @param center The node to set as the center of the BorderPane.
228     * @param top The node to set as the top of the BorderPane.
229     * @param right The node to set as the right of the BorderPane.
230     * @param bottom The node to set as the bottom of the BorderPane.
231     * @param left The node to set as the left of the BorderPane.
232     */
233    public BorderPane(Node center, Node top, Node right, Node bottom, Node left) {
234        super();
235        setCenter(center);
236        setTop(top);
237        setRight(right);
238        setBottom(bottom);
239        setLeft(left);
240    }
241
242    /**
243     * The node placed in the center of this border pane.
244     * If resizable, it will be resized fill the center of the border pane
245     * between the top, bottom, left, and right nodes.   If the node cannot be
246     * resized to fill the center space (it's not resizable or its max size prevents
247     * it) then it will be center aligned unless the child's alignment constraint
248     * has been set.
249     */
250    public final ObjectProperty<Node> centerProperty() {
251        if (center == null) {
252            center = new BorderPositionProperty("center");
253        }
254        return center;
255    }
256    private ObjectProperty<Node> center;
257    public final void setCenter(Node value) { centerProperty().set(value); }
258    public final Node getCenter() { return center == null ? null : center.get(); }
259
260    /**
261     * The node placed on the top edge of this border pane.
262     * If resizable, it will be resized to its preferred height and it's width
263     * will span the width of the border pane.  If the node cannot be
264     * resized to fill the top space (it's not resizable or its max size prevents
265     * it) then it will be aligned top-left within the space unless the child's
266     * alignment constraint has been set.
267     */
268    public final ObjectProperty<Node> topProperty() {
269        if (top == null) {
270            top = new BorderPositionProperty("top");
271        }
272        return top;
273    }
274    private ObjectProperty<Node> top;
275    public final void setTop(Node value) { topProperty().set(value); }
276    public final Node getTop() { return top == null ? null : top.get();  }
277
278    /**
279     * The node placed on the bottom edge of this border pane.
280     * If resizable, it will be resized to its preferred height and it's width
281     * will span the width of the border pane.  If the node cannot be
282     * resized to fill the bottom space (it's not resizable or its max size prevents
283     * it) then it will be aligned bottom-left within the space unless the child's
284     * alignment constraint has been set.
285     */
286    public final ObjectProperty<Node> bottomProperty() {
287        if (bottom == null) {
288            bottom = new BorderPositionProperty("bottom");
289        }
290        return bottom;
291    }
292    private ObjectProperty<Node> bottom;
293    public final void setBottom(Node value) { bottomProperty().set(value); }
294    public final Node getBottom() { return bottom == null ? null : bottom.get();  }
295
296    /**
297     * The node placed on the left edge of this border pane.
298     * If resizable, it will be resized to its preferred width and it's height
299     * will span the height of the border pane between the top and bottom nodes.
300     * If the node cannot be resized to fill the left space (it's not resizable
301     * or its max size prevents it) then it will be aligned top-left within the space
302     * unless the child's alignment constraint has been set.
303     */
304    public final ObjectProperty<Node> leftProperty() {
305        if (left == null) {
306            left = new BorderPositionProperty("left");
307        }
308        return left;
309    }
310    private ObjectProperty<Node> left;
311    public final void setLeft(Node value) { leftProperty().set(value); }
312    public final Node getLeft() { return left == null ? null : left.get(); }
313
314    /**
315     * The node placed on the right edge of this border pane.
316     * If resizable, it will be resized to its preferred width and it's height
317     * will span the height of the border pane between the top and bottom nodes.
318     * If the node cannot be resized to fill the right space (it's not resizable
319     * or its max size prevents it) then it will be aligned top-right within the space
320     * unless the child's alignment constraint has been set.
321     */
322    public final ObjectProperty<Node> rightProperty() {
323        if (right == null) {
324            right = new BorderPositionProperty("right");
325        }
326        return right;
327    }
328    private ObjectProperty<Node> right;
329    public final void setRight(Node value) { rightProperty().set(value); }
330    public final Node getRight() { return right == null ? null : right.get(); }
331
332    /**
333     * @return null unless the center, right, bottom, left or top has a content bias.
334     */
335    @Override public Orientation getContentBias() {
336        final Node c = getCenter();
337        if (c != null && c.isManaged() && c.getContentBias() != null) {
338            return c.getContentBias();
339        }
340
341        final Node r = getRight();
342        if (r != null && r.isManaged() && r.getContentBias() == Orientation.VERTICAL) {
343            return r.getContentBias();
344        }
345
346        final Node l = getLeft();
347        if (l != null && l.isManaged() && l.getContentBias() == Orientation.VERTICAL) {
348            return l.getContentBias();
349        }
350        final Node b = getBottom();
351        if (b != null && b.isManaged() && b.getContentBias() == Orientation.HORIZONTAL) {
352            return b.getContentBias();
353        }
354
355        final Node t = getTop();
356        if (t != null && t.isManaged() && t.getContentBias() == Orientation.HORIZONTAL) {
357            return t.getContentBias();
358        }
359
360
361        return null;
362    }
363
364    @Override protected double computeMinWidth(double height) {
365        double topMinWidth = getAreaWidth(getTop(), -1, true);
366        double bottomMinWidth = getAreaWidth(getBottom(), -1, true);
367
368        double leftPrefWidth;
369        double rightPrefWidth;
370        double centerMinWidth;
371
372        if (height != -1 && (childHasContentBias(getLeft(), Orientation.VERTICAL) ||
373                childHasContentBias(getRight(), Orientation.VERTICAL) ||
374            childHasContentBias(getCenter(), Orientation.VERTICAL))) {
375            double topPrefHeight = getAreaHeight(getTop(), -1, false);
376            double bottomPrefHeight = getAreaHeight(getBottom(), -1, false);
377
378            double middleAreaHeight = Math.max(0, height - topPrefHeight - bottomPrefHeight);
379
380            leftPrefWidth = getAreaWidth(getLeft(), middleAreaHeight, false);
381            rightPrefWidth = getAreaWidth(getRight(), middleAreaHeight, false);
382            centerMinWidth = getAreaWidth(getCenter(), middleAreaHeight, true);
383        } else {
384            leftPrefWidth = getAreaWidth(getLeft(), -1, false);
385            rightPrefWidth = getAreaWidth(getRight(), -1, false);
386            centerMinWidth = getAreaWidth(getCenter(), -1, true);
387        }
388
389        final Insets insets = getInsets();
390        return insets.getLeft() +
391                Math.max(leftPrefWidth + centerMinWidth + rightPrefWidth, Math.max(topMinWidth,bottomMinWidth)) +
392                insets.getRight();
393    }
394
395    @Override protected double computeMinHeight(double width) {
396        final Insets insets = getInsets();
397
398        // Bottom and top are always at their pref height
399        double topPrefHeight = getAreaHeight(getTop(), width, false);
400        double bottomPrefHeight = getAreaHeight(getBottom(), width, false);
401
402        double leftMinHeight = getAreaHeight(getLeft(), -1, true);
403        double rightMinHeight = getAreaHeight(getRight(), -1, true);
404
405        double centerMinHeight;
406        if (width != -1 && childHasContentBias(getCenter(), Orientation.HORIZONTAL)) {
407            double leftPrefWidth = getAreaWidth(getLeft(), -1, false);
408            double rightPrefWidth = getAreaWidth(getRight(), -1, false);
409            centerMinHeight = getAreaHeight(getCenter(),
410                    Math.max(0, width - leftPrefWidth - rightPrefWidth) , true);
411        } else {
412            centerMinHeight = getAreaHeight(getCenter(), -1, true);
413        }
414
415        double middleAreaMinHeigh = Math.max(centerMinHeight, Math.max(rightMinHeight, leftMinHeight));
416
417        return insets.getTop() + topPrefHeight + middleAreaMinHeigh + bottomPrefHeight + insets.getBottom();
418    }
419
420    @Override protected double computePrefWidth(double height) {
421        double topPrefWidth = getAreaWidth(getTop(), -1, false);
422        double bottomPrefWidth = getAreaWidth(getBottom(), -1, false);
423
424        double leftPrefWidth;
425        double rightPrefWidth;
426        double centerPrefWidth;
427
428        if ( height != -1 && (childHasContentBias(getLeft(), Orientation.VERTICAL) ||
429                childHasContentBias(getRight(), Orientation.VERTICAL) ||
430            childHasContentBias(getCenter(), Orientation.VERTICAL))) {
431            double topPrefHeight = getAreaHeight(getTop(), -1, false);
432            double bottomPrefHeight = getAreaHeight(getBottom(), -1, false);
433
434            double middleAreaHeight = Math.max(0, height - topPrefHeight - bottomPrefHeight);
435
436            leftPrefWidth = getAreaWidth(getLeft(), middleAreaHeight, false);
437            rightPrefWidth = getAreaWidth(getRight(), middleAreaHeight, false);
438            centerPrefWidth = getAreaWidth(getCenter(), middleAreaHeight, false);
439        } else {
440            leftPrefWidth = getAreaWidth(getLeft(), -1, false);
441            rightPrefWidth = getAreaWidth(getRight(), -1, false);
442            centerPrefWidth = getAreaWidth(getCenter(), -1, false);
443        }
444
445        final Insets insets = getInsets();
446        return insets.getLeft() +
447                Math.max(leftPrefWidth + centerPrefWidth + rightPrefWidth, Math.max(topPrefWidth,bottomPrefWidth)) +
448                insets.getRight();
449    }
450
451    @Override protected double computePrefHeight(double width) {
452        final Insets insets = getInsets();
453
454        double topPrefHeight = getAreaHeight(getTop(), width, false);
455        double bottomPrefHeight = getAreaHeight(getBottom(), width, false);
456        double leftPrefHeight = getAreaHeight(getLeft(), -1, false);
457        double rightPrefHeight = getAreaHeight(getRight(), -1, false);
458
459        double centerPrefHeight;
460        if (width != -1 && childHasContentBias(getCenter(), Orientation.HORIZONTAL)) {
461            double leftPrefWidth = getAreaWidth(getLeft(), -1, false);
462            double rightPrefWidth = getAreaWidth(getRight(), -1, false);
463            centerPrefHeight = getAreaHeight(getCenter(),
464                    Math.max(0, width - leftPrefWidth - rightPrefWidth) , false);
465        } else {
466            centerPrefHeight = getAreaHeight(getCenter(), -1, false);
467        }
468
469        double middleAreaPrefHeigh = Math.max(centerPrefHeight, Math.max(rightPrefHeight, leftPrefHeight));
470
471        return insets.getTop() + topPrefHeight + middleAreaPrefHeigh + bottomPrefHeight + insets.getBottom();
472    }
473
474    @Override protected void layoutChildren() {
475        final Insets insets = getInsets();
476        double width = getWidth();
477        double height = getHeight();
478        final Orientation bias = getContentBias();
479
480        if (bias == null) {
481            final double minWidth = minWidth(-1);
482            final double minHeight = minHeight(-1);
483            width = width < minWidth ? minWidth : width;
484            height = height < minHeight ? minHeight : height;
485        } else if (bias == Orientation.HORIZONTAL) {
486            final double minWidth = minWidth(-1);
487            width = width < minWidth ? minWidth : width;
488            final double minHeight = minHeight(width);
489            height = height < minHeight ? minHeight : height;
490        } else {
491            final double minHeight = minHeight(-1);
492            height = height < minHeight ? minHeight : height;
493            final double minWidth = minWidth(height);
494            width = width < minWidth ? minWidth : width;
495        }
496
497        final double insideX = insets.getLeft();
498        final double insideY = insets.getTop();
499        final double insideWidth = width - insideX - insets.getRight();
500        final double insideHeight = height - insideY - insets.getBottom();
501        final Node c = getCenter();
502        final Node r = getRight();
503        final Node b = getBottom();
504        final Node l = getLeft();
505        final Node t = getTop();
506
507        double topHeight = 0;
508        if (t != null && t.isManaged()) {
509            Insets topMargin = getNodeMargin(t);
510            double adjustedWidth = adjustWidthByMargin(insideWidth, topMargin);
511            double adjustedHeight = adjustHeightByMargin(insideHeight, topMargin);
512            topHeight = snapSize(t.prefHeight(adjustedWidth));
513            topHeight = Math.min(topHeight, adjustedHeight);
514            Vec2d result = boundedNodeSizeWithBias(t, adjustedWidth,
515                   topHeight, true, true, TEMP_VEC2D);
516            topHeight = snapSize(result.y);
517            t.resize(snapSize(result.x), topHeight);
518
519            topHeight = snapSpace(topMargin.getBottom()) + topHeight + snapSpace(topMargin.getTop());
520            Pos alignment = getAlignment(t);
521            positionInArea(t, insideX, insideY, insideWidth, topHeight, 0/*ignore baseline*/,
522                    topMargin,
523                    alignment != null? alignment.getHpos() : HPos.LEFT,
524                    alignment != null? alignment.getVpos() : VPos.TOP, isSnapToPixel());
525        }
526
527        double bottomHeight = 0;
528        if (b != null && b.isManaged()) {
529            Insets bottomMargin = getNodeMargin(b);
530            double adjustedWidth = adjustWidthByMargin(insideWidth, bottomMargin);
531            double adjustedHeight = adjustHeightByMargin(insideHeight - topHeight, bottomMargin);
532            bottomHeight = snapSize(b.prefHeight(adjustedWidth));
533            bottomHeight = Math.min(bottomHeight, adjustedHeight);
534            Vec2d result = boundedNodeSizeWithBias(b, adjustedWidth,
535                    bottomHeight, true, true, TEMP_VEC2D);
536            bottomHeight = snapSize(result.y);
537            b.resize(snapSize(result.x), bottomHeight);
538
539            bottomHeight = snapSpace(bottomMargin.getBottom()) + bottomHeight + snapSpace(bottomMargin.getTop());
540            Pos alignment = getAlignment(b);
541            positionInArea(b, insideX, insideY + insideHeight - bottomHeight,
542                    insideWidth, bottomHeight, 0/*ignore baseline*/,
543                    bottomMargin,
544                    alignment != null? alignment.getHpos() : HPos.LEFT,
545                    alignment != null? alignment.getVpos() : VPos.BOTTOM, isSnapToPixel());
546        }
547
548        double leftWidth = 0;
549        if (l != null && l.isManaged()) {
550            Insets leftMargin = getNodeMargin(l);
551            double adjustedWidth = adjustWidthByMargin(insideWidth, leftMargin);
552            double adjustedHeight = adjustHeightByMargin(insideHeight - topHeight - bottomHeight, leftMargin); // ????
553            leftWidth = snapSize(l.prefWidth(adjustedHeight));
554            leftWidth = Math.min(leftWidth, adjustedWidth);
555            Vec2d result = boundedNodeSizeWithBias(l, leftWidth, adjustedHeight,
556                    true, true, TEMP_VEC2D);
557            leftWidth = snapSize(result.x);
558            l.resize(leftWidth, snapSize(result.y));
559
560            leftWidth = snapSpace(leftMargin.getLeft()) + leftWidth + snapSpace(leftMargin.getRight());
561            Pos alignment = getAlignment(l);
562            positionInArea(l, insideX, insideY + topHeight,
563                    leftWidth, insideHeight - topHeight - bottomHeight, 0/*ignore baseline*/,
564                    leftMargin,
565                    alignment != null? alignment.getHpos() : HPos.LEFT,
566                    alignment != null? alignment.getVpos() : VPos.TOP, isSnapToPixel());
567        }
568
569        double rightWidth = 0;
570        if (r != null && r.isManaged()) {
571            Insets rightMargin = getNodeMargin(r);
572            double adjustedWidth = adjustWidthByMargin(insideWidth - leftWidth, rightMargin);
573            double adjustedHeight = adjustHeightByMargin(insideHeight - topHeight - bottomHeight, rightMargin);
574
575            rightWidth = snapSize(r.prefWidth(adjustedHeight));
576            rightWidth = Math.min(rightWidth, adjustedWidth);
577            Vec2d result = boundedNodeSizeWithBias(r, rightWidth, adjustedHeight,
578                    true, true, TEMP_VEC2D);
579            rightWidth = snapSize(result.x);
580            r.resize(rightWidth, snapSize(result.y));
581
582            rightWidth = snapSpace(rightMargin.getLeft()) + rightWidth + snapSpace(rightMargin.getRight());
583            Pos alignment = getAlignment(r);
584            positionInArea(r, insideX + insideWidth - rightWidth, insideY + topHeight,
585                    rightWidth, insideHeight - topHeight - bottomHeight, 0/*ignore baseline*/,
586                    rightMargin,
587                    alignment != null? alignment.getHpos() : HPos.RIGHT,
588                    alignment != null? alignment.getVpos() : VPos.TOP, isSnapToPixel());
589        }
590
591        if (c != null && c.isManaged()) {
592            Pos alignment = getAlignment(c);
593
594            layoutInArea(c, insideX + leftWidth, insideY + topHeight,
595                    insideWidth - leftWidth - rightWidth,
596                    insideHeight - topHeight - bottomHeight, 0/*ignore baseline*/,
597                    getNodeMargin(c),
598                    alignment != null? alignment.getHpos() : HPos.CENTER,
599                    alignment != null? alignment.getVpos() : VPos.CENTER);
600        }
601    }
602
603    private double getAreaWidth(Node child, double height, boolean minimum) {
604        if (child != null && child.isManaged()) {
605            Insets margin = getNodeMargin(child);
606            return minimum ? computeChildMinAreaWidth(child, margin, height):
607                                   computeChildPrefAreaWidth(child, margin, height);
608        }
609        return 0;
610    }
611
612    private double getAreaHeight(Node child, double width, boolean minimum) {
613        if (child != null && child.isManaged()) {
614            Insets margin = getNodeMargin(child);
615            return minimum ? computeChildMinAreaHeight(child, margin, width):
616                                   computeChildPrefAreaHeight(child, margin, width);
617        }
618        return 0;
619    }
620
621    private boolean childHasContentBias(Node child, Orientation orientation) {
622        if (child != null && child.isManaged()) {
623            return child.getContentBias() == orientation;
624        }
625        return false;
626    }
627
628    /***************************************************************************
629     *                                                                         *
630     *                         Private Inner Class                             *
631     *                                                                         *
632     **************************************************************************/
633
634    private final class BorderPositionProperty extends ObjectPropertyBase<Node> {
635        private Node oldValue = null;
636        private final String propertyName;
637        private boolean isBeingInvalidated;
638
639        BorderPositionProperty(String propertyName) {
640            this.propertyName = propertyName;
641            getChildren().addListener(new ListChangeListener<Node>() {
642
643                @Override
644                public void onChanged(ListChangeListener.Change<? extends Node> c) {
645                    if (oldValue == null || isBeingInvalidated) {
646                        return;
647                    }
648                    while (c.next()) {
649                        if (c.wasRemoved()) {
650                            List<? extends Node> removed = c.getRemoved();
651                            for (int i = 0, sz = removed.size(); i < sz; ++i) {
652                                if (removed.get(i) == oldValue) {
653                                    oldValue = null; // Do not remove again in invalidated
654                                    set(null);
655                                }
656                            }
657                        }
658                    }
659                }
660            });
661        }
662
663        @Override
664        protected void invalidated() {
665            final List<Node> children = getChildren();
666
667            isBeingInvalidated = true;
668            try {
669                if (oldValue != null) {
670                    children.remove(oldValue);
671                }
672
673                final Node _value = get();
674                this.oldValue = _value;
675
676                if (_value != null) {
677                    children.add(_value);
678                }
679            } finally {
680                isBeingInvalidated = false;
681            }
682        }
683
684        @Override
685        public Object getBean() {
686            return BorderPane.this;
687        }
688
689        @Override
690        public String getName() {
691            return propertyName;
692        }
693    }
694}