modules/controls/src/main/java/javafx/scene/control/skin/TabPaneSkin.java
Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization
@@ -21,12 +21,14 @@
* 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.scene.control.LambdaMultiplePropertyChangeListenerHandler;
+import com.sun.javafx.scene.control.Properties;
import com.sun.javafx.util.Utils;
import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
@@ -56,10 +58,11 @@
import javafx.scene.AccessibleAction;
import javafx.scene.AccessibleAttribute;
import javafx.scene.AccessibleRole;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
+import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.SkinBase;
import javafx.scene.control.Tab;
@@ -84,107 +87,96 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
-import com.sun.javafx.css.converters.EnumConverter;
-import com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler;
+import javafx.css.converter.EnumConverter;
import com.sun.javafx.scene.control.behavior.TabPaneBehavior;
import com.sun.javafx.scene.traversal.Direction;
import com.sun.javafx.scene.traversal.TraversalEngine;
import static com.sun.javafx.scene.control.skin.resources.ControlResources.getString;
-public class TabPaneSkin extends BehaviorSkinBase<TabPane, TabPaneBehavior> {
- private static enum TabAnimation {
+/**
+ * Default skin implementation for the {@link TabPane} control.
+ *
+ * @see TabPane
+ * @since 9
+ */
+public class TabPaneSkin extends SkinBase<TabPane> {
+
+ /***************************************************************************
+ * *
+ * Enums *
+ * *
+ **************************************************************************/
+
+ private enum TabAnimation {
NONE,
GROW
// In future we could add FADE, ...
}
private enum TabAnimationState {
SHOWING, HIDING, NONE;
}
- private ObjectProperty<TabAnimation> openTabAnimation = new StyleableObjectProperty<TabAnimation>(TabAnimation.GROW) {
- @Override public CssMetaData<TabPane,TabAnimation> getCssMetaData() {
- return StyleableProperties.OPEN_TAB_ANIMATION;
- }
- @Override public Object getBean() {
- return TabPaneSkin.this;
- }
- @Override public String getName() {
- return "openTabAnimation";
- }
- };
+ /***************************************************************************
+ * *
+ * Static fields *
+ * *
+ **************************************************************************/
- private ObjectProperty<TabAnimation> closeTabAnimation = new StyleableObjectProperty<TabAnimation>(TabAnimation.GROW) {
- @Override public CssMetaData<TabPane,TabAnimation> getCssMetaData() {
- return StyleableProperties.CLOSE_TAB_ANIMATION;
- }
+ static int CLOSE_BTN_SIZE = 16;
- @Override public Object getBean() {
- return TabPaneSkin.this;
- }
- @Override public String getName() {
- return "closeTabAnimation";
- }
- };
- private static int getRotation(Side pos) {
- switch (pos) {
- case TOP:
- return 0;
- case BOTTOM:
- return 180;
- case LEFT:
- return -90;
- case RIGHT:
- return 90;
- default:
- return 0;
- }
- }
+ /***************************************************************************
+ * *
+ * Private fields *
+ * *
+ **************************************************************************/
- /**
- * VERY HACKY - this lets us 'duplicate' Label and ImageView nodes to be used in a
- * Tab and the tabs menu at the same time.
- */
- private static Node clone(Node n) {
- if (n == null) {
- return null;
- }
- if (n instanceof ImageView) {
- ImageView iv = (ImageView) n;
- ImageView imageview = new ImageView();
- imageview.setImage(iv.getImage());
- return imageview;
- }
- if (n instanceof Label) {
- Label l = (Label)n;
- Label label = new Label(l.getText(), l.getGraphic());
- return label;
- }
- return null;
- }
private static final double ANIMATION_SPEED = 150;
private static final int SPACER = 10;
private TabHeaderArea tabHeaderArea;
private ObservableList<TabContentRegion> tabContentRegions;
private Rectangle clipRect;
private Rectangle tabHeaderAreaClipRect;
private Tab selectedTab;
private boolean isSelectingTab;
+ private double maxw = 0.0d;
+ private double maxh = 0.0d;
+
+ private final TabPaneBehavior behavior;
+
+
- public TabPaneSkin(TabPane tabPane) {
- super(tabPane, new TabPaneBehavior(tabPane));
+ /***************************************************************************
+ * *
+ * Constructors *
+ * *
+ **************************************************************************/
+
+ /**
+ * Creates a new TabPaneSkin 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 TabPaneSkin(TabPane control) {
+ super(control);
- clipRect = new Rectangle(tabPane.getWidth(), tabPane.getHeight());
+ // install default input map for the TabPane control
+ this.behavior = new TabPaneBehavior(control);
+// control.setInputMap(behavior.getInputMap());
+
+ clipRect = new Rectangle(control.getWidth(), control.getHeight());
getSkinnable().setClip(clipRect);
tabContentRegions = FXCollections.<TabContentRegion>observableArrayList();
for (Tab tab : getSkinnable().getTabs()) {
@@ -199,14 +191,18 @@
tabHeaderArea.setVisible(false);
}
initializeTabListener();
- registerChangeListener(tabPane.getSelectionModel().selectedItemProperty(), "SELECTED_TAB");
- registerChangeListener(tabPane.sideProperty(), "SIDE");
- registerChangeListener(tabPane.widthProperty(), "WIDTH");
- registerChangeListener(tabPane.heightProperty(), "HEIGHT");
+ registerChangeListener(control.getSelectionModel().selectedItemProperty(), e -> {
+ isSelectingTab = true;
+ selectedTab = getSkinnable().getSelectionModel().getSelectedItem();
+ getSkinnable().requestLayout();
+ });
+ registerChangeListener(control.sideProperty(), e -> updateTabPosition());
+ registerChangeListener(control.widthProperty(), e -> clipRect.setWidth(getSkinnable().getWidth()));
+ registerChangeListener(control.heightProperty(), e -> clipRect.setHeight(getSkinnable().getHeight()));
selectedTab = getSkinnable().getSelectionModel().getSelectedItem();
// Could not find the selected tab try and get the selected tab using the selected index
if (selectedTab == null && getSkinnable().getSelectionModel().getSelectedIndex() != -1) {
getSkinnable().getSelectionModel().select(getSkinnable().getSelectionModel().getSelectedIndex());
@@ -219,34 +215,247 @@
selectedTab = getSkinnable().getSelectionModel().getSelectedItem();
isSelectingTab = false;
initializeSwipeHandlers();
}
-
- public StackPane getSelectedTabContentRegion() {
- for (TabContentRegion contentRegion : tabContentRegions) {
- if (contentRegion.getTab().equals(selectedTab)) {
- return contentRegion;
+
+
+
+ /***************************************************************************
+ * *
+ * Properties *
+ * *
+ **************************************************************************/
+
+ private ObjectProperty<TabAnimation> openTabAnimation = new StyleableObjectProperty<TabAnimation>(TabAnimation.GROW) {
+ @Override public CssMetaData<TabPane,TabAnimation> getCssMetaData() {
+ return StyleableProperties.OPEN_TAB_ANIMATION;
+ }
+
+ @Override public Object getBean() {
+ return TabPaneSkin.this;
+ }
+
+ @Override public String getName() {
+ return "openTabAnimation";
+ }
+ };
+
+ private ObjectProperty<TabAnimation> closeTabAnimation = new StyleableObjectProperty<TabAnimation>(TabAnimation.GROW) {
+ @Override public CssMetaData<TabPane,TabAnimation> getCssMetaData() {
+ return StyleableProperties.CLOSE_TAB_ANIMATION;
+ }
+
+ @Override public Object getBean() {
+ return TabPaneSkin.this;
+ }
+
+ @Override public String getName() {
+ return "closeTabAnimation";
+ }
+ };
+
+
+
+ /***************************************************************************
+ * *
+ * Public API *
+ * *
+ **************************************************************************/
+
+ /** {@inheritDoc} */
+ @Override public void dispose() {
+ super.dispose();
+
+ if (behavior != null) {
+ behavior.dispose();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+ // The TabPane can only be as wide as it widest content width.
+ for (TabContentRegion contentRegion: tabContentRegions) {
+ maxw = Math.max(maxw, snapSize(contentRegion.prefWidth(-1)));
+ }
+
+ final boolean isHorizontal = isHorizontal();
+ final double tabHeaderAreaSize = snapSize(isHorizontal ?
+ tabHeaderArea.prefWidth(-1) : tabHeaderArea.prefHeight(-1));
+
+ double prefWidth = isHorizontal ?
+ Math.max(maxw, tabHeaderAreaSize) : maxw + tabHeaderAreaSize;
+ return snapSize(prefWidth) + rightInset + leftInset;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+ // The TabPane can only be as high as it highest content height.
+ for (TabContentRegion contentRegion: tabContentRegions) {
+ maxh = Math.max(maxh, snapSize(contentRegion.prefHeight(-1)));
+ }
+
+ final boolean isHorizontal = isHorizontal();
+ final double tabHeaderAreaSize = snapSize(isHorizontal ?
+ tabHeaderArea.prefHeight(-1) : tabHeaderArea.prefWidth(-1));
+
+ double prefHeight = isHorizontal ?
+ maxh + snapSize(tabHeaderAreaSize) : Math.max(maxh, tabHeaderAreaSize);
+ return snapSize(prefHeight) + topInset + bottomInset;
+ }
+
+ /** {@inheritDoc} */
+ @Override public double computeBaselineOffset(double topInset, double rightInset, double bottomInset, double leftInset) {
+ Side tabPosition = getSkinnable().getSide();
+ if (tabPosition == Side.TOP) {
+ return tabHeaderArea.getBaselineOffset() + topInset;
+ }
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void layoutChildren(final double x, final double y,
+ final double w, final double h) {
+ TabPane tabPane = getSkinnable();
+ Side tabPosition = tabPane.getSide();
+
+ double headerHeight = snapSize(tabHeaderArea.prefHeight(-1));
+ double tabsStartX = tabPosition.equals(Side.RIGHT)? x + w - headerHeight : x;
+ double tabsStartY = tabPosition.equals(Side.BOTTOM)? y + h - headerHeight : y;
+
+ if (tabPosition == Side.TOP) {
+ tabHeaderArea.resize(w, headerHeight);
+ tabHeaderArea.relocate(tabsStartX, tabsStartY);
+ tabHeaderArea.getTransforms().clear();
+ tabHeaderArea.getTransforms().add(new Rotate(getRotation(Side.TOP)));
+ } else if (tabPosition == Side.BOTTOM) {
+ tabHeaderArea.resize(w, headerHeight);
+ tabHeaderArea.relocate(w, tabsStartY - headerHeight);
+ tabHeaderArea.getTransforms().clear();
+ tabHeaderArea.getTransforms().add(new Rotate(getRotation(Side.BOTTOM), 0, headerHeight));
+ } else if (tabPosition == Side.LEFT) {
+ tabHeaderArea.resize(h, headerHeight);
+ tabHeaderArea.relocate(tabsStartX + headerHeight, h - headerHeight);
+ tabHeaderArea.getTransforms().clear();
+ tabHeaderArea.getTransforms().add(new Rotate(getRotation(Side.LEFT), 0, headerHeight));
+ } else if (tabPosition == Side.RIGHT) {
+ tabHeaderArea.resize(h, headerHeight);
+ tabHeaderArea.relocate(tabsStartX, y - headerHeight);
+ tabHeaderArea.getTransforms().clear();
+ tabHeaderArea.getTransforms().add(new Rotate(getRotation(Side.RIGHT), 0, headerHeight));
+ }
+
+ tabHeaderAreaClipRect.setX(0);
+ tabHeaderAreaClipRect.setY(0);
+ if (isHorizontal()) {
+ tabHeaderAreaClipRect.setWidth(w);
+ } else {
+ tabHeaderAreaClipRect.setWidth(h);
+ }
+ tabHeaderAreaClipRect.setHeight(headerHeight);
+
+ // ==================================
+ // position the tab content for the selected tab only
+ // ==================================
+ // if the tabs are on the left, the content needs to be indented
+ double contentStartX = 0;
+ double contentStartY = 0;
+
+ if (tabPosition == Side.TOP) {
+ contentStartX = x;
+ contentStartY = y + headerHeight;
+ if (isFloatingStyleClass()) {
+ // This is to hide the top border content
+ contentStartY -= 1;
+ }
+ } else if (tabPosition == Side.BOTTOM) {
+ contentStartX = x;
+ contentStartY = y;
+ if (isFloatingStyleClass()) {
+ // This is to hide the bottom border content
+ contentStartY = 1;
+ }
+ } else if (tabPosition == Side.LEFT) {
+ contentStartX = x + headerHeight;
+ contentStartY = y;
+ if (isFloatingStyleClass()) {
+ // This is to hide the left border content
+ contentStartX -= 1;
+ }
+ } else if (tabPosition == Side.RIGHT) {
+ contentStartX = x;
+ contentStartY = y;
+ if (isFloatingStyleClass()) {
+ // This is to hide the right border content
+ contentStartX = 1;
+ }
+ }
+
+ double contentWidth = w - (isHorizontal() ? 0 : headerHeight);
+ double contentHeight = h - (isHorizontal() ? headerHeight: 0);
+
+ for (int i = 0, max = tabContentRegions.size(); i < max; i++) {
+ TabContentRegion tabContent = tabContentRegions.get(i);
+
+ tabContent.setAlignment(Pos.TOP_LEFT);
+ if (tabContent.getClip() != null) {
+ ((Rectangle)tabContent.getClip()).setWidth(contentWidth);
+ ((Rectangle)tabContent.getClip()).setHeight(contentHeight);
+ }
+
+ // we need to size all tabs, even if they aren't visible. For example,
+ // see RT-29167
+ tabContent.resize(contentWidth, contentHeight);
+ tabContent.relocate(contentStartX, contentStartY);
+ }
+ }
+
+
+
+ /***************************************************************************
+ * *
+ * Private implementation *
+ * *
+ **************************************************************************/
+
+ private static int getRotation(Side pos) {
+ switch (pos) {
+ case TOP:
+ return 0;
+ case BOTTOM:
+ return 180;
+ case LEFT:
+ return -90;
+ case RIGHT:
+ return 90;
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * VERY HACKY - this lets us 'duplicate' Label and ImageView nodes to be used in a
+ * Tab and the tabs menu at the same time.
+ */
+ private static Node clone(Node n) {
+ if (n == null) {
+ return null;
+ }
+ if (n instanceof ImageView) {
+ ImageView iv = (ImageView) n;
+ ImageView imageview = new ImageView();
+ imageview.setImage(iv.getImage());
+ return imageview;
}
+ if (n instanceof Label) {
+ Label l = (Label)n;
+ Label label = new Label(l.getText(), l.getGraphic());
+ return label;
}
return null;
}
- @Override protected void handleControlPropertyChanged(String property) {
- super.handleControlPropertyChanged(property);
- if ("SELECTED_TAB".equals(property)) {
- isSelectingTab = true;
- selectedTab = getSkinnable().getSelectionModel().getSelectedItem();
- getSkinnable().requestLayout();
- } else if ("SIDE".equals(property)) {
- updateTabPosition();
- } else if ("WIDTH".equals(property)) {
- clipRect.setWidth(getSkinnable().getWidth());
- } else if ("HEIGHT".equals(property)) {
- clipRect.setHeight(getSkinnable().getHeight());
- }
- }
private void removeTabs(List<? extends Tab> removedList) {
for (final Tab tab : removedList) {
stopCurrentAnimation(tab);
// Animate the tab removal
final TabHeaderSkin tabRegion = tabHeaderArea.getTabHeaderSkin(tab);
@@ -401,11 +610,11 @@
// now only remove the tabs that are not in the tabsToAdd list
tabsToRemove.removeAll(tabsToAdd);
removeTabs(tabsToRemove);
// and add in any new tabs (that we don't already have showing)
- if (! tabsToAdd.isEmpty()) {
+ if (!tabsToAdd.isEmpty()) {
for (TabContentRegion tabContentRegion : tabContentRegions) {
Tab tab = tabContentRegion.getTab();
TabHeaderSkin tabHeader = tabHeaderArea.getTabHeaderSkin(tab);
if (!tabHeader.isClosing && tabsToAdd.contains(tabContentRegion.getTab())) {
tabsToAdd.remove(tabContentRegion.getTab());
@@ -461,162 +670,33 @@
Side tabPosition = getSkinnable().getSide();
return Side.TOP.equals(tabPosition) || Side.BOTTOM.equals(tabPosition);
}
private void initializeSwipeHandlers() {
- if (IS_TOUCH_SUPPORTED) {
+ if (Properties.IS_TOUCH_SUPPORTED) {
getSkinnable().addEventHandler(SwipeEvent.SWIPE_LEFT, t -> {
- getBehavior().selectNextTab();
+ behavior.selectNextTab();
});
getSkinnable().addEventHandler(SwipeEvent.SWIPE_RIGHT, t -> {
- getBehavior().selectPreviousTab();
+ behavior.selectPreviousTab();
});
}
}
//TODO need to cache this.
private boolean isFloatingStyleClass() {
return getSkinnable().getStyleClass().contains(TabPane.STYLE_CLASS_FLOATING);
}
- private double maxw = 0.0d;
- @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
- // The TabPane can only be as wide as it widest content width.
- for (TabContentRegion contentRegion: tabContentRegions) {
- maxw = Math.max(maxw, snapSize(contentRegion.prefWidth(-1)));
- }
-
- final boolean isHorizontal = isHorizontal();
- final double tabHeaderAreaSize = snapSize(isHorizontal ?
- tabHeaderArea.prefWidth(-1) : tabHeaderArea.prefHeight(-1));
-
- double prefWidth = isHorizontal ?
- Math.max(maxw, tabHeaderAreaSize) : maxw + tabHeaderAreaSize;
- return snapSize(prefWidth) + rightInset + leftInset;
- }
-
- private double maxh = 0.0d;
- @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
- // The TabPane can only be as high as it highest content height.
- for (TabContentRegion contentRegion: tabContentRegions) {
- maxh = Math.max(maxh, snapSize(contentRegion.prefHeight(-1)));
- }
-
- final boolean isHorizontal = isHorizontal();
- final double tabHeaderAreaSize = snapSize(isHorizontal ?
- tabHeaderArea.prefHeight(-1) : tabHeaderArea.prefWidth(-1));
-
- double prefHeight = isHorizontal ?
- maxh + snapSize(tabHeaderAreaSize) : Math.max(maxh, tabHeaderAreaSize);
- return snapSize(prefHeight) + topInset + bottomInset;
- }
-
- @Override public double computeBaselineOffset(double topInset, double rightInset, double bottomInset, double leftInset) {
- Side tabPosition = getSkinnable().getSide();
- if (tabPosition == Side.TOP) {
- return tabHeaderArea.getBaselineOffset() + topInset;
- }
- return 0;
- }
-
- @Override protected void layoutChildren(final double x, final double y,
- final double w, final double h) {
- TabPane tabPane = getSkinnable();
- Side tabPosition = tabPane.getSide();
-
- double headerHeight = snapSize(tabHeaderArea.prefHeight(-1));
- double tabsStartX = tabPosition.equals(Side.RIGHT)? x + w - headerHeight : x;
- double tabsStartY = tabPosition.equals(Side.BOTTOM)? y + h - headerHeight : y;
-
- if (tabPosition == Side.TOP) {
- tabHeaderArea.resize(w, headerHeight);
- tabHeaderArea.relocate(tabsStartX, tabsStartY);
- tabHeaderArea.getTransforms().clear();
- tabHeaderArea.getTransforms().add(new Rotate(getRotation(Side.TOP)));
- } else if (tabPosition == Side.BOTTOM) {
- tabHeaderArea.resize(w, headerHeight);
- tabHeaderArea.relocate(w, tabsStartY - headerHeight);
- tabHeaderArea.getTransforms().clear();
- tabHeaderArea.getTransforms().add(new Rotate(getRotation(Side.BOTTOM), 0, headerHeight));
- } else if (tabPosition == Side.LEFT) {
- tabHeaderArea.resize(h, headerHeight);
- tabHeaderArea.relocate(tabsStartX + headerHeight, h - headerHeight);
- tabHeaderArea.getTransforms().clear();
- tabHeaderArea.getTransforms().add(new Rotate(getRotation(Side.LEFT), 0, headerHeight));
- } else if (tabPosition == Side.RIGHT) {
- tabHeaderArea.resize(h, headerHeight);
- tabHeaderArea.relocate(tabsStartX, y - headerHeight);
- tabHeaderArea.getTransforms().clear();
- tabHeaderArea.getTransforms().add(new Rotate(getRotation(Side.RIGHT), 0, headerHeight));
- }
-
- tabHeaderAreaClipRect.setX(0);
- tabHeaderAreaClipRect.setY(0);
- if (isHorizontal()) {
- tabHeaderAreaClipRect.setWidth(w);
- } else {
- tabHeaderAreaClipRect.setWidth(h);
- }
- tabHeaderAreaClipRect.setHeight(headerHeight);
-
- // ==================================
- // position the tab content for the selected tab only
- // ==================================
- // if the tabs are on the left, the content needs to be indented
- double contentStartX = 0;
- double contentStartY = 0;
-
- if (tabPosition == Side.TOP) {
- contentStartX = x;
- contentStartY = y + headerHeight;
- if (isFloatingStyleClass()) {
- // This is to hide the top border content
- contentStartY -= 1;
- }
- } else if (tabPosition == Side.BOTTOM) {
- contentStartX = x;
- contentStartY = y;
- if (isFloatingStyleClass()) {
- // This is to hide the bottom border content
- contentStartY = 1;
- }
- } else if (tabPosition == Side.LEFT) {
- contentStartX = x + headerHeight;
- contentStartY = y;
- if (isFloatingStyleClass()) {
- // This is to hide the left border content
- contentStartX -= 1;
- }
- } else if (tabPosition == Side.RIGHT) {
- contentStartX = x;
- contentStartY = y;
- if (isFloatingStyleClass()) {
- // This is to hide the right border content
- contentStartX = 1;
- }
- }
-
- double contentWidth = w - (isHorizontal() ? 0 : headerHeight);
- double contentHeight = h - (isHorizontal() ? headerHeight: 0);
-
- for (int i = 0, max = tabContentRegions.size(); i < max; i++) {
- TabContentRegion tabContent = tabContentRegions.get(i);
-
- tabContent.setAlignment(Pos.TOP_LEFT);
- if (tabContent.getClip() != null) {
- ((Rectangle)tabContent.getClip()).setWidth(contentWidth);
- ((Rectangle)tabContent.getClip()).setHeight(contentHeight);
- }
- // we need to size all tabs, even if they aren't visible. For example,
- // see RT-29167
- tabContent.resize(contentWidth, contentHeight);
- tabContent.relocate(contentStartX, contentStartY);
- }
- }
+ /***************************************************************************
+ * *
+ * CSS *
+ * *
+ **************************************************************************/
/**
* Super-lazy instantiation pattern from Bill Pugh.
* @treatAsPrivate implementation detail
*/
@@ -661,11 +741,11 @@
}
}
/**
- * @return The CssMetaData associated with this class, which may include the
+ * Returns the CssMetaData associated with this class, which may include the
* CssMetaData of its super classes.
*/
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
return StyleableProperties.STYLEABLES;
}
@@ -675,10 +755,18 @@
*/
@Override public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
return getClassCssMetaData();
}
+
+
+ /***************************************************************************
+ * *
+ * Support classes *
+ * *
+ **************************************************************************/
+
/**************************************************************************
*
* TabHeaderArea: Area responsible for painting all tabs
*
**************************************************************************/
@@ -1093,11 +1181,12 @@
positionInArea(controlButtons, controlStartX, controlStartY, btnWidth, btnHeight,
/*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
}
} /* End TabHeaderArea */
- static int CLOSE_BTN_SIZE = 16;
+
+
/**************************************************************************
*
* TabHeaderSkin: skin for each tab
*
@@ -1115,15 +1204,11 @@
private Tooltip tooltip;
private Rectangle clip;
private boolean isClosing = false;
- private MultiplePropertyChangeListenerHandler listener =
- new MultiplePropertyChangeListenerHandler(param -> {
- handlePropertyChanged(param);
- return null;
- });
+ private LambdaMultiplePropertyChangeListenerHandler listener = new LambdaMultiplePropertyChangeListenerHandler();
private final ListChangeListener<String> styleClassListener = new ListChangeListener<String>() {
@Override
public void onChanged(Change<? extends String> c) {
getStyleClass().setAll(tab.getStyleClass());
@@ -1156,11 +1241,10 @@
@Override
public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
switch (action) {
case FIRE: {
Tab tab = getTab();
- TabPaneBehavior behavior = getBehavior();
if (behavior.canCloseTab(tab)) {
behavior.closeTab(tab);
setOnMousePressed(null);
}
}
@@ -1170,13 +1254,13 @@
};
closeBtn.setAccessibleRole(AccessibleRole.BUTTON);
closeBtn.setAccessibleText(getString("Accessibility.title.TabPane.CloseButton"));
closeBtn.getStyleClass().setAll("tab-close-button");
closeBtn.setOnMousePressed(new EventHandler<MouseEvent>() {
- @Override public void handle(MouseEvent me) {
+ @Override
+ public void handle(MouseEvent me) {
Tab tab = getTab();
- TabPaneBehavior behavior = getBehavior();
if (behavior.canCloseTab(tab)) {
behavior.closeTab(tab);
setOnMousePressed(null);
}
}
@@ -1284,134 +1368,114 @@
if (tooltip != null) {
Tooltip.install(this, tooltip);
oldTooltip = tooltip;
}
- listener.registerChangeListener(tab.closableProperty(), "CLOSABLE");
- listener.registerChangeListener(tab.selectedProperty(), "SELECTED");
- listener.registerChangeListener(tab.textProperty(), "TEXT");
- listener.registerChangeListener(tab.graphicProperty(), "GRAPHIC");
- listener.registerChangeListener(tab.contextMenuProperty(), "CONTEXT_MENU");
- listener.registerChangeListener(tab.tooltipProperty(), "TOOLTIP");
- listener.registerChangeListener(tab.disableProperty(), "DISABLE");
- listener.registerChangeListener(tab.styleProperty(), "STYLE");
-
- tab.getStyleClass().addListener(weakStyleClassListener);
-
- listener.registerChangeListener(getSkinnable().tabClosingPolicyProperty(), "TAB_CLOSING_POLICY");
- listener.registerChangeListener(getSkinnable().sideProperty(), "SIDE");
- listener.registerChangeListener(getSkinnable().rotateGraphicProperty(), "ROTATE_GRAPHIC");
- listener.registerChangeListener(getSkinnable().tabMinWidthProperty(), "TAB_MIN_WIDTH");
- listener.registerChangeListener(getSkinnable().tabMaxWidthProperty(), "TAB_MAX_WIDTH");
- listener.registerChangeListener(getSkinnable().tabMinHeightProperty(), "TAB_MIN_HEIGHT");
- listener.registerChangeListener(getSkinnable().tabMaxHeightProperty(), "TAB_MAX_HEIGHT");
-
- getProperties().put(Tab.class, tab);
- getProperties().put(ContextMenu.class, tab.getContextMenu());
-
- setOnContextMenuRequested((ContextMenuEvent me) -> {
- if (getTab().getContextMenu() != null) {
- getTab().getContextMenu().show(inner, me.getScreenX(), me.getScreenY());
- me.consume();
- }
- });
- setOnMousePressed(new EventHandler<MouseEvent>() {
- @Override public void handle(MouseEvent me) {
- if (getTab().isDisable()) {
- return;
- }
- if (me.getButton().equals(MouseButton.MIDDLE)) {
- if (showCloseButton()) {
- Tab tab = getTab();
- TabPaneBehavior behavior = getBehavior();
- if (behavior.canCloseTab(tab)) {
- removeListeners(tab);
- behavior.closeTab(tab);
- }
- }
- } else if (me.getButton().equals(MouseButton.PRIMARY)) {
- getBehavior().selectTab(getTab());
- }
- }
- });
-
- // initialize pseudo-class state
- pseudoClassStateChanged(SELECTED_PSEUDOCLASS_STATE, tab.isSelected());
- pseudoClassStateChanged(DISABLED_PSEUDOCLASS_STATE, tab.isDisable());
- final Side side = getSkinnable().getSide();
- pseudoClassStateChanged(TOP_PSEUDOCLASS_STATE, (side == Side.TOP));
- pseudoClassStateChanged(RIGHT_PSEUDOCLASS_STATE, (side == Side.RIGHT));
- pseudoClassStateChanged(BOTTOM_PSEUDOCLASS_STATE, (side == Side.BOTTOM));
- pseudoClassStateChanged(LEFT_PSEUDOCLASS_STATE, (side == Side.LEFT));
- }
-
- private void handlePropertyChanged(final String p) {
- // --- Tab properties
- if ("CLOSABLE".equals(p)) {
+ listener.registerChangeListener(tab.closableProperty(), e -> {
inner.requestLayout();
requestLayout();
- } else if ("SELECTED".equals(p)) {
+ });
+ listener.registerChangeListener(tab.selectedProperty(), e -> {
pseudoClassStateChanged(SELECTED_PSEUDOCLASS_STATE, tab.isSelected());
// Need to request a layout pass for inner because if the width
// and height didn't not change the label or close button may have
// changed.
inner.requestLayout();
requestLayout();
- } else if ("TEXT".equals(p)) {
- label.setText(getTab().getText());
- } else if ("GRAPHIC".equals(p)) {
- label.setGraphic(getTab().getGraphic());
- } else if ("CONTEXT_MENU".equals(p)) {
- // todo
- } else if ("TOOLTIP".equals(p)) {
+ });
+ listener.registerChangeListener(tab.textProperty(),e -> label.setText(getTab().getText()));
+ listener.registerChangeListener(tab.graphicProperty(), e -> label.setGraphic(getTab().getGraphic()));
+ listener.registerChangeListener(tab.tooltipProperty(), e -> {
// uninstall the old tooltip
if (oldTooltip != null) {
Tooltip.uninstall(this, oldTooltip);
}
tooltip = tab.getTooltip();
if (tooltip != null) {
// install new tooltip and save as old tooltip.
Tooltip.install(this, tooltip);
oldTooltip = tooltip;
}
- } else if ("DISABLE".equals(p)) {
+ });
+ listener.registerChangeListener(tab.disableProperty(), e -> {
pseudoClassStateChanged(DISABLED_PSEUDOCLASS_STATE, tab.isDisable());
inner.requestLayout();
requestLayout();
- } else if ("STYLE".equals(p)) {
- setStyle(tab.getStyle());
- }
+ });
+ listener.registerChangeListener(tab.styleProperty(), e -> setStyle(tab.getStyle()));
- // --- Skinnable properties
- else if ("TAB_CLOSING_POLICY".equals(p)) {
+ tab.getStyleClass().addListener(weakStyleClassListener);
+
+ listener.registerChangeListener(getSkinnable().tabClosingPolicyProperty(),e -> {
inner.requestLayout();
requestLayout();
- } else if ("SIDE".equals(p)) {
+ });
+ listener.registerChangeListener(getSkinnable().sideProperty(),e -> {
final Side side = getSkinnable().getSide();
pseudoClassStateChanged(TOP_PSEUDOCLASS_STATE, (side == Side.TOP));
pseudoClassStateChanged(RIGHT_PSEUDOCLASS_STATE, (side == Side.RIGHT));
pseudoClassStateChanged(BOTTOM_PSEUDOCLASS_STATE, (side == Side.BOTTOM));
pseudoClassStateChanged(LEFT_PSEUDOCLASS_STATE, (side == Side.LEFT));
inner.setRotate(side == Side.BOTTOM ? 180.0F : 0.0F);
if (getSkinnable().isRotateGraphic()) {
updateGraphicRotation();
}
- } else if ("ROTATE_GRAPHIC".equals(p)) {
- updateGraphicRotation();
- } else if ("TAB_MIN_WIDTH".equals(p)) {
+ });
+ listener.registerChangeListener(getSkinnable().rotateGraphicProperty(), e -> updateGraphicRotation());
+ listener.registerChangeListener(getSkinnable().tabMinWidthProperty(), e -> {
requestLayout();
getSkinnable().requestLayout();
- } else if ("TAB_MAX_WIDTH".equals(p)) {
+ });
+ listener.registerChangeListener(getSkinnable().tabMaxWidthProperty(), e -> {
requestLayout();
getSkinnable().requestLayout();
- } else if ("TAB_MIN_HEIGHT".equals(p)) {
+ });
+ listener.registerChangeListener(getSkinnable().tabMinHeightProperty(), e -> {
requestLayout();
getSkinnable().requestLayout();
- } else if ("TAB_MAX_HEIGHT".equals(p)) {
+ });
+ listener.registerChangeListener(getSkinnable().tabMaxHeightProperty(), e -> {
requestLayout();
getSkinnable().requestLayout();
+ });
+
+ getProperties().put(Tab.class, tab);
+ getProperties().put(ContextMenu.class, tab.getContextMenu());
+
+ setOnContextMenuRequested((ContextMenuEvent me) -> {
+ if (getTab().getContextMenu() != null) {
+ getTab().getContextMenu().show(inner, me.getScreenX(), me.getScreenY());
+ me.consume();
+ }
+ });
+ setOnMousePressed(new EventHandler<MouseEvent>() {
+ @Override public void handle(MouseEvent me) {
+ if (getTab().isDisable()) {
+ return;
+ }
+ if (me.getButton().equals(MouseButton.MIDDLE)) {
+ if (showCloseButton()) {
+ Tab tab = getTab();
+ if (behavior.canCloseTab(tab)) {
+ removeListeners(tab);
+ behavior.closeTab(tab);
+ }
+ }
+ } else if (me.getButton().equals(MouseButton.PRIMARY)) {
+ behavior.selectTab(getTab());
+ }
}
+ });
+
+ // initialize pseudo-class state
+ pseudoClassStateChanged(SELECTED_PSEUDOCLASS_STATE, tab.isSelected());
+ pseudoClassStateChanged(DISABLED_PSEUDOCLASS_STATE, tab.isDisable());
+ final Side side = getSkinnable().getSide();
+ pseudoClassStateChanged(TOP_PSEUDOCLASS_STATE, (side == Side.TOP));
+ pseudoClassStateChanged(RIGHT_PSEUDOCLASS_STATE, (side == Side.RIGHT));
+ pseudoClassStateChanged(BOTTOM_PSEUDOCLASS_STATE, (side == Side.BOTTOM));
+ pseudoClassStateChanged(LEFT_PSEUDOCLASS_STATE, (side == Side.LEFT));
}
private void updateGraphicRotation() {
if (label.getGraphic() != null) {
label.getGraphic().setRotate(getSkinnable().isRotateGraphic() ? 0.0F :