Revision: 9985
Author: jlaba...@google.com
Date: Wed Apr 13 08:22:34 2011
Log: Resubmtiting r9970.
Review at http://gwt-code-reviews.appspot.com/1355805/
Review by: fabb...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=9985
Added:
/trunk/user/src/com/google/gwt/animation/client/AnimationImpl.java
/trunk/user/src/com/google/gwt/animation/client/AnimationImplMozAnimTiming.java
/trunk/user/src/com/google/gwt/animation/client/AnimationImplTimer.java
/trunk/user/src/com/google/gwt/animation/client/AnimationImplWebkitAnimTiming.java
Modified:
/trunk/user/src/com/google/gwt/animation/Animation.gwt.xml
/trunk/user/src/com/google/gwt/animation/client/Animation.java
/trunk/user/src/com/google/gwt/layout/client/Layout.java
/trunk/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
/trunk/user/src/com/google/gwt/user/client/ui/DeckPanel.java
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/animation/client/AnimationImpl.java Wed
Apr 13 08:22:34 2011
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * 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.google.gwt.animation.client;
+
+import com.google.gwt.dom.client.Element;
+
+/**
+ * Base class for animation implementations.
+ */
+abstract class AnimationImpl {
+
+ /**
+ * Cancel the animation.
+ */
+ public abstract void cancel(Animation animation);
+
+ /**
+ * Run the animation with an optional bounding element.
+ */
+ public abstract void run(Animation animation, Element element);
+
+ /**
+ * Update the {@link Animation}.
+ *
+ * @param animation the {@link Animation}
+ * @param curTime the current time
+ * @return true if the animation is complete, false if still running
+ */
+ protected final boolean updateAnimation(Animation animation, double
curTime) {
+ return animation.isRunning() && animation.update(curTime);
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/src/com/google/gwt/animation/client/AnimationImplMozAnimTiming.java
Wed Apr 13 08:22:34 2011
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * 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.google.gwt.animation.client;
+
+import com.google.gwt.dom.client.Element;
+
+/**
+ * Implementation using <code>mozRequestAnimationFrame</code>.
+ *
+ * @see <a
href="https://developer.mozilla.org/en/DOM/window.mozRequestAnimationFrame">
+ * Documentation on the MDN</a>
+ */
+class AnimationImplMozAnimTiming extends AnimationImpl {
+
+ private int handle;
+
+ @Override
+ public void cancel(Animation animation) {
+ handle++;
+ }
+
+ @Override
+ public void run(Animation animation, Element element) {
+ handle++;
+ nativeRun(animation);
+ }
+
+ private native void nativeRun(Animation animation) /*-{
+ var self = this;
+ var handle =
th...@com.google.gwt.animation.client.AnimationImplMozAnimTiming::handle;
+ var callback = $entry(function(time) {
+ if (handle !=
se...@com.google.gwt.animation.client.AnimationImplMozAnimTiming::handle) {
+ return; // cancelled
+ }
+ var complete =
se...@com.google.gwt.animation.client.AnimationImpl::updateAnimation(Lcom/google/gwt/animation/client/Animation;D)(animation,
time);
+ if (!complete) {
+ $wnd.mozRequestAnimationFrame(callback);
+ }
+ });
+
+ $wnd.mozRequestAnimationFrame(callback);
+ }-*/;
+}
=======================================
--- /dev/null
+++ /trunk/user/src/com/google/gwt/animation/client/AnimationImplTimer.java
Wed Apr 13 08:22:34 2011
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * 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.google.gwt.animation.client;
+
+import com.google.gwt.core.client.Duration;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.dom.client.Element;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation using a timer.
+ */
+class AnimationImplTimer extends AnimationImpl {
+
+ /**
+ * The default time in milliseconds between frames.
+ */
+ private static final int DEFAULT_FRAME_DELAY = 25;
+
+ /**
+ * The {@link Scheduler.RepeatingCommand} that applies the animations.
+ */
+ private static Scheduler.RepeatingCommand animationCommand;
+
+ /**
+ * The {@link Animation Animations} that are currently in progress.
+ */
+ private static List<Animation> animations = null;
+
+ @Override
+ public void cancel(Animation animation) {
+ animations.remove(animation);
+ }
+
+ @Override
+ public void run(Animation animation, Element element) {
+ // Add to the list of animations
+
+ // We use a static list of animations and a single timer, and create
them
+ // only if we are the only active animation. This is safe since JS is
+ // single-threaded.
+ if (animations == null) {
+ animations = new ArrayList<Animation>();
+ animationCommand = new Scheduler.RepeatingCommand() {
+ public boolean execute() {
+ // Duplicate the animations list in case it changes as we
iterate over it
+ Animation[] curAnimations = new Animation[animations.size()];
+ curAnimations = animations.toArray(curAnimations);
+
+ // Iterate through the animations
+ double curTime = Duration.currentTimeMillis();
+ for (Animation animation : curAnimations) {
+ if (updateAnimation(animation, curTime)) {
+ // We can't just remove the animation at the index, because
calling
+ // animation.update may have the side effect of canceling
this
+ // animation, running new animations, or canceling other
animations.
+ animations.remove(animation);
+ }
+ }
+
+ return (animations.size() > 0);
+ }
+ };
+ }
+ animations.add(animation);
+
+ // Restart the timer if this is the only animation
+ if (animations.size() == 1) {
+ Scheduler.get().scheduleFixedDelay(animationCommand,
DEFAULT_FRAME_DELAY);
+ }
+ }
+}
=======================================
--- /dev/null
+++
/trunk/user/src/com/google/gwt/animation/client/AnimationImplWebkitAnimTiming.java
Wed Apr 13 08:22:34 2011
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * 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.google.gwt.animation.client;
+
+import com.google.gwt.dom.client.Element;
+
+/**
+ * Implementation using <code>webkitRequestAnimationFrame</code> and
+ * <code>webkitCancelRequestAnimationFrame</code>.
+ *
+ * @see <a
+ *
href="http://www.chromium.org/developers/web-platform-status#TOC-requestAnimationFrame">
+ * Chromium Web Platform Status</a>
+ * @see <a href="http://webstuff.nfshost.com/anim-timing/Overview.html">
+ * webkit draft spec</a>
+ */
+class AnimationImplWebkitAnimTiming extends AnimationImpl {
+
+ private static native void cancel(double handle) /*-{
+ $wnd.webkitCancelRequestAnimationFrame(handle);
+ }-*/;
+
+ private double handle;
+
+ @Override
+ public void cancel(Animation animation) {
+ cancel(handle);
+ }
+
+ @Override
+ public void run(Animation animation, Element element) {
+ handle = nativeRun(animation, element);
+ }
+
+ private native double nativeRun(Animation animation, Element element)
/*-{
+ var self = this;
+ var callback = $entry(function(time) {
+ // Chrome 10 does not pass the 'time' argument, so we fake it.
+ time = time ||
@com.google.gwt.core.client.Duration::currentTimeMillis()();
+ var complete =
se...@com.google.gwt.animation.client.AnimationImpl::updateAnimation(Lcom/google/gwt/animation/client/Animation;D)(animation,
time);
+ if (!complete) {
+
se...@com.google.gwt.animation.client.AnimationImplWebkitAnimTiming::handle
= $wnd.webkitRequestAnimationFrame(callback, element);
+ }
+ });
+
+ return $wnd.webkitRequestAnimationFrame(callback, element);
+ }-*/;
+}
=======================================
--- /trunk/user/src/com/google/gwt/animation/Animation.gwt.xml Mon Apr 11
09:09:49 2011
+++ /trunk/user/src/com/google/gwt/animation/Animation.gwt.xml Wed Apr 13
08:22:34 2011
@@ -18,7 +18,48 @@
<!--
-->
<module>
<inherits name="com.google.gwt.core.Core"/>
-
- <!-- Include User module to inherit Timer -->
- <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.dom.DOM"/>
+ <inherits name="com.google.gwt.user.UserAgent"/>
+
+ <define-property name="animationTimingSupport" values="no,moz,webkit"/>
+ <collapse-property name="animationTimingSupport" values="*"/>
+ <property-provider name="animationTimingSupport"><![CDATA[
+ if ($wnd.webkitRequestAnimationFrame &&
$wnd.webkitCancelRequestAnimationFrame) {
+ return "webkit";
+ }
+ if ($wnd.mozRequestAnimationFrame) {
+ return "moz";
+ }
+ return "no";
+ ]]></property-provider>
+
+ <set-property name="animationTimingSupport" value="no">
+ <any>
+ <when-property-is name="user.agent" value="ie6" />
+ <when-property-is name="user.agent" value="ie8" />
+ <when-property-is name="user.agent" value="ie9" />
+ <when-property-is name="user.agent" value="opera" />
+ </any>
+ </set-property>
+
+ <!-- Fallback implementation, based on a timer -->
+ <replace-with class="com.google.gwt.animation.client.AnimationImplTimer">
+ <when-type-is class="com.google.gwt.animation.client.AnimationImpl"/>
+ </replace-with>
+
+ <!-- Implementation based on mozRequestAnimationFrame -->
+ <!-- Only used in Firefox when support has been detected -->
+ <replace-with
class="com.google.gwt.animation.client.AnimationImplMozAnimTiming">
+ <when-type-is class="com.google.gwt.animation.client.AnimationImpl"/>
+ <when-property-is name="animationTimingSupport" value="moz" />
+ <when-property-is name="user.agent" value="gecko1_8"/>
+ </replace-with>
+
+ <!-- Implementation based on webkitRequestAnimationFrame -->
+ <!-- Only used in WebKit when support has been detected -->
+ <replace-with
class="com.google.gwt.animation.client.AnimationImplWebkitAnimTiming">
+ <when-type-is class="com.google.gwt.animation.client.AnimationImpl"/>
+ <when-property-is name="animationTimingSupport" value="webkit" />
+ <when-property-is name="user.agent" value="safari"/>
+ </replace-with>
</module>
=======================================
--- /trunk/user/src/com/google/gwt/animation/client/Animation.java Mon Apr
11 09:09:49 2011
+++ /trunk/user/src/com/google/gwt/animation/client/Animation.java Wed Apr
13 08:22:34 2011
@@ -16,55 +16,16 @@
package com.google.gwt.animation.client;
import com.google.gwt.core.client.Duration;
-import com.google.gwt.user.client.Timer;
-
-import java.util.ArrayList;
-import java.util.List;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.Element;
/**
* An {@link Animation} is a continuous event that updates progressively
over
* time at a non-fixed frame rate.
*/
public abstract class Animation {
- /**
- * The default time in milliseconds between frames.
- */
- private static final int DEFAULT_FRAME_DELAY = 25;
-
- /**
- * The {@link Animation Animations} that are currently in progress.
- */
- private static List<Animation> animations = null;
-
- /**
- * The {@link Timer} that applies the animations.
- */
- private static Timer animationTimer = null;
-
- /**
- * Update all {@link Animation Animations}.
- */
- private static void updateAnimations() {
- // Duplicate the animations list in case it changes as we iterate over
it
- Animation[] curAnimations = new Animation[animations.size()];
- curAnimations = animations.toArray(curAnimations);
-
- // Iterator through the animations
- double curTime = Duration.currentTimeMillis();
- for (Animation animation : curAnimations) {
- if (animation.running && animation.update(curTime)) {
- // We can't just remove the animation at the index, because calling
- // animation.update may have the side effect of canceling this
- // animation, running new animations, or canceling other
animations.
- animations.remove(animation);
- }
- }
-
- // Reschedule the timer
- if (animations.size() > 0) {
- animationTimer.schedule(DEFAULT_FRAME_DELAY);
- }
- }
+
+ private AnimationImpl impl = GWT.create(AnimationImpl.class);
/**
* The duration of the {@link Animation} in milliseconds.
@@ -97,7 +58,7 @@
return;
}
- animations.remove(this);
+ impl.cancel(this);
onCancel();
started = false;
running = false;
@@ -106,22 +67,64 @@
/**
* Immediately run this animation. If the animation is already running,
it
* will be canceled first.
+ * <p>
+ * This is equivalent to <code>run(duration, null)</code>.
*
* @param duration the duration of the animation in milliseconds
+ * @see #run(int, Element)
*/
public void run(int duration) {
- run(duration, Duration.currentTimeMillis());
+ run(duration, null);
+ }
+
+ /**
+ * Immediately run this animation. If the animation is already running,
it
+ * will be canceled first.
+ * <p>
+ * If the element is not <code>null</code>, the {@link #onUpdate(double)}
+ * method might be called only if the element may be visible (generally
left
+ * at the appreciation of the browser). Otherwise, it will be called
+ * unconditionally.
+ *
+ * @param duration the duration of the animation in milliseconds
+ * @param element the element that visually bounds the entire animation
+ */
+ public void run(int duration, Element element) {
+ run(duration, Duration.currentTimeMillis(), element);
}
/**
* Run this animation at the given startTime. If the startTime has
already
- * passed, the animation will be synchronize as if it started at the
specified
- * start time. If the animation is already running, it will be canceled
first.
+ * passed, the animation will run synchronously as if it started at the
+ * specified start time. If the animation is already running, it will be
+ * canceled first.
+ * <p>
+ * This is equivalent to <code>run(duration, startTime, null)</code>.
*
* @param duration the duration of the animation in milliseconds
* @param startTime the synchronized start time in milliseconds
+ * @see #run(int, double, Element)
*/
public void run(int duration, double startTime) {
+ run(duration, startTime, null);
+ }
+
+ /**
+ * Run this animation at the given startTime. If the startTime has
already
+ * passed, the animation will run synchronously as if it started at the
+ * specified start time. If the animation is already running, it will be
+ * canceled first.
+ * <p>
+ * If the element is not <code>null</code>, the {@link #onUpdate(double)}
+ * method might be called only if the element may be visible (generally
left
+ * at the appreciation of the browser). Otherwise, it will be called
+ * unconditionally.
+ *
+ * @param duration the duration of the animation in milliseconds
+ * @param startTime the synchronized start time in milliseconds
+ * @param element the element that visually bounds the entire animation
+ */
+ public void run(int duration, double startTime, Element element) {
// Cancel the animation if it is running
cancel();
@@ -135,26 +138,7 @@
return;
}
- // Add to the list of animations
-
- // We use a static list of animations and a single timer, and create
them
- // only if we are the only active animation. This is safe since JS is
- // single-threaded.
- if (animations == null) {
- animations = new ArrayList<Animation>();
- animationTimer = new Timer() {
- @Override
- public void run() {
- updateAnimations();
- }
- };
- }
- animations.add(this);
-
- // Restart the timer if there is the only animation
- if (animations.size() == 1) {
- animationTimer.schedule(DEFAULT_FRAME_DELAY);
- }
+ impl.run(this, element);
}
/**
@@ -206,6 +190,14 @@
* @param progress a double, normally between 0.0 and 1.0 (inclusive)
*/
protected abstract void onUpdate(double progress);
+
+ /**
+ * Is the {@link Animation} running, even if {@link #onStart()} has not
yet
+ * been called.
+ */
+ boolean isRunning() {
+ return running;
+ }
/**
* Update the {@link Animation}.
@@ -213,7 +205,7 @@
* @param curTime the current time
* @return true if the animation is complete, false if still running
*/
- private boolean update(double curTime) {
+ boolean update(double curTime) {
boolean finished = curTime >= startTime + duration;
if (started && !finished) {
// Animation is in progress.
=======================================
--- /trunk/user/src/com/google/gwt/layout/client/Layout.java Mon Apr 11
09:09:49 2011
+++ /trunk/user/src/com/google/gwt/layout/client/Layout.java Wed Apr 13
08:22:34 2011
@@ -578,7 +578,7 @@
}
};
- animation.run(duration);
+ animation.run(duration, parentElem);
}
/**
=======================================
--- /trunk/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
Tue Apr 12 06:14:57 2011
+++ /trunk/user/src/com/google/gwt/user/cellview/client/CellBrowser.java
Wed Apr 13 08:22:34 2011
@@ -712,7 +712,7 @@
if (isAnimationEnabled()) {
// Animate the scrolling.
startScrollLeft = elem.getScrollLeft();
- run(250);
+ run(250, elem);
} else {
// Scroll instantly.
onComplete();
=======================================
--- /trunk/user/src/com/google/gwt/user/client/ui/DeckPanel.java Mon Apr 11
09:09:49 2011
+++ /trunk/user/src/com/google/gwt/user/client/ui/DeckPanel.java Wed Apr 13
08:22:34 2011
@@ -105,7 +105,25 @@
// Start the animation
if (animate) {
- run(ANIMATION_DURATION);
+ // Figure out if the deck panel has a fixed height
+ com.google.gwt.dom.client.Element deckElem =
container1.getParentElement();
+ int deckHeight = deckElem.getOffsetHeight();
+ if (growing) {
+ fixedHeight = container2.getOffsetHeight();
+ container2.getStyle().setPropertyPx("height",
+ Math.max(1, fixedHeight - 1));
+ } else {
+ fixedHeight = container1.getOffsetHeight();
+ container1.getStyle().setPropertyPx("height",
+ Math.max(1, fixedHeight - 1));
+ }
+ if (deckElem.getOffsetHeight() != deckHeight) {
+ fixedHeight = -1;
+ }
+
+ // Only scope to the deck if it's fixed height, otherwise it can
affect
+ // the rest of the page, even if it's not visible to the user.
+ run(ANIMATION_DURATION, fixedHeight == -1 ? null : deckElem);
} else {
onInstantaneousRun();
}
@@ -139,22 +157,6 @@
@Override
protected void onStart() {
- // Figure out if the deck panel has a fixed height
- com.google.gwt.dom.client.Element deckElem =
container1.getParentElement();
- int deckHeight = deckElem.getOffsetHeight();
- if (growing) {
- fixedHeight = container2.getOffsetHeight();
- container2.getStyle().setPropertyPx("height",
- Math.max(1, fixedHeight - 1));
- } else {
- fixedHeight = container1.getOffsetHeight();
- container1.getStyle().setPropertyPx("height",
- Math.max(1, fixedHeight - 1));
- }
- if (deckElem.getOffsetHeight() != deckHeight) {
- fixedHeight = -1;
- }
-
// Start the animation
DOM.setStyleAttribute(container1, "overflow", "hidden");
DOM.setStyleAttribute(container2, "overflow", "hidden");
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors