]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fix multiple VOverlay animation-in/out related issues
authorJouni Koivuviita <jouni@vaadin.com>
Tue, 10 Jun 2014 19:34:59 +0000 (22:34 +0300)
committerJouni Koivuviita <jouni@vaadin.com>
Mon, 16 Jun 2014 07:56:12 +0000 (07:56 +0000)
Any running animation-in is now finished before animation-out is
triggered.

Moved windowClone variable into WindowConnector where it should be.

Change-Id: I0ee733d05558d46a08e5e46f821104ad98783118

client/src/com/vaadin/client/AnimationUtil.java
client/src/com/vaadin/client/ui/VNotification.java
client/src/com/vaadin/client/ui/VOverlay.java
client/src/com/vaadin/client/ui/VWindow.java
client/src/com/vaadin/client/ui/window/WindowConnector.java

index 9f80d98ef223903bdb2ea399b2166fae8fe60518..2077cee3c0cc5caa9fd6efe04c7d565ba9d6a678 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.vaadin.client;
 
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.dom.client.Style;
@@ -57,7 +58,7 @@ public class AnimationUtil {
     }
 
     /** For internal use only. May be removed or replaced in the future. */
-    public static native void registerAnimationEndEventListener(Element elem,
+    public static native JavaScriptObject addAnimationEndListener(Element elem,
             AnimationEndListener listener)
     /*-{
       var callbackFunc = $entry(function(e) {
@@ -71,10 +72,19 @@ public class AnimationUtil {
         elem._vaadin_animationend_callbacks = [];
       }
       elem._vaadin_animationend_callbacks.push(callbackFunc);
+      
+      return callbackFunc;
+    }-*/;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public static native void removeAnimationEndListener(Element elem,
+            JavaScriptObject listener)
+    /*-{
+      elem.removeEventListener(@com.vaadin.client.AnimationUtil::ANIMATION_END_EVENT_NAME, listener, false);
     }-*/;
 
     /** For internal use only. May be removed or replaced in the future. */
-    public static native void unregisterAnimationEndEventListeners(Element elem)
+    public static native void removeAllAnimationEndListeners(Element elem)
     /*-{
       if(elem._vaadin_animationend_callbacks) {
         var callbacks = elem._vaadin_animationend_callbacks;
index 855da517f9f2a422102c5aad4215a6acd6b374be..2aa8e8f879e5c86de64af948ffccbc16f43a5ac3 100644 (file)
@@ -77,7 +77,7 @@ public class VNotification extends VOverlay {
     private static final ArrayList<VNotification> notifications = new ArrayList<VNotification>();
 
     private boolean infiniteDelay = false;
-    private int cssTransitionDelay = -1;
+    private int cssAnimationDelay = -1;
 
     private int x = -1;
     private int y = -1;
@@ -137,10 +137,10 @@ public class VNotification extends VOverlay {
     private void setDelay(int delayMsec) {
         if (delayMsec < 0) {
             infiniteDelay = true;
-            cssTransitionDelay = 0;
+            cssAnimationDelay = 0;
         } else {
             infiniteDelay = false;
-            cssTransitionDelay = delayMsec;
+            cssAnimationDelay = delayMsec;
         }
     }
 
@@ -238,15 +238,16 @@ public class VNotification extends VOverlay {
             removeStyleDependentName(temporaryStyle);
             temporaryStyle = null;
         }
-        if (style != null) {
+        if (style != null && style.length() > 0) {
             temporaryStyle = style;
             addStyleName(style);
             addStyleDependentName(style);
         }
 
+        setPosition(position);
         super.show();
+        updatePositionOffsets(position);
         notifications.add(this);
-        setPosition(position);
         positionOrSizeUpdated();
         /**
          * Android 4 fails to render notifications correctly without a little
@@ -257,25 +258,53 @@ public class VNotification extends VOverlay {
         }
     }
 
+    private void hideWithoutDelay() {
+        cssAnimationDelay = 0;
+        hide();
+    }
+
     @Override
     public void hide() {
-        DOM.removeEventPreview(this);
-        if (cssTransitionDelay >= 0) {
-            AnimationUtil.setAnimationDelay(getElement(), cssTransitionDelay
-                    + "ms");
+        // Run only once
+        if (notifications.contains(this)) {
+            DOM.removeEventPreview(this);
+            if (cssAnimationDelay >= 0) {
+                AnimationUtil.setAnimationDelay(getElement(), cssAnimationDelay
+                        + "ms");
+            }
+            super.hide();
+            notifications.remove(this);
+            fireEvent(new HideEvent(this));
         }
-        super.hide();
-        notifications.remove(this);
-        fireEvent(new HideEvent(this));
     }
 
-    public void setPosition(com.vaadin.shared.Position position) {
+    private void updatePositionOffsets(com.vaadin.shared.Position position) {
         final Element el = getElement();
 
         // Remove all offsets (GWT PopupPanel defaults)
         el.getStyle().clearTop();
         el.getStyle().clearLeft();
 
+        switch (position) {
+        case MIDDLE_LEFT:
+        case MIDDLE_RIGHT:
+            center();
+            el.getStyle().clearLeft();
+            break;
+        case TOP_CENTER:
+        case BOTTOM_CENTER:
+            center();
+            el.getStyle().clearTop();
+            break;
+        case MIDDLE_CENTER:
+            center();
+            break;
+        }
+    }
+
+    public void setPosition(com.vaadin.shared.Position position) {
+        final Element el = getElement();
+
         // Remove any previous positions
         el.removeClassName(STYLENAME_POSITION_TOP);
         el.removeClassName(STYLENAME_POSITION_RIGHT);
@@ -295,16 +324,10 @@ public class VNotification extends VOverlay {
             el.addClassName(STYLENAME_POSITION_RIGHT);
             break;
         case MIDDLE_LEFT:
-            center();
-            // Centering sets the left offset
-            el.getStyle().clearLeft();
             el.addClassName(STYLENAME_POSITION_MIDDLE);
             el.addClassName(STYLENAME_POSITION_LEFT);
             break;
         case MIDDLE_RIGHT:
-            center();
-            // Centering sets the left offset
-            el.getStyle().clearLeft();
             el.addClassName(STYLENAME_POSITION_MIDDLE);
             el.addClassName(STYLENAME_POSITION_RIGHT);
             break;
@@ -317,32 +340,21 @@ public class VNotification extends VOverlay {
             el.addClassName(STYLENAME_POSITION_LEFT);
             break;
         case TOP_CENTER:
-            center();
-            // Centering sets the top offset
-            el.getStyle().clearTop();
             el.addClassName(STYLENAME_POSITION_TOP);
             el.addClassName(STYLENAME_POSITION_CENTER);
             break;
         case BOTTOM_CENTER:
-            center();
-            // Centering sets the top offset
-            el.getStyle().clearTop();
             el.addClassName(STYLENAME_POSITION_BOTTOM);
             el.addClassName(STYLENAME_POSITION_CENTER);
             break;
         case ASSISTIVE:
             el.addClassName(STYLENAME_POSITION_ASSISTIVE);
             break;
-        default:
-        case MIDDLE_CENTER:
-            center();
-            break;
         }
     }
 
     @Override
     public void onBrowserEvent(Event event) {
-        DOM.removeEventPreview(this);
         hide();
     }
 
@@ -353,12 +365,12 @@ public class VNotification extends VOverlay {
         if (infiniteDelay || temporaryStyle == STYLE_SYSTEM) {
             if (type == Event.ONCLICK) {
                 if (DOM.isOrHasChild(getElement(), DOM.eventGetTarget(event))) {
-                    hide();
+                    hideWithoutDelay();
                     return false;
                 }
             } else if (type == Event.ONKEYDOWN
                     && event.getKeyCode() == KeyCodes.KEY_ESCAPE) {
-                hide();
+                hideWithoutDelay();
                 return false;
             }
             if (temporaryStyle == STYLE_SYSTEM) {
@@ -370,7 +382,6 @@ public class VNotification extends VOverlay {
         // default
         switch (type) {
         case Event.ONMOUSEMOVE:
-
             if (x < 0) {
                 x = DOM.eventGetClientX(event);
                 y = DOM.eventGetClientY(event);
index ab493ce383d4ddc28446378232f4774786d9641e..ac4e57268a03ed77d2e1d285e33281d3b6801e0b 100644 (file)
@@ -21,13 +21,13 @@ import java.util.logging.Logger;
 
 import com.google.gwt.animation.client.Animation;
 import com.google.gwt.aria.client.Roles;
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.IFrameElement;
 import com.google.gwt.dom.client.NativeEvent;
 import com.google.gwt.dom.client.Style;
 import com.google.gwt.dom.client.Style.BorderStyle;
-import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.dom.client.Style.Position;
 import com.google.gwt.dom.client.Style.Unit;
 import com.google.gwt.event.logical.shared.CloseEvent;
@@ -443,6 +443,8 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
         current = null;
     }
 
+    private JavaScriptObject animateInListener;
+
     private boolean maybeShowWithAnimation() {
         boolean isAttached = isAttached() && isShowing();
         super.show();
@@ -467,8 +469,25 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
             }
 
             if (animationName.contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
-                AnimationUtil.registerAnimationEndEventListener(getElement(),
-                        getAnimationEndListener(false));
+                animateInListener = AnimationUtil.addAnimationEndListener(
+                        getElement(), new AnimationEndListener() {
+                            @Override
+                            public void onAnimationEnd(NativeEvent event) {
+                                String animationName = AnimationUtil
+                                        .getAnimationName(event);
+                                if (animationName
+                                        .contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
+                                    AnimationUtil.removeAnimationEndListener(
+                                            getElement(), animateInListener);
+                                    removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
+                                    if (isShadowEnabled()) {
+                                        shadow.removeClassName(CLASSNAME_SHADOW
+                                                + "-"
+                                                + ADDITIONAL_CLASSNAME_ANIMATE_IN);
+                                    }
+                                }
+                            }
+                        });
                 return true;
             } else {
                 removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
@@ -936,64 +955,75 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
      * @see com.google.gwt.user.client.ui.PopupPanel#hide(boolean)
      */
     @Override
-    public void hide(boolean autoClosed) {
+    public void hide(final boolean autoClosed) {
         if (BrowserInfo.get().isIE8() || BrowserInfo.get().isIE9()) {
             super.hide(autoClosed);
         } else {
-            // Check if animations are used
-            addStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
-            if (isShadowEnabled()) {
-                shadow.addClassName(CLASSNAME_SHADOW + "-"
-                        + ADDITIONAL_CLASSNAME_ANIMATE_OUT);
-            }
-            ComputedStyle cs = new ComputedStyle(getElement());
-            String animationName = AnimationUtil.getAnimationName(cs);
-            if (animationName == null) {
-                animationName = "";
-            }
-
-            if (animationName.contains(ADDITIONAL_CLASSNAME_ANIMATE_OUT)) {
-                AnimationUtil.registerAnimationEndEventListener(getElement(),
-                        getAnimationEndListener(autoClosed));
-                // No event previews should happen after the animation has
-                // started
-                VOverlay.this.setPreviewingAllNativeEvents(false);
+            if (getStyleName().contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
+                AnimationUtil.addAnimationEndListener(getElement(),
+                        new AnimationEndListener() {
+                            @Override
+                            public void onAnimationEnd(NativeEvent event) {
+                                if (AnimationUtil
+                                        .getAnimationName(event)
+                                        .contains(
+                                                ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
+                                    VOverlay.this.hide(autoClosed);
+                                }
+                            }
+                        });
             } else {
-                removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
+                // Check if animations are used
+                addStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
                 if (isShadowEnabled()) {
-                    shadow.removeClassName(CLASSNAME_SHADOW + "-"
+                    shadow.addClassName(CLASSNAME_SHADOW + "-"
                             + ADDITIONAL_CLASSNAME_ANIMATE_OUT);
                 }
-                super.hide(autoClosed);
-            }
-        }
-    }
-
-    private AnimationEndListener getAnimationEndListener(
-            final boolean autoClosed) {
-        return new AnimationEndListener() {
+                ComputedStyle cs = new ComputedStyle(getElement());
+                String animationName = AnimationUtil.getAnimationName(cs);
+                if (animationName == null) {
+                    animationName = "";
+                }
 
-            @Override
-            public void onAnimationEnd(NativeEvent event) {
-                AnimationUtil
-                        .unregisterAnimationEndEventListeners(getElement());
-                String animationName = AnimationUtil.getAnimationName(event);
-                if (animationName.contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
-                    removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
-                    if (isShadowEnabled()) {
-                        shadow.removeClassName(CLASSNAME_SHADOW + "-"
-                                + ADDITIONAL_CLASSNAME_ANIMATE_IN);
-                    }
-                } else if (animationName
-                        .contains(ADDITIONAL_CLASSNAME_ANIMATE_OUT)) {
+                if (animationName.contains(ADDITIONAL_CLASSNAME_ANIMATE_OUT)) {
+                    AnimationUtil.addAnimationEndListener(getElement(),
+                            new AnimationEndListener() {
+                                @Override
+                                public void onAnimationEnd(NativeEvent event) {
+                                    String animationName = AnimationUtil
+                                            .getAnimationName(event);
+                                    if (animationName
+                                            .contains(ADDITIONAL_CLASSNAME_ANIMATE_OUT)) {
+                                        AnimationUtil
+                                                .removeAllAnimationEndListeners(getElement());
+                                        // Remove both animation styles just in
+                                        // case
+                                        removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
+                                        removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
+                                        if (isShadowEnabled()) {
+                                            shadow.removeClassName(CLASSNAME_SHADOW
+                                                    + "-"
+                                                    + ADDITIONAL_CLASSNAME_ANIMATE_IN);
+                                            shadow.removeClassName(CLASSNAME_SHADOW
+                                                    + "-"
+                                                    + ADDITIONAL_CLASSNAME_ANIMATE_OUT);
+                                        }
+                                        VOverlay.super.hide(autoClosed);
+                                    }
+                                }
+                            });
+                    // No event previews should happen after the animation has
+                    // started
+                    VOverlay.this.setPreviewingAllNativeEvents(false);
+                } else {
                     removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
                     if (isShadowEnabled()) {
                         shadow.removeClassName(CLASSNAME_SHADOW + "-"
                                 + ADDITIONAL_CLASSNAME_ANIMATE_OUT);
                     }
-                    VOverlay.super.hide(autoClosed);
+                    super.hide(autoClosed);
                 }
             }
-        };
+        }
     }
 }
\ No newline at end of file
index e2cc33744ebfb50c63d761badca4fdcfc1818f9c..7c1a21f6540c0d45872c611b2c75bdc0359d8fc5 100644 (file)
@@ -30,7 +30,6 @@ import com.google.gwt.core.client.Scheduler.ScheduledCommand;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.dom.client.Node;
 import com.google.gwt.dom.client.Style;
 import com.google.gwt.dom.client.Style.Display;
 import com.google.gwt.dom.client.Style.Position;
@@ -201,9 +200,6 @@ public class VWindow extends VWindowOverlay implements
     /** For internal use only. May be removed or replaced in the future. */
     public int bringToFrontSequence = -1;
 
-    /** For internal use only. May be removed or replaced in the future. */
-    public Node windowClone;
-
     private VLazyExecutor delayedContentsSizeUpdater = new VLazyExecutor(200,
             new ScheduledCommand() {
 
@@ -1037,14 +1033,6 @@ public class VWindow extends VWindowOverlay implements
     }
 
     private void onCloseClick() {
-        // Take a copy of the contents, since the server will detach all
-        // children of this window, and the window will be emptied during the
-        // next hierarchy update (we need to keep the contents visible for the
-        // duration of a possible 'out-animation')
-        // This is used by the WindowConnector in the case the window is
-        // actually closed and removed from the UI
-        windowClone = getElement().getFirstChild().cloneNode(true);
-
         // Send the close event to the server
         client.updateVariable(id, "close", true, true);
     }
index 40185099d33081a800334f84fb61f78c4b8a7001..2a2f031144ad10a9a1570e54d9bf3ab37ee1088e 100644 (file)
@@ -19,6 +19,7 @@ import java.util.logging.Logger;
 
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Node;
 import com.google.gwt.dom.client.Style;
 import com.google.gwt.dom.client.Style.Position;
 import com.google.gwt.dom.client.Style.Unit;
@@ -58,6 +59,8 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
         SimpleManagedLayout, PostLayoutListener, MayScrollChildren,
         WindowMoveHandler {
 
+    private Node windowClone;
+
     private ClickEventHandler clickEventHandler = new ClickEventHandler(this) {
         @Override
         protected void fireClick(NativeEvent event,
@@ -193,14 +196,16 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
         // We always have 1 child, unless the child is hidden
         getWidget().contentPanel.setWidget(getContentWidget());
 
-        // If the window is removed from the UI, add the copy of the contents to
-        // the window (in case of an 'out-animation')
-        if (getParent() == null) {
+        if (getParent() == null && windowClone != null) {
+            // If the window is removed from the UI, add the copy of the
+            // contents to the window (in case of an 'out-animation')
             getWidget().getElement().removeAllChildren();
-            getWidget().getElement().appendChild(getWidget().windowClone);
+            getWidget().getElement().appendChild(windowClone);
+
+            // Clean reference
+            windowClone = null;
         }
-        // Clean reference
-        getWidget().windowClone = null;
+
     }
 
     @Override
@@ -277,6 +282,16 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
             window.center();
         }
         window.positionOrSizeUpdated();
+
+        if (getParent() != null) {
+            // Take a copy of the contents, since the server will detach all
+            // children of this window when it's closed, and the window will be
+            // emptied during the following hierarchy update (we need to keep
+            // the contents visible for the duration of a possible
+            // 'out-animation')
+            windowClone = getWidget().getElement().getFirstChild()
+                    .cloneNode(true);
+        }
     }
 
     @Override