changeset 6888:668c601a2856

RT-36875: Visual artefacts in TabPane switcher. I took this opportunity to also clean up the TabPaneSkin code a bit and improve the animations a little.
author jgiles
date Tue, 29 Apr 2014 15:35:58 +1200
parents d06e9b3e8351
children 0da5df134799
files modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TabPaneSkin.java modules/controls/src/main/java/javafx/scene/control/TabPane.java
diffstat 2 files changed, 57 insertions(+), 144 deletions(-) [+]
line wrap: on
line diff
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TabPaneSkin.java	Tue Apr 29 14:00:21 2014 +1200
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TabPaneSkin.java	Tue Apr 29 15:35:58 2014 +1200
@@ -160,7 +160,7 @@
         }
         return null;
     }
-    private static final double ANIMATION_SPEED = 300;
+    private static final double ANIMATION_SPEED = 150;
     private static final int SPACER = 10;
 
     private TabHeaderArea tabHeaderArea;
@@ -244,6 +244,7 @@
             // Animate the tab removal
             final TabHeaderSkin tabRegion = tabHeaderArea.getTabHeaderSkin(tab);
             if (tabRegion != null) {
+                tabRegion.isClosing = true;
                 if (closeTabAnimation.get() == TabAnimation.GROW) {
                     tabRegion.animating = true;
                     Timeline closedTabTimeline = createTimeline(tabRegion, Duration.millis(ANIMATION_SPEED), 0.0F, event -> {
@@ -387,12 +388,14 @@
         // remove the menu item from the popup menu
         ContextMenu popupMenu = tabHeaderArea.controlButtons.popup;
         TabMenuItem tabItem = null;
-        for (MenuItem item : popupMenu.getItems()) {
-            tabItem = (TabMenuItem) item;
-            if (tab == tabItem.getTab()) {
-                break;
+        if (popupMenu != null) {
+            for (MenuItem item : popupMenu.getItems()) {
+                tabItem = (TabMenuItem) item;
+                if (tab == tabItem.getTab()) {
+                    break;
+                }
+                tabItem = null;
             }
-            tabItem = null;
         }
         if (tabItem != null) {
             tabItem.dispose();
@@ -654,6 +657,8 @@
         private StackPane headerBackground;
         private TabControlButtons controlButtons;
 
+        private boolean measureClosingTabs = false;
+
         private double scrollOffset;
         public double getScrollOffset() {
             return scrollOffset;
@@ -675,7 +680,7 @@
                     double width = 0.0F;
                     for (Node child : getChildren()) {
                         TabHeaderSkin tabHeaderSkin = (TabHeaderSkin)child;
-                        if (tabHeaderSkin.isVisible()) {
+                        if (tabHeaderSkin.isVisible() && (measureClosingTabs || ! tabHeaderSkin.isClosing)) {
                             width += tabHeaderSkin.prefWidth(height);
                         }
                     }
@@ -786,7 +791,11 @@
             double shadowRadius = 0;
             double clipOffset = firstTabIndent();
             double controlButtonPrefWidth = snapSize(controlButtons.prefWidth(-1));
+
+            measureClosingTabs = true;
             double headersPrefWidth = snapSize(headersRegion.prefWidth(-1));
+            measureClosingTabs = false;
+
             double headersPrefHeight = snapSize(headersRegion.prefHeight(-1));
 
             // Add the spacer if isShowTabsMenu is true.
@@ -949,19 +958,16 @@
             double tabBackgroundHeight = snapSize(prefHeight(-1));
             double headersPrefWidth = snapSize(headersRegion.prefWidth(-1));
             double headersPrefHeight = snapSize(headersRegion.prefHeight(-1));
-            
-            if (tabsFit()) {
-                controlButtons.showTabsMenu(false);
-            } else {
-                controlButtons.showTabsMenu(true);
-            }
-            
+
+            controlButtons.showTabsMenu(! tabsFit());
+
             updateHeaderClip();
             headersRegion.requestLayout();
 
             // RESIZE CONTROL BUTTONS
             double btnWidth = snapSize(controlButtons.prefWidth(-1));
-            controlButtons.resize(btnWidth, controlButtons.getControlTabHeight());
+            final double btnHeight = controlButtons.prefHeight(btnWidth);
+            controlButtons.resize(btnWidth, btnHeight);
 
             // POSITION TABS
             headersRegion.resize(headersPrefWidth, headersPrefHeight);
@@ -983,29 +989,29 @@
                 startX = leftInset;
                 startY = tabBackgroundHeight - headersPrefHeight - bottomInset;
                 controlStartX = w - btnWidth + leftInset;
-                controlStartY = snapSize(getHeight()) - controlButtons.getControlTabHeight() - bottomInset;
+                controlStartY = snapSize(getHeight()) - btnHeight - bottomInset;
             } else if (tabPosition.equals(Side.RIGHT)) {
                 startX = topInset;
                 startY = tabBackgroundHeight - headersPrefHeight - leftInset;
                 controlStartX = w - btnWidth + topInset;
-                controlStartY = snapSize(getHeight()) - controlButtons.getControlTabHeight() - leftInset;
+                controlStartY = snapSize(getHeight()) - btnHeight - leftInset;
             } else if (tabPosition.equals(Side.BOTTOM)) {
                 startX = snapSize(getWidth()) - headersPrefWidth - leftInset;
                 startY = tabBackgroundHeight - headersPrefHeight - topInset;
                 controlStartX = rightInset;
-                controlStartY = snapSize(getHeight()) - controlButtons.getControlTabHeight() - topInset;
+                controlStartY = snapSize(getHeight()) - btnHeight - topInset;
             } else if (tabPosition.equals(Side.LEFT)) {
                 startX = snapSize(getWidth()) - headersPrefWidth - topInset;
                 startY = tabBackgroundHeight - headersPrefHeight - rightInset;
                 controlStartX = leftInset;
-                controlStartY = snapSize(getHeight()) - controlButtons.getControlTabHeight() - rightInset;
+                controlStartY = snapSize(getHeight()) - btnHeight - rightInset;
             }
             if (headerBackground.isVisible()) {
                 positionInArea(headerBackground, 0, 0,
                         snapSize(getWidth()), snapSize(getHeight()), /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
             }
             positionInArea(headersRegion, startX, startY, w, h, /*baseline ignored*/0, HPos.LEFT, VPos.CENTER);
-            positionInArea(controlButtons, controlStartX, controlStartY, btnWidth, controlButtons.getControlTabHeight(),
+            positionInArea(controlButtons, controlStartX, controlStartY, btnWidth, btnHeight,
                         /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
         }
     } /* End TabHeaderArea */
@@ -1029,8 +1035,10 @@
         private Tooltip oldTooltip;
         private Tooltip tooltip;
         private Rectangle clip;
-        
-        private MultiplePropertyChangeListenerHandler listener = 
+
+        private boolean isClosing = false;
+
+        private MultiplePropertyChangeListenerHandler listener =
                 new MultiplePropertyChangeListenerHandler(param -> {
                     handlePropertyChanged(param);
                     return null;
@@ -1516,23 +1524,9 @@
             setupPopupMenu();
 
             inner = new StackPane() {
-                private double getArrowBtnWidth() {
-                    if (animationLock) return maxArrowWidth;
-                    if (isShowTabsMenu()) {
-                        maxArrowWidth = Math.max(maxArrowWidth, snapSize(downArrow.prefWidth(getHeight())) + snapSize(downArrowBtn.prefWidth(getHeight())));
-                    }
-                    return maxArrowWidth;
-                }
-
                 @Override protected double computePrefWidth(double height) {
-                    if (animationLock) return innerPrefWidth;
-                    innerPrefWidth = getActualPrefWidth();
-                    return innerPrefWidth;
-                }
-
-                public double getActualPrefWidth() {
                     double pw;
-                    double maxArrowWidth = getArrowBtnWidth();
+                    double maxArrowWidth = ! isShowTabsMenu() ? 0 : snapSize(downArrow.prefWidth(getHeight())) + snapSize(downArrowBtn.prefWidth(getHeight()));
                     pw = 0.0F;
                     if (isShowTabsMenu()) {
                         pw += maxArrowWidth;
@@ -1555,30 +1549,15 @@
                 }
 
                 @Override protected void layoutChildren() {
-                    Side tabPosition = getSkinnable().getSide();
-                    double x = 0.0F;
-                    //padding.left;
-                    double y = snappedTopInset();
-                    double h = snapSize(getHeight()) - y + snappedBottomInset();
-                    // when on the left or bottom, we need to position the tabs controls
-                    // button such that it is the furtherest button away from the tabs.
-                    if (tabPosition.equals(Side.BOTTOM) || tabPosition.equals(Side.LEFT)) {
-                        x += positionTabsMenu(x, y, h, true);
-                    } else {
-                        x += positionTabsMenu(x, y, h, false);
+                    if (isShowTabsMenu()) {
+                        double x = 0;
+                        double y = snappedTopInset();
+                        double w = snapSize(getWidth()) - x + snappedLeftInset();
+                        double h = snapSize(getHeight()) - y + snappedBottomInset();
+                        positionArrow(downArrowBtn, downArrow, x, y, w, h);
                     }
                 }
 
-                private double positionTabsMenu(double x, double y, double h, boolean showSep) {
-                    double newX = x;
-                    if (isShowTabsMenu()) {
-                        // DOWN ARROW BUTTON
-                        positionArrow(downArrowBtn, downArrow, newX, y, maxArrowWidth, h);
-                        newX += maxArrowWidth;
-                    }
-                    return newX;
-                }
-
                 private void positionArrow(Pane btn, StackPane arrow, double x, double y, double width, double height) {
                     btn.resize(width, height);
                     positionInArea(btn, x, y, width, height, /*baseline ignored*/0,
@@ -1602,9 +1581,7 @@
                 Side tabPosition = getSkinnable().getSide();
                 downArrow.setRotate(tabPosition.equals(Side.BOTTOM)? 180.0F : 0.0F);
             });
-            tabPane.getTabs().addListener((ListChangeListener<Tab>) c -> {
-                setupPopupMenu();
-            });
+            tabPane.getTabs().addListener((ListChangeListener<Tab>) c -> setupPopupMenu());
             showControlButtons = false;
             if (isShowTabsMenu()) {
                 showControlButtons = true;
@@ -1616,61 +1593,24 @@
         private boolean showTabsMenu = false;
 
         private void showTabsMenu(boolean value) {
-            if (value && !showTabsMenu) {
+            final boolean wasTabsMenuShowing = isShowTabsMenu();
+            this.showTabsMenu = value;
+
+            if (showTabsMenu && !wasTabsMenuShowing) {
                 downArrowBtn.setVisible(true);
                 showControlButtons = true;
                 inner.requestLayout();
                 tabHeaderArea.requestLayout();
-            } else if (!value && showTabsMenu) {
+            } else if (!showTabsMenu && wasTabsMenuShowing) {
                 hideControlButtons();
             }
-            this.showTabsMenu = value;
         }
 
         private boolean isShowTabsMenu() {
             return showTabsMenu;
         }
 
-        private final DoubleProperty controlTabHeight = new SimpleDoubleProperty(this, "controlTabHeight");
-        {
-            controlTabHeight.addListener(valueModel -> {
-                requestLayout();
-            });
-        }
-        // TODO: Maybe the getter and setter can be dropped completely after
-        // turning controlTabHeight into a DoubleVariable?
-        public double getControlTabHeight() {
-            return controlTabHeight.get();
-        }
-        public void setControlTabHeight(double value) {
-            controlTabHeight.set(value);
-        }
-
-        private boolean animationLock = false;
-        private void setAnimationLock(boolean value) {
-            animationLock = value;
-            tabHeaderArea.requestLayout();
-        }
-
-//        FIXME this should be allowed, but the method is final on Node
-//        @Override
-//        public boolean isVisible() {
-//            return getSkinnable().isShowScrollArrows() || getSkinnable().isShowTabsMenu();
-//        }
-
-        double maxArrowWidth;
-        double innerPrefWidth;
-
-        private double prefWidth;
         @Override protected double computePrefWidth(double height) {
-            if (animationLock) {
-                return prefWidth;
-            }
-            prefWidth = getActualPrefWidth(height);
-            return prefWidth;
-        }
-
-        private double getActualPrefWidth(double height) {
             double pw = snapSize(inner.prefWidth(height));
             if (pw > 0) {
                 pw += snappedLeftInset() + snappedRightInset();
@@ -1699,53 +1639,29 @@
         }
 
         private void showControlButtons() {
-            double prefHeight = snapSize(prefHeight(-1));
-            Timeline timeline = new Timeline();
-            KeyValue keyValue = new KeyValue(controlTabHeight, prefHeight, Interpolator.EASE_OUT);
-
             setVisible(true);
-            timeline.getKeyFrames().clear();
-            timeline.getKeyFrames().add(new KeyFrame(Duration.millis(ANIMATION_SPEED), event -> {
-                if (popup == null) {
-                    setupPopupMenu();
-                }
-                requestLayout();
-            }, keyValue));
-            timeline.play();
         }
 
         private void hideControlButtons() {
-            setAnimationLock(true);
-            Timeline timeline = new Timeline();
-            KeyValue keyValue = new KeyValue(controlTabHeight, 0.0, Interpolator.EASE_IN);
+            // If the scroll arrows or tab menu is still visible we don't want
+            // to hide it animate it back it.
+            if (isShowTabsMenu()) {
+                showControlButtons = true;
+            } else {
+                setVisible(false);
+                popup.getItems().clear();
+                popup = null;
+            }
 
-            timeline.getKeyFrames().clear();
-            timeline.getKeyFrames().add(new KeyFrame(Duration.millis(ANIMATION_SPEED), event -> {
-                if (!isShowTabsMenu()) {
-                    downArrowBtn.setVisible(false);
-                }
-                // If the scroll arrows or tab menu is still visible we don't want
-                // to hide it animate it back it.
-                if (isShowTabsMenu()) {
-                    showControlButtons = true;
-                } else {
-                    setVisible(false);
-                    popup.getItems().clear();
-                    popup = null;
-                }
-                setAnimationLock(false);
-                // This needs to be called when we are in the left tabPosition
-                // to allow for the clip offset to move properly (otherwise
-                // it jumps too early - before the animation is done).
-                requestLayout();
-            }, keyValue));
-            timeline.play();
+            // This needs to be called when we are in the left tabPosition
+            // to allow for the clip offset to move properly (otherwise
+            // it jumps too early - before the animation is done).
+            requestLayout();
         }
 
         private void setupPopupMenu() {
             if (popup == null) {
                 popup = new ContextMenu();
-//                popup.setManaged(false);
             }
             popup.getItems().clear();
             ToggleGroup group = new ToggleGroup();
@@ -1753,9 +1669,7 @@
             for (final Tab tab : getSkinnable().getTabs()) {
                 TabMenuItem item = new TabMenuItem(tab);                
                 item.setToggleGroup(group);
-                item.setOnAction(t -> {
-                    getSkinnable().getSelectionModel().select(tab);
-                });
+                item.setOnAction(t -> getSkinnable().getSelectionModel().select(tab));
                 menuitems.add(item);
             }
             popup.getItems().addAll(menuitems);
--- a/modules/controls/src/main/java/javafx/scene/control/TabPane.java	Tue Apr 29 14:00:21 2014 +1200
+++ b/modules/controls/src/main/java/javafx/scene/control/TabPane.java	Tue Apr 29 15:35:58 2014 +1200
@@ -653,7 +653,6 @@
                                     int downPos = tabIndex - i;
                                     if (downPos >= 0) {
                                         Tab _tab = getModelItem(downPos);
-                                        System.out.println(_tab.getText() + " disabled: " + _tab.isDisabled() + ", disable: " + _tab.isDisable());
                                         if (_tab != null && ! _tab.isDisable()) {
                                             select(_tab);
                                             break;