1 /* 2 * Copyright (c) 2009, 2018, 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 java.util.ArrayList; 29 import java.util.Collections; 30 import java.util.List; 31 import javafx.beans.property.ObjectProperty; 32 import javafx.css.CssMetaData; 33 import javafx.css.StyleableObjectProperty; 34 import javafx.css.StyleableProperty; 35 import javafx.geometry.Insets; 36 import javafx.geometry.Orientation; 37 import javafx.geometry.Pos; 38 import javafx.geometry.VPos; 39 import javafx.scene.Node; 40 import javafx.css.converter.EnumConverter; 41 import javafx.css.Styleable; 42 import javafx.geometry.HPos; 43 import javafx.util.Callback; 44 45 /** 46 * 47 * StackPane lays out its children in a back-to-front stack. 48 * <p> 49 * The z-order of the children is defined by the order of the children list 50 * with the 0th child being the bottom and last child on top. If a border and/or 51 * padding have been set, the children will be laid out within those insets. 52 * <p> 53 * The stackpane will attempt to resize each child to fill its content area. 54 * If the child could not be sized to fill the stackpane (either because it was 55 * not resizable or its max size prevented it) then it will be aligned within 56 * the area using the alignment property, which defaults to Pos.CENTER. 57 * <p> 58 * StackPane example: 59 * <pre>{@code 60 * StackPane stack = new StackPane(); 61 * stack.getChildren().addAll(new Rectangle(100,100,Color.BLUE), new Label("Go!)); 62 * }</pre> 63 * <p> 64 * StackPane lays out each managed child regardless of the child's 65 * visible property value; unmanaged children are ignored.</p> 66 * <p> 67 * StackPane may be styled with backgrounds and borders using CSS. See 68 * {@link javafx.scene.layout.Region Region} for details.</p> 69 * 70 * <h3>Resizable Range</h3> 71 * 72 * <p> 73 * A stackpane's parent will resize the stackpane within the stackpane's resizable range 74 * during layout. By default the stackpane computes this range based on its content 75 * as outlined in the table below. 76 * </p> 77 * 78 * <table border="1"> 79 * <caption>StackPane Resize Table</caption> 80 * <tr><td></td><th scope="col">width</th><th scope="col">height</th></tr> 81 * <tr><th scope="row">minimum</th> 82 * <td>left/right insets plus the largest of the children's min widths.</td> 83 * <td>top/bottom insets plus the largest of the children's min heights.</td></tr> 84 * <tr><th scope="row">preferred</th> 85 * <td>left/right insets plus the largest of the children's pref widths.</td> 86 * <td>top/bottom insets plus the largest of the children's pref heights.</td></tr> 87 * <tr><th scope="row">maximum</th> 88 * <td>Double.MAX_VALUE</td><td>Double.MAX_VALUE</td></tr> 89 * </table> 90 * <p> 91 * A stackpane's unbounded maximum width and height are an indication to the parent that 92 * it may be resized beyond its preferred size to fill whatever space is assigned 93 * to it. 94 * <p> 95 * StackPane provides properties for setting the size range directly. These 96 * properties default to the sentinel value USE_COMPUTED_SIZE, however the 97 * application may set them to other values as needed: 98 * <pre><code> // ensure stackpane is never resized beyond it's preferred size 99 * <b>stackpane.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);</b> 100 * </code></pre> 101 * Applications may restore the computed values by setting these properties back 102 * to USE_COMPUTED_SIZE. 103 * 104 * <p> 105 * StackPane does not clip its content by default, so it is possible that children's 106 * bounds may extend outside its own bounds if a child's min size prevents it from 107 * being fit within the stackpane.</p> 108 * 109 * <h3>Optional Layout Constraints</h3> 110 * 111 * <p> 112 * An application may set constraints on individual children to customize StackPane's layout. 113 * For each constraint, StackPane provides a static method for setting it on the child. 114 * </p> 115 * 116 * <table border="1"> 117 * <caption>StackPane Constraint Table</caption> 118 * <tr><th>Constraint</th><th scope="col">Type</th><th scope="col">Description</th></tr> 119 * <tr><th scope="row">alignment</th><td>javafx.geometry.Pos</td><td>The alignment of the child within the stackpane.</td></tr> 120 * <tr><th scope="row">margin</th><td>javafx.geometry.Insets</td><td>Margin space around the outside of the child.</td></tr> 121 * </table> 122 * <p> 123 * Examples: 124 * <pre><code> // Align the title Label at the bottom-center of the stackpane 125 * Label title = new Label(); 126 * <b>StackPane.setAlignment(title, Pos.BOTTOM_CENTER);</b> 127 * stackpane.getChildren.addAll(new ImageView(...), title); 128 * 129 * // Create an 8 pixel margin around a listview in the stackpane 130 * ListView list = new ListView(); 131 * <b>StackPane.setMargin(list, new Insets(8,8,8,8);</b> 132 * stackpane.getChildren().add(list); 133 * </code></pre> 134 * 135 * @since JavaFX 2.0 136 */ 137 138 public class StackPane extends Pane { 139 140 private boolean biasDirty = true; 141 private Orientation bias; 142 143 /******************************************************************** 144 * BEGIN static methods 145 ********************************************************************/ 146 147 private static final String MARGIN_CONSTRAINT = "stackpane-margin"; 148 private static final String ALIGNMENT_CONSTRAINT = "stackpane-alignment"; 149 150 /** 151 * Sets the alignment for the child when contained by a stackpane. 152 * If set, will override the stackpane's default alignment. 153 * Setting the value to null will remove the constraint. 154 * @param child the child node of a stackpane 155 * @param value the alignment position for the child 156 */ 157 public static void setAlignment(Node child, Pos value) { 158 setConstraint(child, ALIGNMENT_CONSTRAINT, value); 159 } 160 161 /** 162 * Returns the child's alignment constraint if set. 163 * @param child the child node of a stackpane 164 * @return the alignment position for the child or null if no alignment was set 165 */ 166 public static Pos getAlignment(Node child) { 167 return (Pos)getConstraint(child, ALIGNMENT_CONSTRAINT); 168 } 169 170 /** 171 * Sets the margin for the child when contained by a stackpane. 172 * If set, the stackpane will layout the child with the margin space around it. 173 * Setting the value to null will remove the constraint. 174 * @param child the child node of a stackpane 175 * @param value the margin of space around the child 176 */ 177 public static void setMargin(Node child, Insets value) { 178 setConstraint(child, MARGIN_CONSTRAINT, value); 179 } 180 181 /** 182 * Returns the child's margin constraints if set. 183 * @param child the child node of a stackpane 184 * @return the margin for the child or null if no margin was set 185 */ 186 public static Insets getMargin(Node child) { 187 return (Insets)getConstraint(child, MARGIN_CONSTRAINT); 188 } 189 190 private static final Callback<Node, Insets> marginAccessor = n -> getMargin(n); 191 192 /** 193 * Removes all stackpane constraints from the child node. 194 * @param child the child node 195 */ 196 public static void clearConstraints(Node child) { 197 setAlignment(child, null); 198 setMargin(child, null); 199 } 200 /******************************************************************** 201 * END static methods 202 ********************************************************************/ 203 204 /** 205 * Creates a StackPane layout with default CENTER alignment. 206 */ 207 public StackPane() { 208 super(); 209 } 210 211 /** 212 * Creates a StackPane layout with default CENTER alignment. 213 * @param children The initial set of children for this pane. 214 * @since JavaFX 8.0 215 */ 216 public StackPane(Node... children) { 217 super(); 218 getChildren().addAll(children); 219 } 220 221 /** 222 * The default alignment of children within the stackpane's width and height. 223 * This may be overridden on individual children by setting the child's 224 * alignment constraint. 225 * @return the alignment of children within this stackpane 226 */ 227 public final ObjectProperty<Pos> alignmentProperty() { 228 if (alignment == null) { 229 alignment = new StyleableObjectProperty<Pos>(Pos.CENTER) { 230 @Override 231 public void invalidated() { 232 requestLayout(); 233 } 234 235 @Override 236 public CssMetaData<StackPane, Pos> getCssMetaData() { 237 return StyleableProperties.ALIGNMENT; 238 } 239 240 @Override 241 public Object getBean() { 242 return StackPane.this; 243 } 244 245 @Override 246 public String getName() { 247 return "alignment"; 248 } 249 }; 250 } 251 return alignment; 252 } 253 254 private ObjectProperty<Pos> alignment; 255 public final void setAlignment(Pos value) { alignmentProperty().set(value); } 256 public final Pos getAlignment() { return alignment == null ? Pos.CENTER : alignment.get(); } 257 private Pos getAlignmentInternal() { 258 Pos localPos = getAlignment(); 259 return localPos == null ? Pos.CENTER : localPos; 260 } 261 262 /** 263 * 264 * @return the first non-null contentBias of its managed children or null if no managed children 265 * have a content bias. 266 */ 267 @Override public Orientation getContentBias() { 268 if (biasDirty) { 269 bias = null; 270 final List<Node> children = getManagedChildren(); 271 for (Node child : children) { 272 Orientation contentBias = child.getContentBias(); 273 if (contentBias != null) { 274 bias = contentBias; 275 if (contentBias == Orientation.HORIZONTAL) { 276 break; 277 } 278 } 279 } 280 biasDirty = false; 281 } 282 return bias; 283 } 284 285 @Override protected double computeMinWidth(double height) { 286 List<Node>managed = getManagedChildren(); 287 return getInsets().getLeft() + 288 computeMaxMinAreaWidth(managed, marginAccessor, height, true) + 289 getInsets().getRight(); 290 } 291 292 @Override protected double computeMinHeight(double width) { 293 List<Node>managed = getManagedChildren(); 294 return getInsets().getTop() + 295 computeMaxMinAreaHeight(managed, marginAccessor, getAlignmentInternal().getVpos(), width) + 296 getInsets().getBottom(); 297 } 298 299 @Override protected double computePrefWidth(double height) { 300 List<Node>managed = getManagedChildren(); 301 Insets padding = getInsets(); 302 return padding.getLeft() + 303 computeMaxPrefAreaWidth(managed, marginAccessor, 304 (height == -1) ? -1 : (height - padding.getTop() - padding.getBottom()), true) + 305 padding.getRight(); 306 } 307 308 @Override protected double computePrefHeight(double width) { 309 List<Node>managed = getManagedChildren(); 310 Insets padding = getInsets(); 311 return padding.getTop() + 312 computeMaxPrefAreaHeight(managed, marginAccessor, 313 (width == -1) ? -1 : (width - padding.getLeft() - padding.getRight()), 314 getAlignmentInternal().getVpos()) + 315 padding.getBottom(); 316 } 317 318 319 @Override public void requestLayout() { 320 biasDirty = true; 321 bias = null; 322 super.requestLayout(); 323 } 324 325 @Override protected void layoutChildren() { 326 List<Node> managed = getManagedChildren(); 327 Pos align = getAlignmentInternal(); 328 HPos alignHpos = align.getHpos(); 329 VPos alignVpos = align.getVpos(); 330 final double width = getWidth(); 331 double height = getHeight(); 332 double top = getInsets().getTop(); 333 double right = getInsets().getRight(); 334 double left = getInsets().getLeft(); 335 double bottom = getInsets().getBottom(); 336 double contentWidth = width - left - right; 337 double contentHeight = height - top - bottom; 338 double baselineOffset = alignVpos == VPos.BASELINE ? 339 getAreaBaselineOffset(managed, marginAccessor, i -> width, contentHeight, true) 340 : 0; 341 for (int i = 0, size = managed.size(); i < size; i++) { 342 Node child = managed.get(i); 343 Pos childAlignment = StackPane.getAlignment(child); 344 layoutInArea(child, left, top, 345 contentWidth, contentHeight, 346 baselineOffset, getMargin(child), 347 childAlignment != null? childAlignment.getHpos() : alignHpos, 348 childAlignment != null? childAlignment.getVpos() : alignVpos); 349 } 350 } 351 352 /*************************************************************************** 353 * * 354 * Stylesheet Handling * 355 * * 356 **************************************************************************/ 357 358 /* 359 * Super-lazy instantiation pattern from Bill Pugh. 360 */ 361 private static class StyleableProperties { 362 private static final CssMetaData<StackPane,Pos> ALIGNMENT = 363 new CssMetaData<StackPane,Pos>("-fx-alignment", 364 new EnumConverter<Pos>(Pos.class), 365 Pos.CENTER) { 366 367 @Override 368 public boolean isSettable(StackPane node) { 369 return node.alignment == null || 370 !node.alignment.isBound(); 371 } 372 373 @Override 374 public StyleableProperty<Pos> getStyleableProperty(StackPane node) { 375 return (StyleableProperty<Pos>)node.alignmentProperty(); 376 } 377 }; 378 379 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 380 static { 381 final List<CssMetaData<? extends Styleable, ?>> styleables = 382 new ArrayList<CssMetaData<? extends Styleable, ?>>(Region.getClassCssMetaData()); 383 styleables.add(ALIGNMENT); 384 STYLEABLES = Collections.unmodifiableList(styleables); 385 } 386 } 387 388 /** 389 * @return The CssMetaData associated with this class, which may include the 390 * CssMetaData of its superclasses. 391 * @since JavaFX 8.0 392 */ 393 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 394 return StyleableProperties.STYLEABLES; 395 } 396 397 /** 398 * {@inheritDoc} 399 * 400 * @since JavaFX 8.0 401 */ 402 403 404 @Override 405 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 406 return getClassCssMetaData(); 407 } 408 409 }