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 }