aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJouni Koivuviita <jouni@vaadin.com>2014-06-05 17:23:01 +0300
committerJouni Koivuviita <jouni@vaadin.com>2014-06-05 17:23:01 +0300
commit97ba8b0756b8f8ba02b2eda6857f9fda0f9b2cb2 (patch)
treec25a17dc98713fc57c38d7054a9d3cdd53a5e8bf
parentdcb6cfd6bc050ad9451e14cb12345aa5a4c43727 (diff)
downloadvaadin-framework-97ba8b0756b8f8ba02b2eda6857f9fda0f9b2cb2.tar.gz
vaadin-framework-97ba8b0756b8f8ba02b2eda6857f9fda0f9b2cb2.zip
Implement Notification animation with CSS (#13660)
Default opacity (90%), fade duration (400ms) and fade delay (1000ms) are defined in the base theme. “In” animations can be defined by using a dependent stylename ‘animate-in’ (prefixed with the widgets own primary stylename), e.g. 'v-Notification-animate-in'. That CSS class should then have a CSS animation specified, and the CSS animation name should contain 'animate-in', e.g. .v-Notification-animate-in { animation: notification-animate-in 400ms; } @keyframes notification-animate-in { ... } “Out” animations are defined similarly, but with 'animate-out' as the keyword. The base theme contains mixins for declaring keyframes rules as well as animation values, which include needed vendor prefixes. Refactored deprecated GWT API’s to use newer API’s. Change-Id: Iba929e8de8b6ce100852d472d65c9c9d0630c48f
-rw-r--r--WebContent/VAADIN/themes/base/base.scss1
-rw-r--r--WebContent/VAADIN/themes/base/common/mixins.scss17
-rw-r--r--WebContent/VAADIN/themes/base/notification/notification.scss14
-rw-r--r--client/src/com/vaadin/client/AnimationUtil.java168
-rw-r--r--client/src/com/vaadin/client/ui/VNotification.java180
-rw-r--r--client/src/com/vaadin/client/ui/VOverlay.java157
-rw-r--r--client/src/com/vaadin/client/ui/VWindow.java20
-rw-r--r--client/src/com/vaadin/client/ui/window/WindowConnector.java9
8 files changed, 421 insertions, 145 deletions
diff --git a/WebContent/VAADIN/themes/base/base.scss b/WebContent/VAADIN/themes/base/base.scss
index 88a8bd9cb9..3570c5efec 100644
--- a/WebContent/VAADIN/themes/base/base.scss
+++ b/WebContent/VAADIN/themes/base/base.scss
@@ -1,3 +1,4 @@
+@import "common/mixins.scss";
@import "absolutelayout/absolutelayout.scss";
@import "accordion/accordion.scss";
@import "button/button.scss";
diff --git a/WebContent/VAADIN/themes/base/common/mixins.scss b/WebContent/VAADIN/themes/base/common/mixins.scss
new file mode 100644
index 0000000000..79d26d6c16
--- /dev/null
+++ b/WebContent/VAADIN/themes/base/common/mixins.scss
@@ -0,0 +1,17 @@
+@mixin keyframes ($name) {
+ @-webkit-keyframes #{$name} {
+ @content;
+ }
+ @-moz-keyframes #{$name} {
+ @content;
+ }
+ @keyframes #{$name} {
+ @content;
+ }
+}
+
+@mixin animation ($anim) {
+ -webkit-animation: $anim;
+ -moz-animation: $anim;
+ animation: $anim;
+}
diff --git a/WebContent/VAADIN/themes/base/notification/notification.scss b/WebContent/VAADIN/themes/base/notification/notification.scss
index 9751422fb7..aa70a167dc 100644
--- a/WebContent/VAADIN/themes/base/notification/notification.scss
+++ b/WebContent/VAADIN/themes/base/notification/notification.scss
@@ -7,7 +7,8 @@
overflow: hidden;
padding: 1em;
max-width:85%;
-
+ opacity: .9;
+ filter: alpha(opacity=90);
}
.#{$primaryStyleName} h1,
.#{$primaryStyleName} p,
@@ -39,5 +40,14 @@
display: block;
margin: 0;
}
+.#{$primaryStyleName}-animate-out {
+ @include animation(animate-out 400ms 1s);
+}
+
+}
-} \ No newline at end of file
+@include keyframes(animate-out) {
+ 100% {
+ opacity: 0;
+ }
+}
diff --git a/client/src/com/vaadin/client/AnimationUtil.java b/client/src/com/vaadin/client/AnimationUtil.java
new file mode 100644
index 0000000000..034ea659a9
--- /dev/null
+++ b/client/src/com/vaadin/client/AnimationUtil.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2000-2013 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.client;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Style;
+
+/**
+ * Utility methods for working with CSS transitions and animations.
+ *
+ * @author Vaadin Ltd
+ */
+public class AnimationUtil {
+
+ /**
+ * For internal use only. May be removed or replaced in the future.
+ *
+ * Set the animation-duration CSS property.
+ *
+ * @param elem
+ * the element whose animation-duration to set
+ * @param duration
+ * the duration as a valid CSS value
+ */
+ public static void setAnimationDuration(Element elem, String duration) {
+ Style style = elem.getStyle();
+ style.setProperty(ANIMATION_PROPERTY_NAME + "Duration", duration);
+ }
+
+ /**
+ * For internal use only. May be removed or replaced in the future.
+ *
+ * Set the animation-delay CSS property.
+ *
+ * @param elem
+ * the element whose animation-delay to set
+ * @param delay
+ * the delay as a valid CSS value
+ */
+ public static void setAnimationDelay(Element elem, String delay) {
+ Style style = elem.getStyle();
+ style.setProperty(ANIMATION_PROPERTY_NAME + "Delay", delay);
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public static native void registerAnimationEndEventListener(Element elem,
+ AnimationEndListener listener)
+ /*-{
+ var callbackFunc = $entry(function(e) {
+ listener.@com.vaadin.client.AnimationUtil.AnimationEndListener::onAnimationEnd(Lcom/google/gwt/dom/client/NativeEvent;)(e);
+ });
+
+ elem.addEventListener(@com.vaadin.client.AnimationUtil::ANIMATION_END_EVENT_NAME, callbackFunc, false);
+
+ // Store function reference for later removal
+ if(!elem._vaadin_animationend_callbacks) {
+ elem._vaadin_animationend_callbacks = [];
+ }
+ elem._vaadin_animationend_callbacks.push(callbackFunc);
+ }-*/;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public static native void unregisterAnimationEndEventListeners(Element elem)
+ /*-{
+ if(elem._vaadin_animationend_callbacks) {
+ var callbacks = elem._vaadin_animationend_callbacks;
+ for(var i=0; i < callbacks.length; i++) {
+ elem.removeEventListener(@com.vaadin.client.AnimationUtil::ANIMATION_END_EVENT_NAME, callbacks[i], false);
+ }
+ }
+ }-*/;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public interface AnimationEndListener {
+ public void onAnimationEnd(NativeEvent event);
+ }
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public static native String getAnimationName(NativeEvent event)
+ /*-{
+ if(event.webkitAnimationName)
+ return event.webkitAnimationName;
+ else if(event.animationName)
+ return event.animationName;
+ else if(event.mozAnimationName)
+ return event.mozAnimationName;
+ else if(event.oAnimationName)
+ return event.oAnimationName;
+
+ return "";
+ }-*/;
+
+ /** For internal use only. May be removed or replaced in the future. */
+ public static native String getAnimationName(ComputedStyle cstyle)
+ /*-{
+ var cs = cstyle.@com.vaadin.client.ComputedStyle::computedStyle;
+
+ if(!cs.getPropertyValue)
+ return "";
+
+ if(cs.getPropertyValue("-webkit-animation-name"))
+ return cs.getPropertyValue("-webkit-animation-name");
+
+ else if(cs.getPropertyValue("animation-name"))
+ return cs.getPropertyValue("animation-name");
+
+ else if(cs.getPropertyValue("-moz-animation-name"))
+ return cs.getPropertyValue("-moz-animation-name");
+
+ else if(cs.getPropertyValue("-o-animation-name"))
+ return cs.getPropertyValue("-o-animation-name");
+
+ return "";
+ }-*/;
+
+ private static final String ANIMATION_END_EVENT_NAME = whichAnimationEndEvent();
+
+ private static native String whichAnimationEndEvent()
+ /*-{
+ var el = document.createElement('fakeelement');
+ var anims = {
+ 'animationName': 'animationend',
+ 'OAnimationName': 'oAnimationEnd',
+ 'MozAnimation': 'animationend',
+ 'WebkitAnimation': 'webkitAnimationEnd'
+ }
+
+ for(var a in anims){
+ if( el.style[a] !== undefined ){
+ return anims[a];
+ }
+ }
+ }-*/;
+
+ private static final String ANIMATION_PROPERTY_NAME = whichAnimationProperty();
+
+ private static native String whichAnimationProperty()
+ /*-{
+ var el = document.createElement('fakeelement');
+ var anims = [
+ 'animation',
+ 'oAnimation',
+ 'mozAnimation',
+ 'webkitAnimation'
+ ]
+
+ for(var i=0; i < anims.length; i++) {
+ if( el.style[anims[i]] !== undefined ){
+ return anims[i];
+ }
+ }
+ }-*/;
+
+}
diff --git a/client/src/com/vaadin/client/ui/VNotification.java b/client/src/com/vaadin/client/ui/VNotification.java
index 93dc26f8be..48a3ba6df4 100644
--- a/client/src/com/vaadin/client/ui/VNotification.java
+++ b/client/src/com/vaadin/client/ui/VNotification.java
@@ -17,13 +17,12 @@
package com.vaadin.client.ui;
import java.util.ArrayList;
-import java.util.Date;
import java.util.EventObject;
import java.util.Iterator;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.DOM;
@@ -33,6 +32,7 @@ import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.AnimationUtil;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.UIDL;
@@ -66,16 +66,11 @@ public class VNotification extends VOverlay {
private static final int mouseMoveThreshold = 7;
private static final int Z_INDEX_BASE = 20000;
public static final String STYLE_SYSTEM = "system";
- private static final int FADE_ANIMATION_INTERVAL = 50; // == 20 fps
private static final ArrayList<VNotification> notifications = new ArrayList<VNotification>();
- private int startOpacity = 90;
- private int fadeMsec = 400;
- private int delayMsec = 1000;
-
- private Timer fader;
- private Timer delay;
+ private boolean infiniteDelay = false;
+ private int cssTransitionDelay = -1;
private int x = -1;
private int y = -1;
@@ -103,13 +98,14 @@ public class VNotification extends VOverlay {
@Deprecated
public VNotification(int delayMsec) {
this();
- this.delayMsec = delayMsec;
+ setDelay(delayMsec);
+
if (BrowserInfo.get().isTouchDevice()) {
new Timer() {
@Override
public void run() {
if (isAttached()) {
- fade();
+ hide();
}
}
}.schedule(delayMsec + TOUCH_DEVICE_IDLE_DELAY);
@@ -127,24 +123,17 @@ public class VNotification extends VOverlay {
@Deprecated
public VNotification(int delayMsec, int fadeMsec, int startOpacity) {
this(delayMsec);
- this.fadeMsec = fadeMsec;
- this.startOpacity = startOpacity;
+ AnimationUtil.setAnimationDuration(getElement(), fadeMsec + "ms");
+ getElement().getStyle().setOpacity(startOpacity / 100);
}
- public void startDelay() {
- DOM.removeEventPreview(this);
- if (delayMsec > 0) {
- if (delay == null) {
- delay = new Timer() {
- @Override
- public void run() {
- fade();
- }
- };
- delay.schedule(delayMsec);
- }
- } else if (delayMsec == 0) {
- fade();
+ private void setDelay(int delayMsec) {
+ if (delayMsec < 0) {
+ infiniteDelay = true;
+ cssTransitionDelay = 0;
+ } else {
+ infiniteDelay = false;
+ cssTransitionDelay = delayMsec;
}
}
@@ -237,12 +226,17 @@ public class VNotification extends VOverlay {
}
public void show(Position position, String style) {
- setOpacity(getElement(), startOpacity);
+ if (temporaryStyle != null) {
+ removeStyleName(temporaryStyle);
+ removeStyleDependentName(temporaryStyle);
+ temporaryStyle = null;
+ }
if (style != null) {
temporaryStyle = style;
addStyleName(style);
addStyleDependentName(style);
}
+
super.show();
notifications.add(this);
setPosition(position);
@@ -259,97 +253,59 @@ public class VNotification extends VOverlay {
@Override
public void hide() {
DOM.removeEventPreview(this);
- cancelDelay();
- cancelFade();
- if (temporaryStyle != null) {
- removeStyleName(temporaryStyle);
- removeStyleDependentName(temporaryStyle);
- temporaryStyle = null;
+ if (cssTransitionDelay >= 0) {
+ AnimationUtil.setAnimationDelay(getElement(), cssTransitionDelay
+ + "ms");
}
super.hide();
notifications.remove(this);
fireEvent(new HideEvent(this));
}
- public void fade() {
- DOM.removeEventPreview(this);
- cancelDelay();
- if (fader == null) {
- fader = new Timer() {
- private final long start = new Date().getTime();
-
- @Override
- public void run() {
- /*
- * To make animation smooth, don't count that event happens
- * on time. Reduce opacity according to the actual time
- * spent instead of fixed decrement.
- */
- long now = new Date().getTime();
- long timeEplaced = now - start;
- float remainingFraction = 1 - timeEplaced
- / (float) fadeMsec;
- int opacity = (int) (startOpacity * remainingFraction);
- if (opacity <= 0) {
- cancel();
- hide();
- } else {
- setOpacity(getElement(), opacity);
- }
- }
- };
- fader.scheduleRepeating(FADE_ANIMATION_INTERVAL);
- }
- }
-
public void setPosition(com.vaadin.shared.Position position) {
- final Element el = getElement();
- el.getStyle().clearTop();
- el.getStyle().clearLeft();
- el.getStyle().clearBottom();
- el.getStyle().clearRight();
+ final Style style = getElement().getStyle();
+ style.clearTop();
+ style.clearRight();
+ style.clearBottom();
+ style.clearLeft();
switch (position) {
case TOP_LEFT:
- el.getStyle().setTop(0, Unit.PX);
- el.getStyle().setLeft(0, Unit.PX);
+ style.setTop(0, Unit.PX);
+ style.setLeft(0, Unit.PX);
break;
case TOP_RIGHT:
- el.getStyle().setTop(0, Unit.PX);
- el.getStyle().setRight(0, Unit.PX);
+ style.setTop(0, Unit.PX);
+ style.setRight(0, Unit.PX);
break;
case MIDDLE_LEFT:
center();
- el.getStyle().setLeft(0, Unit.PX);
+ style.setLeft(0, Unit.PX);
break;
case MIDDLE_RIGHT:
center();
- el.getStyle().clearLeft();
- el.getStyle().setRight(0, Unit.PX);
+ style.clearLeft();
+ style.setRight(0, Unit.PX);
break;
case BOTTOM_RIGHT:
- // Avoiding strings would be ugly since another Position is imported
- // TODO this is most likely redundant
- el.getStyle().setProperty("position", "absolute");
-
- el.getStyle().setBottom(0, Unit.PX);
- el.getStyle().setRight(0, Unit.PX);
+ style.setBottom(0, Unit.PX);
+ style.setRight(0, Unit.PX);
break;
case BOTTOM_LEFT:
- el.getStyle().setBottom(0, Unit.PX);
- el.getStyle().setLeft(0, Unit.PX);
+ style.setBottom(0, Unit.PX);
+ style.setLeft(0, Unit.PX);
break;
case TOP_CENTER:
center();
- el.getStyle().setTop(0, Unit.PX);
+ style.setTop(0, Unit.PX);
break;
case BOTTOM_CENTER:
center();
- el.getStyle().clearTop();
- el.getStyle().setBottom(0, Unit.PX);
+ style.clearTop();
+ style.setBottom(0, Unit.PX);
break;
case ASSISTIVE:
- el.getStyle().setTop(-2000, Unit.PX);
- el.getStyle().setLeft(-2000, Unit.PX);
+ style.setTop(-2000, Unit.PX);
+ style.setLeft(-2000, Unit.PX);
break;
default:
case MIDDLE_CENTER:
@@ -358,49 +314,25 @@ public class VNotification extends VOverlay {
}
}
- private void cancelFade() {
- if (fader != null) {
- fader.cancel();
- fader = null;
- }
- }
-
- private void cancelDelay() {
- if (delay != null) {
- delay.cancel();
- delay = null;
- }
- }
-
- private void setOpacity(Element el, int opacity) {
- el.getStyle().setOpacity(opacity / 100.0);
- if (BrowserInfo.get().isIE()) {
- el.getStyle().setProperty("filter",
- "Alpha(opacity=" + opacity + ")");
- }
- }
-
@Override
public void onBrowserEvent(Event event) {
DOM.removeEventPreview(this);
- if (fader == null) {
- fade();
- }
+ hide();
}
@Override
public boolean onEventPreview(Event event) {
int type = DOM.eventGetType(event);
// "modal"
- if (delayMsec == -1 || temporaryStyle == STYLE_SYSTEM) {
+ if (infiniteDelay || temporaryStyle == STYLE_SYSTEM) {
if (type == Event.ONCLICK) {
if (DOM.isOrHasChild(getElement(), DOM.eventGetTarget(event))) {
- fade();
+ hide();
return false;
}
} else if (type == Event.ONKEYDOWN
&& event.getKeyCode() == KeyCodes.KEY_ESCAPE) {
- fade();
+ hide();
return false;
}
if (temporaryStyle == STYLE_SYSTEM) {
@@ -418,19 +350,19 @@ public class VNotification extends VOverlay {
y = DOM.eventGetClientY(event);
} else if (Math.abs(DOM.eventGetClientX(event) - x) > mouseMoveThreshold
|| Math.abs(DOM.eventGetClientY(event) - y) > mouseMoveThreshold) {
- startDelay();
+ hide();
}
break;
case Event.ONMOUSEDOWN:
case Event.ONMOUSEWHEEL:
case Event.ONSCROLL:
- startDelay();
+ hide();
break;
case Event.ONKEYDOWN:
if (event.getRepeat()) {
return true;
}
- startDelay();
+ hide();
break;
default:
break;
@@ -511,17 +443,17 @@ public class VNotification extends VOverlay {
public static VNotification createNotification(int delayMsec, Widget owner) {
final VNotification notification = GWT.create(VNotification.class);
notification.setWaiAriaRole(null);
+ notification.setDelay(delayMsec);
- notification.delayMsec = delayMsec;
if (BrowserInfo.get().isTouchDevice()) {
new Timer() {
@Override
public void run() {
if (notification.isAttached()) {
- notification.fade();
+ notification.hide();
}
}
- }.schedule(notification.delayMsec + TOUCH_DEVICE_IDLE_DELAY);
+ }.schedule(delayMsec + TOUCH_DEVICE_IDLE_DELAY);
}
notification.setOwner(owner);
return notification;
diff --git a/client/src/com/vaadin/client/ui/VOverlay.java b/client/src/com/vaadin/client/ui/VOverlay.java
index aca016a90f..ab493ce383 100644
--- a/client/src/com/vaadin/client/ui/VOverlay.java
+++ b/client/src/com/vaadin/client/ui/VOverlay.java
@@ -16,11 +16,15 @@
package com.vaadin.client.ui;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
import com.google.gwt.animation.client.Animation;
import com.google.gwt.aria.client.Roles;
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;
@@ -33,11 +37,13 @@ import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.AnimationUtil;
+import com.vaadin.client.AnimationUtil.AnimationEndListener;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ComputedStyle;
import com.vaadin.client.Util;
-import com.vaadin.client.VConsole;
/**
* <p>
@@ -158,6 +164,9 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
*/
public static final String CLASSNAME_CONTAINER = "v-overlay-container";
+ private static final String ADDITIONAL_CLASSNAME_ANIMATE_IN = "animate-in";
+ private static final String ADDITIONAL_CLASSNAME_ANIMATE_OUT = "animate-out";
+
/**
* The shadow element for this overlay.
*
@@ -423,8 +432,10 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
@Override
public void show() {
current = this;
- super.show();
- if (isAnimationEnabled()) {
+
+ boolean hasAnimationIn = maybeShowWithAnimation();
+
+ if (isAnimationEnabled() && !hasAnimationIn) {
new ResizeAnimation().run(POPUP_PANEL_ANIMATION_DURATION);
} else {
positionOrSizeUpdated(1.0);
@@ -432,6 +443,44 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
current = null;
}
+ private boolean maybeShowWithAnimation() {
+ boolean isAttached = isAttached() && isShowing();
+ super.show();
+
+ // Don't animate if already visible or browser is IE8 or IE9 (no CSS
+ // animation support)
+ if (isAttached || BrowserInfo.get().isIE8()
+ || BrowserInfo.get().isIE9()) {
+ return false;
+ } else {
+ // Check if animations are used
+ addStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
+ if (isShadowEnabled()) {
+ shadow.addClassName(CLASSNAME_SHADOW + "-"
+ + ADDITIONAL_CLASSNAME_ANIMATE_IN);
+ }
+
+ ComputedStyle cs = new ComputedStyle(getElement());
+ String animationName = AnimationUtil.getAnimationName(cs);
+ if (animationName == null) {
+ animationName = "";
+ }
+
+ if (animationName.contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
+ AnimationUtil.registerAnimationEndEventListener(getElement(),
+ getAnimationEndListener(false));
+ return true;
+ } else {
+ removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_IN);
+ if (isShadowEnabled()) {
+ shadow.removeClassName(CLASSNAME_SHADOW + "-"
+ + ADDITIONAL_CLASSNAME_ANIMATE_IN);
+ }
+ return false;
+ }
+ }
+ }
+
@Override
protected void onDetach() {
super.onDetach();
@@ -519,17 +568,17 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
return;
}
// Calculate proper z-index
- String zIndex = null;
+ int zIndex = -1;
try {
// Odd behaviour with Windows Hosted Mode forces us to use
// this redundant try/catch block (See dev.vaadin.com #2011)
- zIndex = DOM.getStyleAttribute(getElement(), "zIndex");
+ zIndex = Integer.parseInt(getElement().getStyle().getZIndex());
} catch (Exception ignore) {
// Ignored, will cause no harm
- zIndex = "1000";
+ zIndex = 1000;
}
- if (zIndex == null) {
- zIndex = "" + Z_INDEX;
+ if (zIndex == -1) {
+ zIndex = Z_INDEX;
}
// Calculate position and size
if (BrowserInfo.get().isIE()) {
@@ -570,7 +619,7 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
* @deprecated See main JavaDoc for VOverlay
*/
@Deprecated
- private void updateShadowPosition(final double progress, String zIndex,
+ private void updateShadowPosition(final double progress, int zIndex,
PositionAndSize positionAndSize) {
// Opera needs some shaking to get parts of the shadow showing
// properly (ticket #2704)
@@ -582,12 +631,8 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
}
updatePositionAndSize(shadow, positionAndSize);
- shadow.getStyle().setProperty("zIndex", zIndex);
- if (progress < 0.9) {
- shadow.getStyle().setDisplay(Display.NONE);
- } else {
- shadow.getStyle().clearDisplay();
- }
+ shadow.getStyle().setZIndex(zIndex);
+ shadow.getStyle().setProperty("display", progress < 0.9 ? "none" : "");
// Opera fix, part 2 (ticket #2704)
if (BrowserInfo.get().isOpera()) {
@@ -758,7 +803,9 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
if (ac == null) {
// could not figure out which one we belong to, styling will
// probably fail
- VConsole.error("Could not determine ApplicationConnection for Overlay. Overlay will be attached directly to the root panel");
+ Logger.getLogger(getClass().getSimpleName())
+ .log(Level.WARNING,
+ "Could not determine ApplicationConnection for Overlay. Overlay will be attached directly to the root panel");
return RootPanel.get().getElement();
} else {
return getOverlayContainer(ac);
@@ -873,4 +920,80 @@ public class VOverlay extends PopupPanel implements CloseHandler<PopupPanel> {
return $wnd.innerHeight !== undefined ? $wnd.innerHeight :-1;
}-*/;
-}
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.google.gwt.user.client.ui.PopupPanel#hide()
+ */
+ @Override
+ public void hide() {
+ hide(false);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.google.gwt.user.client.ui.PopupPanel#hide(boolean)
+ */
+ @Override
+ public void hide(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);
+ } else {
+ removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
+ if (isShadowEnabled()) {
+ shadow.removeClassName(CLASSNAME_SHADOW + "-"
+ + ADDITIONAL_CLASSNAME_ANIMATE_OUT);
+ }
+ super.hide(autoClosed);
+ }
+ }
+ }
+
+ private AnimationEndListener getAnimationEndListener(
+ final boolean autoClosed) {
+ return new AnimationEndListener() {
+
+ @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)) {
+ removeStyleDependentName(ADDITIONAL_CLASSNAME_ANIMATE_OUT);
+ if (isShadowEnabled()) {
+ shadow.removeClassName(CLASSNAME_SHADOW + "-"
+ + ADDITIONAL_CLASSNAME_ANIMATE_OUT);
+ }
+ VOverlay.super.hide(autoClosed);
+ }
+ }
+ };
+ }
+} \ No newline at end of file
diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java
index 9b1f7a6f3c..e2cc33744e 100644
--- a/client/src/com/vaadin/client/ui/VWindow.java
+++ b/client/src/com/vaadin/client/ui/VWindow.java
@@ -30,6 +30,7 @@ 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;
@@ -200,6 +201,9 @@ 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() {
@@ -303,7 +307,10 @@ public class VWindow extends VWindowOverlay implements
}
private static VWindow getTopmostWindow() {
- return windowOrder.get(windowOrder.size() - 1);
+ if (windowOrder.size() > 0) {
+ return windowOrder.get(windowOrder.size() - 1);
+ }
+ return null;
}
/** For internal use only. May be removed or replaced in the future. */
@@ -1030,6 +1037,15 @@ 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);
}
@@ -1263,7 +1279,7 @@ public class VWindow extends VWindowOverlay implements
// are not cancelled here and target this window to be consume():d
// meaning the event won't be sent to the rest of the preview handlers.
- if (getTopmostWindow().vaadinModality) {
+ if (getTopmostWindow() != null && getTopmostWindow().vaadinModality) {
// Topmost window is modal. Cancel the event if it targets something
// outside that window (except debug console...)
if (DOM.getCaptureElement() != null) {
diff --git a/client/src/com/vaadin/client/ui/window/WindowConnector.java b/client/src/com/vaadin/client/ui/window/WindowConnector.java
index 5c8f5e2d2d..40185099d3 100644
--- a/client/src/com/vaadin/client/ui/window/WindowConnector.java
+++ b/client/src/com/vaadin/client/ui/window/WindowConnector.java
@@ -192,6 +192,15 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
// 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) {
+ getWidget().getElement().removeAllChildren();
+ getWidget().getElement().appendChild(getWidget().windowClone);
+ }
+ // Clean reference
+ getWidget().windowClone = null;
}
@Override