diff -r ed8e447fe983 -r 85c73e7d5e14 javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TabPaneSkin.java --- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TabPaneSkin.java Wed Jan 02 12:47:04 2013 +0100 +++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TabPaneSkin.java Thu Jan 03 00:23:26 2013 +0100 @@ -25,7 +25,11 @@ package com.sun.javafx.scene.control.skin; import com.sun.javafx.PlatformUtil; +import com.sun.javafx.css.CssMetaData; import com.sun.javafx.css.PseudoClass; +import com.sun.javafx.css.StyleableObjectProperty; +import com.sun.javafx.css.converters.EnumConverter; + import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; @@ -34,7 +38,10 @@ import javafx.beans.Observable; import javafx.beans.property.DoubleProperty; import javafx.beans.property.DoublePropertyBase; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.WritableValue; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; @@ -48,9 +55,11 @@ import javafx.geometry.Side; import javafx.geometry.VPos; import javafx.scene.Node; +import javafx.scene.control.Cell; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; +import javafx.scene.control.SkinBase; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.control.TabPane.TabClosingPolicy; @@ -74,6 +83,7 @@ import javafx.scene.control.ToggleGroup; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -81,7 +91,12 @@ import javafx.scene.input.*; public class TabPaneSkin extends BehaviorSkinBase { - + public enum TabAnimation { + NONE, + GROW + // In future we could add FADE, ... + } + private static int getRotation(Side pos) { switch (pos) { case TOP: @@ -129,7 +144,43 @@ private Tab selectedTab; private Tab previousSelectedTab; private boolean isSelectingTab; + private ObjectProperty openTabAnimation = new StyleableObjectProperty() { + @Override + public CssMetaData getCssMetaData() { + return StyleableProperties.OPEN_TAB_ANIMATION; + } + + @Override + public Object getBean() { + return TabPaneSkin.this; + } + + @Override + public String getName() { + return "openTabAnimation"; + } + + }; + private ObjectProperty closeTabAnimation = new StyleableObjectProperty() { + + @Override + public CssMetaData getCssMetaData() { + return StyleableProperties.CLOSE_TAB_ANIMATION; + } + + @Override + public Object getBean() { + return TabPaneSkin.this; + } + + @Override + public String getName() { + return "closeTabAnimation"; + } + + }; + public TabPaneSkin(TabPane tabPane) { super(tabPane, new TabPaneBehavior(tabPane)); @@ -199,30 +250,37 @@ } } - private Map closedTab = new HashMap(); - private void initializeTabListener() { getSkinnable().getTabs().addListener(new ListChangeListener() { - @Override public void onChanged(final Change c) { + @Override public void onChanged(final Change c) { + final Map closedTab = new HashMap<>(); while (c.next()) { for (final Tab tab : c.getRemoved()) { // Animate the tab removal final TabHeaderSkin tabRegion = tabHeaderArea.getTabHeaderSkin(tab); Timeline closedTabTimeline = null; if (tabRegion != null) { - tabRegion.animating = true; - closedTabTimeline = createTimeline(tabRegion, Duration.millis(ANIMATION_SPEED * 1.5F), 0.0F, new EventHandler() { + if( closeTabAnimation.get() == TabAnimation.GROW ) { + tabRegion.animating = true; + closedTabTimeline = createTimeline(tabRegion, Duration.millis(ANIMATION_SPEED * 1.5F), 0.0F, new EventHandler() { - @Override - public void handle(ActionEvent event) { - removeTab(tab); - closedTab.remove(tab); - if (getSkinnable().getTabs().isEmpty()) { - tabHeaderArea.setVisible(false); + @Override + public void handle(ActionEvent event) { + removeTab(tab); + closedTab.remove(tab); + if (getSkinnable().getTabs().isEmpty()) { + tabHeaderArea.setVisible(false); + } } + }); + closedTabTimeline.play(); + } else { + removeTab(tab); + closedTab.remove(tab); + if (getSkinnable().getTabs().isEmpty()) { + tabHeaderArea.setVisible(false); } - }); - closedTabTimeline.play(); + } } closedTab.put(tab, closedTabTimeline); } @@ -251,24 +309,28 @@ addTabContent(tab); final TabHeaderSkin tabRegion = tabHeaderArea.getTabHeaderSkin(tab); if (tabRegion != null) { - tabRegion.animateNewTab = new Runnable() { + if( openTabAnimation.get() == TabAnimation.GROW ) { + tabRegion.animateNewTab = new Runnable() { - @Override - public void run() { - final double w = snapSize(tabRegion.prefWidth(-1)); - tabRegion.animating = true; - tabRegion.prefWidth.set(0.0); - tabRegion.setVisible(true); - createTimeline(tabRegion, Duration.millis(ANIMATION_SPEED), w, new EventHandler() { + @Override + public void run() { + final double w = snapSize(tabRegion.prefWidth(-1)); + tabRegion.animating = true; + tabRegion.prefWidth.set(0.0); + tabRegion.setVisible(true); + createTimeline(tabRegion, Duration.millis(ANIMATION_SPEED), w, new EventHandler() { - @Override - public void handle(ActionEvent event) { - tabRegion.animating = false; - tabRegion.inner.requestLayout(); - } - }).play(); - } - }; + @Override + public void handle(ActionEvent event) { + tabRegion.animating = false; + tabRegion.inner.requestLayout(); + } + }).play(); + } + }; + } else { + tabRegion.setVisible(true); + } } } } @@ -479,6 +541,71 @@ } } } + + /** + * Super-lazy instantiation pattern from Bill Pugh. + * @treatAsPrivate implementation detail + */ + private static class StyleableProperties { + private static final List STYLEABLES; + + private final static CssMetaData OPEN_TAB_ANIMATION = new CssMetaData("-fx-open-tab-animation", new EnumConverter(TabAnimation.class), TabAnimation.GROW) { + + @Override + public boolean isSettable(TabPane node) { + return true; + } + + @Override + public WritableValue getWritableValue(TabPane node) { + TabPaneSkin skin = (TabPaneSkin) node.getSkin(); + return skin.openTabAnimation; + } + + }; + + private final static CssMetaData CLOSE_TAB_ANIMATION = new CssMetaData("-fx-close-tab-animation", new EnumConverter(TabAnimation.class), TabAnimation.GROW) { + + @Override + public boolean isSettable(TabPane node) { + return true; + } + + @Override + public WritableValue getWritableValue(TabPane node) { + TabPaneSkin skin = (TabPaneSkin) node.getSkin(); + return skin.openTabAnimation; + } + + }; + + static { + + final List styleables = + new ArrayList(SkinBase.getClassCssMetaData()); + Collections.addAll(styleables, + OPEN_TAB_ANIMATION, + CLOSE_TAB_ANIMATION + ); + STYLEABLES = Collections.unmodifiableList(styleables); + + } + } + + /** + * @return The CssMetaData associated with this class, which may include the + * CssMetaData of its super classes. + */ + public static List getClassCssMetaData() { + return StyleableProperties.STYLEABLES; + } + + /** + * {@inheritDoc} + */ + @Override public List getCssMetaData() { + return getClassCssMetaData(); + } /************************************************************************** * diff -r ed8e447fe983 -r 85c73e7d5e14 javafx-ui-controls/src/javafx/scene/control/TabPane.java --- a/javafx-ui-controls/src/javafx/scene/control/TabPane.java Wed Jan 02 12:47:04 2013 +0100 +++ b/javafx-ui-controls/src/javafx/scene/control/TabPane.java Thu Jan 03 00:23:26 2013 +0100 @@ -579,9 +579,18 @@ /** * {@inheritDoc} */ - @Override - public List getCssMetaData() { - return getClassCssMetaData(); + @Override public List getCssMetaData() { + List meta = getClassCssMetaData(); + Skin skin = getSkin(); + if( skin instanceof SkinBase ) { + List skinMeta = ((SkinBase)skin).getCssMetaData(); + List rv = new ArrayList<>(meta.size()+skinMeta.size()); + rv.addAll(meta); + rv.addAll(skinMeta); + return rv; + } else { + return meta; + } } private static final PseudoClass.State TOP_PSEUDOCLASS_STATE = PseudoClass.getState("top");