Browse Source

Fix multiple VOverlay animation-in/out related issues

Any running animation-in is now finished before animation-out is
triggered.

Moved windowClone variable into WindowConnector where it should be.

Change-Id: I0ee733d05558d46a08e5e46f821104ad98783118
tags/7.3.0.beta1
Jouni Koivuviita 10 years ago
parent
commit
e620bfb972

+ 12
- 2
client/src/com/vaadin/client/AnimationUtil.java View 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;

+ 44
- 33
client/src/com/vaadin/client/ui/VNotification.java View 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);

+ 77
- 47
client/src/com/vaadin/client/ui/VOverlay.java View 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);
}
}
};
}
}
}

+ 0
- 12
client/src/com/vaadin/client/ui/VWindow.java View 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);
}

+ 21
- 6
client/src/com/vaadin/client/ui/window/WindowConnector.java View 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

Loading…
Cancel
Save