modules/controls/src/main/java/javafx/scene/control/skin/TitledPaneSkin.java

Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization

@@ -21,13 +21,15 @@
  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
 
-package com.sun.javafx.scene.control.skin;
+package javafx.scene.control.skin;
 
 import com.sun.javafx.PlatformUtil;
+import com.sun.javafx.scene.control.behavior.BehaviorBase;
+import com.sun.javafx.scene.control.skin.Utils;
 import javafx.animation.Animation.Status;
 import javafx.animation.Interpolator;
 import javafx.animation.KeyFrame;
 import javafx.animation.KeyValue;
 import javafx.animation.Timeline;

@@ -36,11 +38,13 @@
 import javafx.geometry.HPos;
 import javafx.geometry.Pos;
 import javafx.geometry.VPos;
 import javafx.scene.Cursor;
 import javafx.scene.Node;
+import javafx.scene.control.Button;
 import javafx.scene.control.ContentDisplay;
+import javafx.scene.control.Control;
 import javafx.scene.control.TitledPane;
 import javafx.scene.layout.StackPane;
 import javafx.scene.shape.Rectangle;
 import javafx.util.Duration;
 

@@ -51,31 +55,72 @@
 import javafx.scene.control.Labeled;
 import javafx.scene.control.ContextMenu;
 import javafx.scene.input.MouseButton;
 import javafx.scene.text.Font;
 
-public class TitledPaneSkin extends LabeledSkinBase<TitledPane, TitledPaneBehavior>  {
+/**
+ * Default skin implementation for the {@link TitledPane} control.
+ *
+ * @see TitledPane
+ * @since 9
+ */
+public class TitledPaneSkin extends LabeledSkinBase<TitledPane>  {
+
+    /***************************************************************************
+     *                                                                         *
+     * Static fields                                                           *
+     *                                                                         *
+     **************************************************************************/
 
-    public static final Duration TRANSITION_DURATION = new Duration(350.0);
+    private static final Duration TRANSITION_DURATION = new Duration(350.0);
 
     // caching results in poorer looking text (it is blurry), so we don't do it
     // unless on a low powered device (admittedly the test below isn't a great
     // indicator of power, but it'll do for now).
     private static final boolean CACHE_ANIMATION = PlatformUtil.isEmbedded();
 
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Private fields                                                          *
+     *                                                                         *
+     **************************************************************************/
+
+    private final TitledPaneBehavior behavior;
+
     private final TitleRegion titleRegion;
     private final StackPane contentContainer;
     private Node content;
     private Timeline timeline;
     private double transitionStartValue;
     private Rectangle clipRect;
     private Pos pos;
     private HPos hpos;
     private VPos vpos;
 
-    public TitledPaneSkin(final TitledPane titledPane) {
-        super(titledPane, new TitledPaneBehavior(titledPane));
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Constructors                                                            *
+     *                                                                         *
+     **************************************************************************/
+
+    /**
+     * Creates a new TitledPaneSkin instance, installing the necessary child
+     * nodes into the Control {@link Control#getChildren() children} list, as
+     * well as the necessary input mappings for handling key, mouse, etc events.
+     *
+     * @param control The control that this skin should be installed onto.
+     */
+    public TitledPaneSkin(final TitledPane control) {
+        super(control);
+
+        // install default input map for the TitledPane control
+        this.behavior = new TitledPaneBehavior(control);
+//        control.setInputMap(behavior.getInputMap());
 
         clipRect = new Rectangle();
 
         transitionStartValue = 0;
         titleRegion = new TitleRegion();

@@ -90,120 +135,100 @@
                 }
             }
         };
         contentContainer.setClip(clipRect);
 
-        if (titledPane.isExpanded()) {
+        if (control.isExpanded()) {
             setTransition(1.0f);
-            setExpanded(titledPane.isExpanded());
+            setExpanded(control.isExpanded());
         } else {
             setTransition(0.0f);
             if (content != null) {
                 content.setVisible(false);
             }
         }
 
         getChildren().setAll(contentContainer, titleRegion);
 
-        registerChangeListener(titledPane.contentProperty(), "CONTENT");
-        registerChangeListener(titledPane.expandedProperty(), "EXPANDED");
-        registerChangeListener(titledPane.collapsibleProperty(), "COLLAPSIBLE");
-        registerChangeListener(titledPane.alignmentProperty(), "ALIGNMENT");
-        registerChangeListener(titledPane.widthProperty(), "WIDTH");
-        registerChangeListener(titledPane.heightProperty(), "HEIGHT");
-        registerChangeListener(titleRegion.alignmentProperty(), "TITLE_REGION_ALIGNMENT");
-
-        pos = titledPane.getAlignment();
-        hpos = pos == null ? HPos.LEFT   : pos.getHpos();
-        vpos = pos == null ? VPos.CENTER : pos.getVpos();
-    }
-
-    public StackPane getContentContainer() {
-        return contentContainer;
-    }
-
-    @Override
-    protected void handleControlPropertyChanged(String property) {
-        super.handleControlPropertyChanged(property);
-        if ("CONTENT".equals(property)) {
+        registerChangeListener(control.contentProperty(), e -> {
             content = getSkinnable().getContent();
             if (content == null) {
                 contentContainer.getChildren().clear();
             } else {
                 contentContainer.getChildren().setAll(content);
             }
-        } else if ("EXPANDED".equals(property)) {
-            setExpanded(getSkinnable().isExpanded());
-        } else if ("COLLAPSIBLE".equals(property)) {
-            titleRegion.update();
-        } else if ("ALIGNMENT".equals(property)) {
+        });
+        registerChangeListener(control.expandedProperty(), e -> setExpanded(getSkinnable().isExpanded()));
+        registerChangeListener(control.collapsibleProperty(), e -> titleRegion.update());
+        registerChangeListener(control.alignmentProperty(), e -> {
             pos = getSkinnable().getAlignment();
             hpos = pos.getHpos();
             vpos = pos.getVpos();
-        } else if ("TITLE_REGION_ALIGNMENT".equals(property)) {
+        });
+        registerChangeListener(control.widthProperty(), e -> clipRect.setWidth(getSkinnable().getWidth()));
+        registerChangeListener(control.heightProperty(), e -> clipRect.setHeight(contentContainer.getHeight()));
+        registerChangeListener(titleRegion.alignmentProperty(), e -> {
             pos = titleRegion.getAlignment();
             hpos = pos.getHpos();
             vpos = pos.getVpos();
-        } else if ("WIDTH".equals(property)) {
-            clipRect.setWidth(getSkinnable().getWidth());
-        } else if ("HEIGHT".equals(property)) {
-            clipRect.setHeight(contentContainer.getHeight());
-        } else if ("GRAPHIC_TEXT_GAP".equals(property)) {
-            titleRegion.requestLayout();            
-        }
-    }
+        });
 
-    // Override LabeledSkinBase updateChildren because
-    // it removes all the children.  The update() in TitleRegion
-    // will replace this method.
-    @Override protected void updateChildren() {
-        if (titleRegion != null) {
-            titleRegion.update();
-        }
+        pos = control.getAlignment();
+        hpos = pos == null ? HPos.LEFT   : pos.getHpos();
+        vpos = pos == null ? VPos.CENTER : pos.getVpos();
     }
 
-    private void setExpanded(boolean expanded) {
-        if (! getSkinnable().isCollapsible()) {
-            setTransition(1.0f);
-            return;
-        }
 
-        // we need to perform the transition between expanded / hidden
-        if (getSkinnable().isAnimated()) {
-            transitionStartValue = getTransition();
-            doAnimationTransition();
-        } else {
-            if (expanded) {
-                setTransition(1.0f);
-            } else {
-                setTransition(0.0f);
-            }
-            if (content != null) {
-                content.setVisible(expanded);
-             }
-            getSkinnable().requestLayout();
-        }
-    }
+
+    /***************************************************************************
+     *                                                                         *
+     * Properties                                                              *
+     *                                                                         *
+     **************************************************************************/
 
     private DoubleProperty transition;
-    private void setTransition(double value) { transitionProperty().set(value); }
-    private double getTransition() { return transition == null ? 0.0 : transition.get(); }
-    private DoubleProperty transitionProperty() {
+    private final void setTransition(double value) { transitionProperty().set(value); }
+    private final double getTransition() { return transition == null ? 0.0 : transition.get(); }
+    private final DoubleProperty transitionProperty() {
         if (transition == null) {
             transition = new SimpleDoubleProperty(this, "transition", 0.0) {
                 @Override protected void invalidated() {
                     contentContainer.requestLayout();
                 }
             };
         }
         return transition;
     }
 
-    private boolean isInsideAccordion() {
-        return getSkinnable().getParent() != null && getSkinnable().getParent() instanceof Accordion;
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Public API                                                              *
+     *                                                                         *
+     **************************************************************************/
+
+    /** {@inheritDoc} */
+    @Override public void dispose() {
+        super.dispose();
+
+        if (behavior != null) {
+            behavior.dispose();
+        }
+    }
+
+    // Override LabeledSkinBase updateChildren because
+    // it removes all the children.  The update() in TitleRegion
+    // will replace this method.
+    /** {@inheritDoc} */
+    @Override protected void updateChildren() {
+        if (titleRegion != null) {
+            titleRegion.update();
+        }
     }
 
+    /** {@inheritDoc} */
     @Override protected void layoutChildren(final double x, double y,
             final double w, final double h) {
         
         // header
         double headerHeight = snapSize(titleRegion.prefHeight(-1));

@@ -226,38 +251,78 @@
         clipRect.setHeight(contentHeight);
         positionInArea(contentContainer, x, y,
             w, contentHeight, /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
     }
 
+    /** {@inheritDoc} */
     @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
         double titleWidth = snapSize(titleRegion.prefWidth(height));
         double contentWidth = snapSize(contentContainer.minWidth(height));
         return Math.max(titleWidth, contentWidth) + leftInset + rightInset;
     }
 
+    /** {@inheritDoc} */
     @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
         double headerHeight = snapSize(titleRegion.prefHeight(width));
         double contentHeight = contentContainer.minHeight(width) * getTransition();
         return headerHeight + snapSize(contentHeight) + topInset + bottomInset;
     }
 
+    /** {@inheritDoc} */
     @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
         double titleWidth = snapSize(titleRegion.prefWidth(height));
         double contentWidth = snapSize(contentContainer.prefWidth(height));
         return Math.max(titleWidth, contentWidth) + leftInset + rightInset;
     }
 
+    /** {@inheritDoc} */
     @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
         double headerHeight = snapSize(titleRegion.prefHeight(width));
         double contentHeight = contentContainer.prefHeight(width) * getTransition();
         return headerHeight + snapSize(contentHeight) + topInset + bottomInset;
     }
 
+    /** {@inheritDoc} */
     @Override protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
         return Double.MAX_VALUE;
     }
 
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Private implementation                                                  *
+     *                                                                         *
+     **************************************************************************/
+
+    private void setExpanded(boolean expanded) {
+        if (! getSkinnable().isCollapsible()) {
+            setTransition(1.0f);
+            return;
+        }
+
+        // we need to perform the transition between expanded / hidden
+        if (getSkinnable().isAnimated()) {
+            transitionStartValue = getTransition();
+            doAnimationTransition();
+        } else {
+            if (expanded) {
+                setTransition(1.0f);
+            } else {
+                setTransition(0.0f);
+            }
+            if (content != null) {
+                content.setVisible(expanded);
+             }
+            getSkinnable().requestLayout();
+        }
+    }
+
+    private boolean isInsideAccordion() {
+        return getSkinnable().getParent() != null && getSkinnable().getParent() instanceof Accordion;
+    }
+
     double getTitleRegionSize(double width) {
         return snapSize(titleRegion.prefHeight(width)) + snappedTopInset() + snappedBottomInset();
     }
 
     private double prefHeightFromAccordion = 0;

@@ -332,10 +397,18 @@
 
         timeline.getKeyFrames().setAll(k1, k2);
         timeline.play();
     }
 
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Support classes                                                         *
+     *                                                                         *
+     **************************************************************************/
+
     class TitleRegion extends StackPane {
         private final StackPane arrowRegion;
 
         public TitleRegion() {
             getStyleClass().setAll("title");

@@ -364,11 +437,11 @@
                 ContextMenu contextMenu = getSkinnable().getContextMenu() ;
                 if (contextMenu != null) {
                     contextMenu.hide() ;
                 }
                 if (getSkinnable().isCollapsible() && getSkinnable().isFocused()) {
-                    getBehavior().toggle();
+                    behavior.toggle();
                 }
             });
 
             // title region consists of the title and the arrow regions
             update();