1 /* 2 * Copyright (c) 2010, 2014, 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.control; 27 28 import java.util.ArrayList; 29 import java.util.Collections; 30 import java.util.List; 31 32 import javafx.beans.property.BooleanProperty; 33 import javafx.beans.property.BooleanPropertyBase; 34 import javafx.beans.property.ObjectProperty; 35 import javafx.beans.property.SimpleObjectProperty; 36 import javafx.beans.value.WritableValue; 37 import javafx.geometry.Orientation; 38 import javafx.scene.AccessibleAction; 39 import javafx.scene.AccessibleAttribute; 40 import javafx.scene.AccessibleRole; 41 import javafx.scene.Node; 42 import javafx.css.PseudoClass; 43 import javafx.css.StyleableBooleanProperty; 44 import javafx.css.CssMetaData; 45 46 import javafx.css.converter.BooleanConverter; 47 import javafx.scene.control.skin.TitledPaneSkin; 48 49 import javafx.beans.DefaultProperty; 50 import javafx.css.Styleable; 51 import javafx.css.StyleableProperty; 52 53 /** 54 * <p>A TitledPane is a panel with a title that can be opened and closed.</p> 55 * 56 * <p>The panel in a TitledPane can be any {@link Node} such as UI controls or groups 57 * of nodes added to a layout container.</p> 58 * 59 * <p>It is not recommended to set the MinHeight, PrefHeight, or MaxHeight 60 * for this control. Unexpected behavior will occur because the 61 * TitledPane's height changes when it is opened or closed.</p> 62 * 63 * <p>Note that whilst TitledPane extends from Labeled, the inherited properties 64 * are used to manipulate the TitledPane header, not the content area itself. If 65 * the intent is to modify the content area, consider using a layout container 66 * such as {@link javafx.scene.layout.StackPane} and setting your actual content 67 * inside of that. You can then manipulate the StackPane to get the layout 68 * results you are after.</p> 69 * 70 * <p>Example:</p> 71 * <pre><code> 72 * TitledPane t1 = new TitledPane("T1", new Button("B1")); 73 * </code></pre> 74 * 75 * @since JavaFX 2.0 76 */ 77 @DefaultProperty("content") 78 public class TitledPane extends Labeled { 79 80 /*************************************************************************** 81 * * 82 * Constructors * 83 * * 84 **************************************************************************/ 85 86 /** 87 * Creates a new TitledPane with no title or content. 88 */ 89 public TitledPane() { 90 getStyleClass().setAll(DEFAULT_STYLE_CLASS); 91 setAccessibleRole(AccessibleRole.TITLED_PANE); 92 93 // initialize pseudo-class state 94 pseudoClassStateChanged(PSEUDO_CLASS_EXPANDED, true); 95 } 96 97 /** 98 * Creates a new TitledPane with a title and content. 99 * @param title The title of the TitledPane. 100 * @param content The content of the TitledPane. 101 */ 102 public TitledPane(String title, Node content) { 103 this(); 104 setText(title); 105 setContent(content); 106 } 107 108 109 /*************************************************************************** 110 * * 111 * Properties * 112 * * 113 **************************************************************************/ 114 115 // --- Content 116 private ObjectProperty<Node> content; 117 118 /** 119 * <p> The content of the TitlePane which can be any Node 120 * such as UI controls or groups of nodes added to a layout container. 121 * 122 * @param value The content for this TitlePane. 123 */ 124 public final void setContent(Node value) { 125 contentProperty().set(value); 126 } 127 128 /** 129 * The content of the TitledPane. {@code Null} is returned when 130 * if there is no content. 131 * 132 * @return The content of this TitledPane. 133 */ 134 public final Node getContent() { 135 return content == null ? null : content.get(); 136 } 137 138 /** 139 * The content of the TitledPane. 140 * 141 * @return The content of the TitlePane. 142 */ 143 public final ObjectProperty<Node> contentProperty() { 144 if (content == null) { 145 content = new SimpleObjectProperty<Node>(this, "content"); 146 } 147 return content; 148 } 149 150 151 // --- Expanded 152 private BooleanProperty expanded = new BooleanPropertyBase(true) { 153 @Override protected void invalidated() { 154 final boolean active = get(); 155 pseudoClassStateChanged(PSEUDO_CLASS_EXPANDED, active); 156 pseudoClassStateChanged(PSEUDO_CLASS_COLLAPSED, !active); 157 notifyAccessibleAttributeChanged(AccessibleAttribute.EXPANDED); 158 } 159 160 @Override 161 public Object getBean() { 162 return TitledPane.this; 163 } 164 165 @Override 166 public String getName() { 167 return "expanded"; 168 } 169 }; 170 171 /** 172 * Sets the expanded state of the TitledPane. The default is {@code true}. 173 * 174 */ 175 public final void setExpanded(boolean value) { expandedProperty().set(value); } 176 177 /* 178 * Returns the expanded state of the TitledPane. 179 * 180 * @return The expanded state of the TitledPane. 181 */ 182 public final boolean isExpanded() { return expanded.get(); } 183 184 /** 185 * The expanded state of the TitledPane. 186 */ 187 public final BooleanProperty expandedProperty() { return expanded; } 188 189 190 // --- Animated 191 private BooleanProperty animated = new StyleableBooleanProperty(true) { 192 193 @Override 194 public Object getBean() { 195 return TitledPane.this; 196 } 197 198 @Override 199 public String getName() { 200 return "animated"; 201 } 202 203 @Override 204 public CssMetaData<TitledPane,Boolean> getCssMetaData() { 205 return StyleableProperties.ANIMATED; 206 } 207 208 }; 209 210 /** 211 * Specifies how the TitledPane should open and close. The panel will be 212 * animated out when this value is set to {@code true}. The default is {@code true}. 213 * 214 */ 215 public final void setAnimated(boolean value) { animatedProperty().set(value); } 216 217 /** 218 * Returns the animated state of the TitledPane. 219 * 220 * @return The animated state of the TitledPane. 221 */ 222 public final boolean isAnimated() { return animated.get(); } 223 224 /** 225 * The animated state of the TitledPane. 226 */ 227 public final BooleanProperty animatedProperty() { return animated; } 228 229 230 // --- Collapsible 231 private BooleanProperty collapsible = new StyleableBooleanProperty(true) { 232 233 @Override 234 public Object getBean() { 235 return TitledPane.this; 236 } 237 238 @Override 239 public String getName() { 240 return "collapsible"; 241 } 242 243 @Override 244 public CssMetaData<TitledPane,Boolean> getCssMetaData() { 245 return StyleableProperties.COLLAPSIBLE; 246 } 247 248 }; 249 250 /** 251 * Specifies if the TitledPane can be collapsed. The default is {@code true}. 252 * 253 */ 254 public final void setCollapsible(boolean value) { collapsibleProperty().set(value); } 255 256 /** 257 * Returns the collapsible state of the TitlePane. 258 * 259 * @return The collapsible state of the TitledPane. 260 */ 261 public final boolean isCollapsible() { return collapsible.get(); } 262 263 /** 264 * The collapsible state of the TitledPane. 265 */ 266 public final BooleanProperty collapsibleProperty() { return collapsible; } 267 268 /*************************************************************************** 269 * * 270 * Methods * 271 * * 272 **************************************************************************/ 273 274 /** {@inheritDoc} */ 275 @Override protected Skin<?> createDefaultSkin() { 276 return new TitledPaneSkin(this); 277 } 278 279 /*************************************************************************** 280 * * 281 * Stylesheet Handling * 282 * * 283 **************************************************************************/ 284 285 private static final String DEFAULT_STYLE_CLASS = "titled-pane"; 286 287 private static final PseudoClass PSEUDO_CLASS_EXPANDED = 288 PseudoClass.getPseudoClass("expanded"); 289 private static final PseudoClass PSEUDO_CLASS_COLLAPSED = 290 PseudoClass.getPseudoClass("collapsed"); 291 292 293 private static class StyleableProperties { 294 295 private static final CssMetaData<TitledPane,Boolean> COLLAPSIBLE = 296 new CssMetaData<TitledPane,Boolean>("-fx-collapsible", 297 BooleanConverter.getInstance(), Boolean.TRUE) { 298 299 @Override 300 public boolean isSettable(TitledPane n) { 301 return n.collapsible == null || !n.collapsible.isBound(); 302 } 303 304 @Override 305 public StyleableProperty<Boolean> getStyleableProperty(TitledPane n) { 306 return (StyleableProperty<Boolean>)(WritableValue<Boolean>)n.collapsibleProperty(); 307 } 308 }; 309 310 private static final CssMetaData<TitledPane,Boolean> ANIMATED = 311 new CssMetaData<TitledPane,Boolean>("-fx-animated", 312 BooleanConverter.getInstance(), Boolean.TRUE) { 313 314 @Override 315 public boolean isSettable(TitledPane n) { 316 return n.animated == null || !n.animated.isBound(); 317 } 318 319 @Override 320 public StyleableProperty<Boolean> getStyleableProperty(TitledPane n) { 321 return (StyleableProperty<Boolean>)(WritableValue<Boolean>)n.animatedProperty(); 322 } 323 }; 324 325 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 326 static { 327 final List<CssMetaData<? extends Styleable, ?>> styleables = 328 new ArrayList<CssMetaData<? extends Styleable, ?>>(Labeled.getClassCssMetaData()); 329 styleables.add(COLLAPSIBLE); 330 styleables.add(ANIMATED); 331 STYLEABLES = Collections.unmodifiableList(styleables); 332 } 333 } 334 335 /** 336 * @return The CssMetaData associated with this class, which may include the 337 * CssMetaData of its super classes. 338 * @since JavaFX 8.0 339 */ 340 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 341 return StyleableProperties.STYLEABLES; 342 } 343 344 /** 345 * {@inheritDoc} 346 * @since JavaFX 8.0 347 */ 348 @Override 349 public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() { 350 return getClassCssMetaData(); 351 } 352 353 @Override 354 public Orientation getContentBias() { 355 final Node c = getContent(); 356 return c == null ? super.getContentBias() : c.getContentBias(); 357 } 358 359 360 /*************************************************************************** 361 * * 362 * Accessibility handling * 363 * * 364 **************************************************************************/ 365 366 @Override 367 public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { 368 switch (attribute) { 369 case TEXT: { 370 String accText = getAccessibleText(); 371 if (accText != null && !accText.isEmpty()) return accText; 372 return getText(); 373 } 374 case EXPANDED: return isExpanded(); 375 default: return super.queryAccessibleAttribute(attribute, parameters); 376 } 377 } 378 379 @Override 380 public void executeAccessibleAction(AccessibleAction action, Object... parameters) { 381 switch (action) { 382 case EXPAND: setExpanded(true); break; 383 case COLLAPSE: setExpanded(false); break; 384 default: super.executeAccessibleAction(action); 385 } 386 } 387 }