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();