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

Reply via email to