Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2009, 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.ArrayList; 029import java.util.Collections; 030import java.util.List; 031import javafx.beans.property.ObjectProperty; 032import javafx.css.CssMetaData; 033import javafx.css.StyleableObjectProperty; 034import javafx.css.StyleableProperty; 035import javafx.geometry.Insets; 036import javafx.geometry.Orientation; 037import javafx.geometry.Pos; 038import javafx.geometry.VPos; 039import javafx.scene.Node; 040import com.sun.javafx.css.converters.EnumConverter; 041import javafx.css.Styleable; 042import javafx.geometry.HPos; 043 044/** 045 * 046 * StackPane lays out its children in a back-to-front stack. 047 * <p> 048 * The z-order of the children is defined by the order of the children list 049 * with the 0th child being the bottom and last child on top. If a border and/or 050 * padding have been set, the children will be layed out within those insets. 051 * <p> 052 * The stackpane will attempt to resize each child to fill its content area. 053 * If the child could not be sized to fill the stackpane (either because it was 054 * not resizable or its max size prevented it) then it will be aligned within 055 * the area using the alignment property, which defaults to Pos.CENTER. 056 * <p> 057 * StackPane example: 058 * <pre><code> StackPane stack = new StackPane(); 059 * stack.getChildren().addAll(new Rectangle(100,100,Color.BLUE), new Label("Go!)); 060 * </code></pre> 061 * <p> 062 * StackPane lays out each managed child regardless of the child's 063 * visible property value; unmanaged children are ignored.</p> 064 * <p> 065 * StackPane may be styled with backgrounds and borders using CSS. See 066 * {@link javafx.scene.layout.Region Region} for details.</p> 067 * 068 * <h4>Resizable Range</h4> 069 * 070 * A stackpane's parent will resize the stackpane within the stackpane's resizable range 071 * during layout. By default the stackpane computes this range based on its content 072 * as outlined in the table below. 073 * <p> 074 * <table border="1"> 075 * <tr><td></td><th>width</th><th>height</th></tr> 076 * <tr><th>minimum</th> 077 * <td>left/right insets plus the largest of the children's min widths.</td> 078 * <td>top/bottom insets plus the largest of the children's min heights.</td></tr> 079 * <tr><th>preferred</th> 080 * <td>left/right insets plus the largest of the children's pref widths.</td> 081 * <td>top/bottom insets plus the largest of the children's pref heights.</td></tr> 082 * <tr><th>maximum</th> 083 * <td>Double.MAX_VALUE</td><td>Double.MAX_VALUE</td></tr> 084 * </table> 085 * <p> 086 * A stackpane's unbounded maximum width and height are an indication to the parent that 087 * it may be resized beyond its preferred size to fill whatever space is assigned 088 * to it. 089 * <p> 090 * StackPane provides properties for setting the size range directly. These 091 * properties default to the sentinel value USE_COMPUTED_SIZE, however the 092 * application may set them to other values as needed: 093 * <pre><code> // ensure stackpane is never resized beyond it's preferred size 094 * <b>stackpane.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);</b> 095 * </code></pre> 096 * Applications may restore the computed values by setting these properties back 097 * to USE_COMPUTED_SIZE. 098 * 099 * <p> 100 * StackPane does not clip its content by default, so it is possible that childrens' 101 * bounds may extend outside its own bounds if a child's min size prevents it from 102 * being fit within the stackpane.</p> 103 * 104 * <h4>Optional Layout Constraints</h4> 105 * 106 * An application may set constraints on individual children to customize StackPane's layout. 107 * For each constraint, StackPane provides a static method for setting it on the child. 108 * <p> 109 * <table border="1"> 110 * <tr><th>Constraint</th><th>Type</th><th>Description</th></tr> 111 * <tr><td>alignment</td><td>javafx.geometry.Pos</td><td>The alignment of the child within the stackpane.</td></tr> 112 * <tr><td>margin</td><td>javafx.geometry.Insets</td><td>Margin space around the outside of the child.</td></tr> 113 * </table> 114 * <p> 115 * Examples: 116 * <pre><code> // Align the title Label at the bottom-center of the stackpane 117 * Label title = new Label(); 118 * <b>StackPane.setAlignment(title, Pos.BOTTOM_CENTER);</b> 119 * stackpane.getChildren.addAll(new ImageView(...), title); 120 * 121 * // Create an 8 pixel margin around a listview in the stackpane 122 * ListView list = new ListView(); 123 * <b>StackPane.setMargin(list, new Insets(8,8,8,8);</b> 124 * stackpane.getChildren().add(list); 125 * </code></pre> 126 * 127 */ 128 129public class StackPane extends Pane { 130 131 private boolean biasDirty = true; 132 private boolean performingLayout = false; 133 private Orientation bias; 134 135 /******************************************************************** 136 * BEGIN static methods 137 ********************************************************************/ 138 139 private static final String MARGIN_CONSTRAINT = "stackpane-margin"; 140 private static final String ALIGNMENT_CONSTRAINT = "stackpane-alignment"; 141 142 /** 143 * Sets the alignment for the child when contained by a stackpane. 144 * If set, will override the stackpane's default alignment. 145 * Setting the value to null will remove the constraint. 146 * @param child the child node of a stackpane 147 * @param value the alignment position for the child 148 */ 149 public static void setAlignment(Node child, Pos value) { 150 setConstraint(child, ALIGNMENT_CONSTRAINT, value); 151 } 152 153 /** 154 * Returns the child's alignment constraint if set. 155 * @param child the child node of a stackpane 156 * @return the alignment position for the child or null if no alignment was set 157 */ 158 public static Pos getAlignment(Node child) { 159 return (Pos)getConstraint(child, ALIGNMENT_CONSTRAINT); 160 } 161 162 /** 163 * Sets the margin for the child when contained by a stackpane. 164 * If set, the stackpane will layout the child with the margin space around it. 165 * Setting the value to null will remove the constraint. 166 * @param child the child node of a stackpane 167 * @param value the margin of space around the child 168 */ 169 public static void setMargin(Node child, Insets value) { 170 setConstraint(child, MARGIN_CONSTRAINT, value); 171 } 172 173 /** 174 * Returns the child's margin constraints if set. 175 * @param child the child node of a stackpane 176 * @return the margin for the child or null if no margin was set 177 */ 178 public static Insets getMargin(Node child) { 179 return (Insets)getConstraint(child, MARGIN_CONSTRAINT); 180 } 181 182 /** 183 * Removes all stackpane constraints from the child node. 184 * @param child the child node 185 */ 186 public static void clearConstraints(Node child) { 187 setAlignment(child, null); 188 setMargin(child, null); 189 } 190 /******************************************************************** 191 * END static methods 192 ********************************************************************/ 193 194 /** 195 * Creates a StackPane layout with default CENTER alignment. 196 */ 197 public StackPane() { 198 super(); 199 } 200 201 /** 202 * Creates a StackPane layout with default CENTER alignment. 203 * @param children The initial set of children for this pane. 204 */ 205 public StackPane(Node... children) { 206 super(); 207 getChildren().addAll(children); 208 } 209 210 /** 211 * The default alignment of children within the stackpane's width and height. 212 * This may be overridden on individual children by setting the child's 213 * alignment constraint. 214 */ 215 public final ObjectProperty<Pos> alignmentProperty() { 216 if (alignment == null) { 217 alignment = new StyleableObjectProperty<Pos>(Pos.CENTER) { 218 @Override 219 public void invalidated() { 220 requestLayout(); 221 } 222 223 @Override 224 public CssMetaData<StackPane, Pos> getCssMetaData() { 225 return StyleableProperties.ALIGNMENT; 226 } 227 228 @Override 229 public Object getBean() { 230 return StackPane.this; 231 } 232 233 @Override 234 public String getName() { 235 return "alignment"; 236 } 237 }; 238 } 239 return alignment; 240 } 241 242 private ObjectProperty<Pos> alignment; 243 public final void setAlignment(Pos value) { alignmentProperty().set(value); } 244 public final Pos getAlignment() { return alignment == null ? Pos.CENTER : alignment.get(); } 245 private Pos getAlignmentInternal() { 246 Pos localPos = getAlignment(); 247 return localPos == null ? Pos.CENTER : localPos; 248 } 249 250 /** 251 * 252 * @return the first non-null contentBias of its managed children or null if no managed children 253 * have a content bias. 254 */ 255 @Override public Orientation getContentBias() { 256 if (biasDirty) { 257 final List<Node> children = getChildren(); 258 for (Node child : children) { 259 Orientation contentBias = child.getContentBias(); 260 if (child.isManaged() && contentBias != null) { 261 bias = contentBias; 262 break; 263 } 264 } 265 biasDirty = false; 266 } 267 return bias; 268 } 269 270 @Override protected double computeMinWidth(double height) { 271 List<Node>managed = getManagedChildren(); 272 return getInsets().getLeft() + 273 computeMaxMinAreaWidth(managed, getMargins(managed), getAlignmentInternal().getHpos(), height) + 274 getInsets().getRight(); 275 } 276 277 @Override protected double computeMinHeight(double width) { 278 List<Node>managed = getManagedChildren(); 279 return getInsets().getTop() + 280 computeMaxMinAreaHeight(managed, getMargins(managed), getAlignmentInternal().getVpos(), width) + 281 getInsets().getBottom(); 282 } 283 284 @Override protected double computePrefWidth(double height) { 285 List<Node>managed = getManagedChildren(); 286 Insets[] margins = getMargins(managed); 287// double h = -1; 288// boolean vertBias = false; 289// for (Node child: managed) { 290// if (child.getContentBias() == Orientation.VERTICAL) { 291// vertBias = true; 292// break; 293// } 294// } 295// if (vertBias) { 296// // widest may depend on height 297// h = computeMaxPrefAreaHeight(managed, margins, -1, getAlignment().getVpos()); 298// } 299 Insets padding = getInsets(); 300 return padding.getLeft() + 301 computeMaxPrefAreaWidth(managed, margins, 302 (height == -1) ? -1 : (height - padding.getTop() - padding.getBottom()), 303 getAlignmentInternal().getHpos()) + 304 padding.getRight(); 305 } 306 307 @Override protected double computePrefHeight(double width) { 308 List<Node>managed = getManagedChildren(); 309 Insets[] margins = getMargins(managed); 310// double w = -1; 311// boolean horizBias = false; 312// for (Node child: managed) { 313// if (child.getContentBias() == Orientation.HORIZONTAL) { 314// horizBias = true; 315// break; 316// } 317// } 318// if (horizBias) { 319// // tallest may depend on width of tile 320// w = computeMaxPrefAreaWidth(managed, margins, -1, getAlignment().getHpos()); 321// } 322 Insets padding = getInsets(); 323 return padding.getTop() + 324 computeMaxPrefAreaHeight(managed, margins, 325 (width == -1) ? -1 : (width - padding.getLeft() - padding.getRight()), 326 getAlignmentInternal().getVpos()) + 327 padding.getBottom(); 328 } 329 330 private Insets[] getMargins(List<Node>managed) { 331 Insets margins[] = new Insets[managed.size()]; 332 for(int i = 0; i < margins.length; i++) { 333 margins[i] = getMargin(managed.get(i)); 334 } 335 return margins; 336 } 337 338 @Override public void requestLayout() { 339 if (performingLayout) { 340 return; 341 } 342 biasDirty = true; 343 bias = null; 344 super.requestLayout(); 345 } 346 347 @Override protected void layoutChildren() { 348 performingLayout = true; 349 List<Node> managed = getManagedChildren(); 350 Pos align = getAlignmentInternal(); 351 HPos alignHpos = align.getHpos(); 352 VPos alignVpos = align.getVpos(); 353 double width = getWidth(); 354 double height = getHeight(); 355 double top = getInsets().getTop(); 356 double right = getInsets().getRight(); 357 double left = getInsets().getLeft(); 358 double bottom = getInsets().getBottom(); 359 double baselineOffset = alignVpos == VPos.BASELINE ? getMaxBaselineOffset(managed) 360 : height/2; 361 double contentWidth = width - left - right; 362 double contentHeight = height - top - bottom; 363 for (int i = 0, size = managed.size(); i < size; i++) { 364 Node child = managed.get(i); 365 Pos childAlignment = StackPane.getAlignment(child); 366 layoutInArea(child, left, top, 367 contentWidth, contentHeight, 368 baselineOffset, getMargin(child), 369 childAlignment != null? childAlignment.getHpos() : alignHpos, 370 childAlignment != null? childAlignment.getVpos() : alignVpos); 371 } 372 performingLayout = false; 373 } 374 375 /*************************************************************************** 376 * * 377 * Stylesheet Handling * 378 * * 379 **************************************************************************/ 380 381 /** 382 * Super-lazy instantiation pattern from Bill Pugh. 383 * @treatAsPrivate implementation detail 384 */ 385 private static class StyleableProperties { 386 private static final CssMetaData<StackPane,Pos> ALIGNMENT = 387 new CssMetaData<StackPane,Pos>("-fx-alignment", 388 new EnumConverter<Pos>(Pos.class), 389 Pos.CENTER) { 390 391 @Override 392 public boolean isSettable(StackPane node) { 393 return node.alignment == null || 394 !node.alignment.isBound(); 395 } 396 397 @Override 398 public StyleableProperty<Pos> getStyleableProperty(StackPane node) { 399 return (StyleableProperty<Pos>)node.alignmentProperty(); 400 } 401 }; 402 403 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 404 static { 405 final List<CssMetaData<? extends Styleable, ?>> styleables = 406 new ArrayList<CssMetaData<? extends Styleable, ?>>(Region.getClassCssMetaData()); 407 styleables.add(ALIGNMENT); 408 STYLEABLES = Collections.unmodifiableList(styleables); 409 } 410 } 411 412 /** 413 * @return The CssMetaData associated with this class, which may include the 414 * CssMetaData of its super classes. 415 */ 416 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 417 return StyleableProperties.STYLEABLES; 418 } 419 420 /** 421 * {@inheritDoc} 422 * 423 */ 424 425 426 @Override 427 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 428 return getClassCssMetaData(); 429 } 430 431}