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 java.util.List; 029import javafx.geometry.Bounds; 030import javafx.geometry.Insets; 031import javafx.geometry.Orientation; 032import javafx.scene.Node; 033 034/** 035 * AnchorPane allows the edges of child nodes to be anchored to an offset from 036 * the anchor pane's edges. If the anchor pane has a border and/or padding set, the 037 * offsets will be measured from the inside edge of those insets. 038 * <p> 039 * AnchorPane lays out each managed child regardless of the child's visible property value; 040 * unmanaged children are ignored for all layout calculations.</p> 041 * <p> 042 * AnchorPanes may be styled with backgrounds and borders using CSS. See 043 * {@link javafx.scene.layout.Region Region} superclass for details.</p> 044 * 045 * <h4>Anchor Constraints</h4> 046 * The application sets anchor constraints on each child to configure the anchors 047 * on one or more sides. If a child is anchored on opposite sides (and is resizable), the 048 * anchor pane will resize it to maintain both offsets, otherwise the anchor pane 049 * will resize it to its preferred size. If in the former case (anchored on opposite 050 * sides) and the child is not resizable, then only the top/left anchor will be honored. 051 * AnchorPane provides a static method for setting each anchor constraint. 052 * <p> 053 * <table border="1"> 054 * <tr><th>Constraint</th><th>Type</th><th>Description</th></tr> 055 * <tr><td>topAnchor</td><td>double</td><td>distance from the anchor pane's top insets to the child's top edge.</td></tr> 056 * <tr><td>leftAnchor</td><td>double</td><td>distance from the anchor pane's left insets to the child's left edge.</td></tr> 057 * <tr><td>bottomAnchor</td><td>double</td><td>distance from the anchor pane's bottom insets to the child's bottom edge.</td></tr> 058 * <tr><td>rightAnchor</td><td>double</td><td>distance from the anchor pane's right insets to the child's right edge.</td></tr> 059 * </table> 060 * <p> 061 * AnchorPane Example: 062 * <pre><code> AnchorPane anchorPane = new AnchorPane(); 063 * // List should stretch as anchorPane is resized 064 * ListView list = new ListView(); 065 * <b> AnchorPane.setTopAnchor(list, 10.0); 066 * AnchorPane.setLeftAnchor(list, 10.0); 067 * AnchorPane.setRightAnchor(list, 65.0);</b> 068 * // Button will float on right edge 069 * Button button = new Button("Add"); 070 * <b>AnchorPane.setTopAnchor(button, 10.0); 071 * AnchorPane.setRightAnchor(button, 10.0);</b> 072 * anchorPane.getChildren().addAll(list, button); 073 * </code></pre> 074 * 075 * <h4>Resizable Range</h4> 076 * An anchor pane's parent will resize the anchor pane within the anchor pane's resizable range 077 * during layout. By default the anchor pane computes this range based on its content 078 * as outlined in the table below. 079 * <p> 080 * <table border="1"> 081 * <tr><td></td><th>width</th><th>height</th></tr> 082 * <tr><th>minimum</th> 083 * <td>left/right insets plus width required to display children anchored at left/right with at least their min widths</td> 084 * <td>top/bottom insets plus height required to display children anchored at top/bottom with at least their min heights</td></tr> 085 * <tr><th>preferred</th> 086 * <td>left/right insets plus width required to display children anchored at left/right with at least their pref widths</td> 087 * <td>top/bottom insets plus height required to display children anchored at top/bottom with at least their pref heights</td></tr> 088 * <tr><th>maximum</th> 089 * <td>Double.MAX_VALUE</td><td>Double.MAX_VALUE</td></tr> 090 * </table> 091 * <p> 092 * An anchor pane's unbounded maximum width and height are an indication to the parent that 093 * it may be resized beyond its preferred size to fill whatever space is assigned 094 * to it. 095 * <p> 096 * AnchorPane provides properties for setting the size range directly. These 097 * properties default to the sentinel value Region.USE_COMPUTED_SIZE, however the 098 * application may set them to other values as needed: 099 * <pre><code> <b>anchorPane.setPrefSize(300, 300);</b> 100 * </code></pre> 101 * Applications may restore the computed values by setting these properties back 102 * to Region.USE_COMPUTED_SIZE. 103 * <p> 104 * AnchorPane does not clip its content by default, so it is possible that childrens' 105 * bounds may extend outside its own bounds if the anchor pane is resized smaller 106 * than its preferred size.</p> 107 * 108 */ 109public class AnchorPane extends Pane { 110 111 private static final String TOP_ANCHOR = "pane-top-anchor"; 112 private static final String LEFT_ANCHOR = "pane-left-anchor"; 113 private static final String BOTTOM_ANCHOR = "pane-bottom-anchor"; 114 private static final String RIGHT_ANCHOR = "pane-right-anchor"; 115 116 /******************************************************************** 117 * BEGIN static methods 118 ********************************************************************/ 119 120 /** 121 * Sets the top anchor for the child when contained by an anchor pane. 122 * If set, the anchor pane will maintain the child's size and position so 123 * that it's top is always offset by that amount from the anchor pane's top 124 * content edge. 125 * Setting the value to null will remove the constraint. 126 * @param child the child node of an anchor pane 127 * @param value the offset from the top of the anchor pane 128 */ 129 public static void setTopAnchor(Node child, Double value) { 130 setConstraint(child, TOP_ANCHOR, value); 131 } 132 133 /** 134 * Returns the child's top anchor constraint if set. 135 * @param child the child node of an anchor pane 136 * @return the offset from the top of the anchor pane or null if no top anchor was set 137 */ 138 public static Double getTopAnchor(Node child) { 139 return (Double)getConstraint(child, TOP_ANCHOR); 140 } 141 142 /** 143 * Sets the left anchor for the child when contained by an anchor pane. 144 * If set, the anchor pane will maintain the child's size and position so 145 * that it's left is always offset by that amount from the anchor pane's left 146 * content edge. 147 * Setting the value to null will remove the constraint. 148 * @param child the child node of an anchor pane 149 * @param value the offset from the left of the anchor pane 150 */ 151 public static void setLeftAnchor(Node child, Double value) { 152 setConstraint(child, LEFT_ANCHOR, value); 153 } 154 155 /** 156 * Returns the child's left anchor constraint if set. 157 * @param child the child node of an anchor pane 158 * @return the offset from the left of the anchor pane or null if no left anchor was set 159 */ 160 public static Double getLeftAnchor(Node child) { 161 return (Double)getConstraint(child, LEFT_ANCHOR); 162 } 163 164 /** 165 * Sets the bottom anchor for the child when contained by an anchor pane. 166 * If set, the anchor pane will maintain the child's size and position so 167 * that it's bottom is always offset by that amount from the anchor pane's bottom 168 * content edge. 169 * Setting the value to null will remove the constraint. 170 * @param child the child node of an anchor pane 171 * @param value the offset from the bottom of the anchor pane 172 */ 173 public static void setBottomAnchor(Node child, Double value) { 174 setConstraint(child, BOTTOM_ANCHOR, value); 175 } 176 177 /** 178 * Returns the child's bottom anchor constraint if set. 179 * @param child the child node of an anchor pane 180 * @return the offset from the bottom of the anchor pane or null if no bottom anchor was set 181 */ 182 public static Double getBottomAnchor(Node child) { 183 return (Double)getConstraint(child, BOTTOM_ANCHOR); 184 } 185 186 /** 187 * Sets the right anchor for the child when contained by an anchor pane. 188 * If set, the anchor pane will maintain the child's size and position so 189 * that it's right is always offset by that amount from the anchor pane's right 190 * content edge. 191 * Setting the value to null will remove the constraint. 192 * @param child the child node of an anchor pane 193 * @param value the offset from the right of the anchor pane 194 */ 195 public static void setRightAnchor(Node child, Double value) { 196 setConstraint(child, RIGHT_ANCHOR, value); 197 } 198 199 /** 200 * Returns the child's right anchor constraint if set. 201 * @param child the child node of an anchor pane 202 * @return the offset from the right of the anchor pane or null if no right anchor was set 203 */ 204 public static Double getRightAnchor(Node child) { 205 return (Double)getConstraint(child, RIGHT_ANCHOR); 206 } 207 208 /** 209 * Removes all anchor pane constraints from the child node. 210 * @param child the child node 211 */ 212 public static void clearConstraints(Node child) { 213 setTopAnchor(child, null); 214 setRightAnchor(child, null); 215 setBottomAnchor(child, null); 216 setLeftAnchor(child, null); 217 } 218 219 /******************************************************************** 220 * END static methods 221 ********************************************************************/ 222 223 /** 224 * Creates an AnchorPane layout. 225 */ 226 public AnchorPane() { 227 super(); 228 } 229 230 /** 231 * Creates an AnchorPane layout with the given children. 232 * @param children The initial set of children for this pane. 233 */ 234 public AnchorPane(Node... children) { 235 super(); 236 getChildren().addAll(children); 237 } 238 239 @Override protected double computeMinWidth(double height) { 240 return computeWidth(true, height); 241 } 242 243 @Override protected double computeMinHeight(double width) { 244 return computeHeight(true, width); 245 } 246 247 @Override protected double computePrefWidth(double height) { 248 return computeWidth(false, height); 249 } 250 251 @Override protected double computePrefHeight(double width) { 252 return computeHeight(false, width); 253 } 254 255 private double computeWidth(final boolean minimum, final double height) { 256 double max = 0; 257 double contentHeight = height != -1 ? height - getInsets().getTop() - getInsets().getBottom() : -1; 258 final List<Node> children = getChildren(); 259 for (int i=0, size=children.size(); i<size; i++) { 260 Node child = children.get(i); 261 if (child.isManaged()) { 262 Double leftAnchor = getLeftAnchor(child); 263 Double rightAnchor = getRightAnchor(child); 264 265 double left = leftAnchor != null? leftAnchor : 266 (rightAnchor != null? 0 : child.getLayoutBounds().getMinX() + child.getLayoutX()); 267 double right = rightAnchor != null? rightAnchor : 0; 268 double childHeight = -1; 269 if (child.getContentBias() == Orientation.VERTICAL && contentHeight != -1) { 270 // The width depends on the node's height! 271 childHeight = computeChildHeight(child, getTopAnchor(child), getBottomAnchor(child), contentHeight, -1); 272 } 273 max = Math.max(max, left + (minimum && leftAnchor != null && rightAnchor != null? 274 child.minWidth(childHeight) : child.prefWidth(childHeight)) + right); 275 } 276 } 277 278 final Insets insets = getInsets(); 279 return insets.getLeft() + max + insets.getRight(); 280 } 281 282 private double computeHeight(final boolean minimum, final double width) { 283 double max = 0; 284 double contentWidth = width != -1 ? width - getInsets().getLeft()- getInsets().getRight() : -1; 285 final List<Node> children = getChildren(); 286 for (int i=0, size=children.size(); i<size; i++) { 287 Node child = children.get(i); 288 if (child.isManaged()) { 289 Double topAnchor = getTopAnchor(child); 290 Double bottomAnchor = getBottomAnchor(child); 291 292 double top = topAnchor != null? topAnchor : 293 (bottomAnchor != null? 0 : child.getLayoutBounds().getMinY() + child.getLayoutY()); 294 double bottom = bottomAnchor != null? bottomAnchor : 0; 295 double childWidth = -1; 296 if (child.getContentBias() == Orientation.HORIZONTAL && contentWidth != -1) { 297 childWidth = computeChildWidth(child, getLeftAnchor(child), getRightAnchor(child), contentWidth, -1); 298 } 299 max = Math.max(max, top + (minimum && topAnchor != null && bottomAnchor != null? 300 child.minHeight(childWidth) : child.prefHeight(childWidth)) + bottom); 301 } 302 } 303 304 final Insets insets = getInsets(); 305 return insets.getTop() + max + insets.getBottom(); 306 } 307 308 private double computeChildWidth(Node child, Double leftAnchor, Double rightAnchor, double areaWidth, double height) { 309 if (leftAnchor != null && rightAnchor != null && child.isResizable()) { 310 final Insets insets = getInsets(); 311 return areaWidth - insets.getLeft() - insets.getRight() - leftAnchor - rightAnchor; 312 } 313 return computeChildPrefAreaWidth(child, Insets.EMPTY, height); 314 } 315 316 private double computeChildHeight(Node child, Double topAnchor, Double bottomAnchor, double areaHeight, double width) { 317 if (topAnchor != null && bottomAnchor != null && child.isResizable()) { 318 final Insets insets = getInsets(); 319 return areaHeight - insets.getTop() - insets.getBottom() - topAnchor - bottomAnchor; 320 } 321 return computeChildPrefAreaHeight(child, Insets.EMPTY, width); 322 } 323 324 @Override protected void layoutChildren() { 325 final Insets insets = getInsets(); 326 final List<Node> children = getChildren(); 327 for (int i=0, size=children.size(); i<size; i++) { 328 Node child = children.get(i); 329 if (child.isManaged()) { 330 final Double topAnchor = getTopAnchor(child); 331 final Double bottomAnchor = getBottomAnchor(child); 332 final Double leftAnchor = getLeftAnchor(child); 333 final Double rightAnchor = getRightAnchor(child); 334 final Bounds childLayoutBounds = child.getLayoutBounds(); 335 final Orientation bias = child.getContentBias(); 336 337 double x = child.getLayoutX() + childLayoutBounds.getMinX(); 338 double y = child.getLayoutY() + childLayoutBounds.getMinY(); 339 double w; 340 double h; 341 342 if (bias == Orientation.VERTICAL) { 343 // width depends on height 344 // WARNING: The order of these calls is crucial, there is some 345 // hidden ordering dependency here! 346 h = computeChildHeight(child, topAnchor, bottomAnchor, getHeight(), -1); 347 w = computeChildWidth(child, leftAnchor, rightAnchor, getWidth(), h); 348 } else if (bias == Orientation.HORIZONTAL) { 349 w = computeChildWidth(child, leftAnchor, rightAnchor, getWidth(), -1); 350 h = computeChildHeight(child, topAnchor, bottomAnchor, getHeight(), w); 351 } else { 352 // bias may be null 353 w = computeChildWidth(child, leftAnchor, rightAnchor, getWidth(), -1); 354 h = computeChildHeight(child, topAnchor, bottomAnchor, getHeight(), -1); 355 } 356 357 if (leftAnchor != null) { 358 x = insets.getLeft() + leftAnchor; 359 } else if (rightAnchor != null) { 360 x = getWidth() - insets.getRight() - rightAnchor - w; 361 } 362 363 if (topAnchor != null) { 364 y = insets.getTop() + topAnchor; 365 } else if (bottomAnchor != null) { 366 y = getHeight() - insets.getBottom() - bottomAnchor - h; 367 } 368 369 child.resizeRelocate(x, y, w, h); 370 } 371 } 372 } 373}