summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-05 03:43:55 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-05 03:43:55 +0000
commit4ce7e15da651c6221720e33dc8c500830d4b6b8a (patch)
tree376ef1d7e7951dae3376e65f4edee9e7367adc89 /app
parent5ee3ca64cdf2d00f82a1bc36f36e8ab5e520b4de (diff)
downloadchromium_src-4ce7e15da651c6221720e33dc8c500830d4b6b8a.zip
chromium_src-4ce7e15da651c6221720e33dc8c500830d4b6b8a.tar.gz
chromium_src-4ce7e15da651c6221720e33dc8c500830d4b6b8a.tar.bz2
Refactors animation to allow for cleaner subclassing. I'm doing this
for creating a different animation subclass (which you'll see shortly). BUG=none TEST=none Review URL: http://codereview.chromium.org/1961001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46433 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app')
-rw-r--r--app/animation.cc172
-rw-r--r--app/animation.h128
-rw-r--r--app/animation_container.cc69
-rw-r--r--app/animation_container.h48
-rw-r--r--app/animation_container_unittest.cc6
-rw-r--r--app/animation_unittest.cc62
-rw-r--r--app/app_base.gypi4
-rw-r--r--app/linear_animation.cc91
-rw-r--r--app/linear_animation.h70
-rw-r--r--app/slide_animation.cc35
-rw-r--r--app/slide_animation.h22
-rw-r--r--app/test_animation_delegate.h3
-rw-r--r--app/throb_animation.cc8
-rw-r--r--app/tween.cc81
-rw-r--r--app/tween.h43
15 files changed, 536 insertions, 306 deletions
diff --git a/app/animation.cc b/app/animation.cc
index 5e9d10c..2dc2bcf6 100644
--- a/app/animation.cc
+++ b/app/animation.cc
@@ -4,120 +4,87 @@
#include "app/animation.h"
-#include "app/animation_container.h"
+#include "app/tween.h"
#include "gfx/rect.h"
#if defined(OS_WIN)
#include "base/win_util.h"
#endif
-using base::Time;
-using base::TimeDelta;
-
-Animation::Animation(int frame_rate,
- AnimationDelegate* delegate)
- : animating_(false),
- frame_rate_(frame_rate),
- timer_interval_(CalculateInterval(frame_rate)),
- state_(0.0),
- delegate_(delegate) {
-}
-
-Animation::Animation(int duration,
- int frame_rate,
- AnimationDelegate* delegate)
- : animating_(false),
- frame_rate_(frame_rate),
- timer_interval_(CalculateInterval(frame_rate)),
- duration_(TimeDelta::FromMilliseconds(duration)),
- state_(0.0),
- delegate_(delegate) {
-
- SetDuration(duration);
+Animation::Animation(base::TimeDelta timer_interval)
+ : timer_interval_(timer_interval),
+ is_animating_(false),
+ delegate_(NULL) {
}
Animation::~Animation() {
- if (animating_)
+ // Don't send out notification from the destructor. Chances are the delegate
+ // owns us and is being deleted as well.
+ if (is_animating_)
container_->Stop(this);
}
-double Animation::GetCurrentValue() const {
- // Default is linear relationship, subclass to adapt.
- return state_;
-}
+void Animation::Start() {
+ if (is_animating_)
+ return;
-double Animation::CurrentValueBetween(double start, double target) const {
- return start + (target - start) * GetCurrentValue();
-}
+ if (!container_.get())
+ container_ = new AnimationContainer();
-int Animation::CurrentValueBetween(int start, int target) const {
- return static_cast<int>(CurrentValueBetween(static_cast<double>(start),
- static_cast<double>(target)));
-}
+ is_animating_ = true;
-gfx::Rect Animation::CurrentValueBetween(const gfx::Rect& start_bounds,
- const gfx::Rect& target_bounds) const {
- return gfx::Rect(CurrentValueBetween(start_bounds.x(), target_bounds.x()),
- CurrentValueBetween(start_bounds.y(), target_bounds.y()),
- CurrentValueBetween(start_bounds.width(),
- target_bounds.width()),
- CurrentValueBetween(start_bounds.height(),
- target_bounds.height()));
+ container_->Start(this);
+
+ AnimationStarted();
}
-void Animation::Start() {
- if (!animating_) {
- if (!container_.get())
- container_ = new AnimationContainer();
+void Animation::Stop() {
+ if (!is_animating_)
+ return;
- animating_ = true;
+ is_animating_ = false;
- container_->Start(this);
+ // Notify the container first as the delegate may delete us.
+ container_->Stop(this);
- if (delegate_)
- delegate_->AnimationStarted(this);
+ AnimationStopped();
+
+ if (delegate_) {
+ if (ShouldSendCanceledFromStop())
+ delegate_->AnimationCanceled(this);
+ else
+ delegate_->AnimationEnded(this);
}
}
-void Animation::Stop() {
- if (animating_) {
- animating_ = false;
+double Animation::CurrentValueBetween(double start, double target) const {
+ return Tween::ValueBetween(GetCurrentValue(), start, target);
+}
- // Notify the container first as the delegate may delete us.
- container_->Stop(this);
+int Animation::CurrentValueBetween(int start, int target) const {
+ return Tween::ValueBetween(GetCurrentValue(), start, target);
- if (delegate_) {
- if (state_ >= 1.0)
- delegate_->AnimationEnded(this);
- else
- delegate_->AnimationCanceled(this);
- }
- }
}
-void Animation::End() {
- if (animating_) {
- animating_ = false;
+gfx::Rect Animation::CurrentValueBetween(const gfx::Rect& start_bounds,
+ const gfx::Rect& target_bounds) const {
+ return Tween::ValueBetween(GetCurrentValue(), start_bounds, target_bounds);
+}
- // Notify the container first as the delegate may delete us.
- container_->Stop(this);
+void Animation::SetContainer(AnimationContainer* container) {
+ if (container == container_.get())
+ return;
- AnimateToState(1.0);
- if (delegate_)
- delegate_->AnimationEnded(this);
- }
-}
+ if (is_animating_)
+ container_->Stop(this);
-bool Animation::IsAnimating() const {
- return animating_;
-}
+ if (container)
+ container_ = container;
+ else
+ container_ = new AnimationContainer();
-void Animation::SetDuration(int duration) {
- duration_ = TimeDelta::FromMilliseconds(duration);
- if (duration_ < timer_interval_)
- duration_ = timer_interval_;
- if (animating_)
- start_time_ = container_->last_tick_time();
+ if (is_animating_)
+ container_->Start(this);
}
// static
@@ -139,41 +106,6 @@ bool Animation::ShouldRenderRichAnimation() {
return true;
}
-void Animation::SetContainer(AnimationContainer* container) {
- if (container == container_.get())
- return;
-
- if (animating_)
- container_->Stop(this);
-
- if (container)
- container_ = container;
- else
- container_ = new AnimationContainer();
-
- if (animating_)
- container_->Start(this);
-}
-
-void Animation::Step(base::TimeTicks time_now) {
- TimeDelta elapsed_time = time_now - start_time_;
- state_ = static_cast<double>(elapsed_time.InMicroseconds()) /
- static_cast<double>(duration_.InMicroseconds());
- if (state_ >= 1.0)
- state_ = 1.0;
-
- AnimateToState(state_);
-
- if (delegate_)
- delegate_->AnimationProgressed(this);
-
- if (state_ == 1.0)
- Stop();
-}
-
-TimeDelta Animation::CalculateInterval(int frame_rate) {
- int timer_interval = 1000000 / frame_rate;
- if (timer_interval < 10000)
- timer_interval = 10000;
- return TimeDelta::FromMicroseconds(timer_interval);
+void Animation::SetStartTime(base::TimeTicks start_time) {
+ start_time_ = start_time;
}
diff --git a/app/animation.h b/app/animation.h
index c483e1d..5c672c9 100644
--- a/app/animation.h
+++ b/app/animation.h
@@ -1,32 +1,26 @@
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Inspired by NSAnimation
#ifndef APP_ANIMATION_H_
#define APP_ANIMATION_H_
+#include "app/animation_container.h"
#include "base/ref_counted.h"
#include "base/time.h"
-class Animation;
-class AnimationContainer;
-
namespace gfx {
class Rect;
}
+class Animation;
+
// AnimationDelegate
//
// Implement this interface when you want to receive notifications about the
// state of an animation.
-//
class AnimationDelegate {
public:
- // Called when an animation has started.
- virtual void AnimationStarted(const Animation* animation) {
- }
-
// Called when an animation has completed.
virtual void AnimationEnded(const Animation* animation) {
}
@@ -43,41 +37,26 @@ class AnimationDelegate {
virtual ~AnimationDelegate() {}
};
-// Animation
-//
-// This class provides a basic implementation of an object that uses a timer
-// to increment its state over the specified time and frame-rate. To
-// actually do something useful with this you need to subclass it and override
-// AnimateToState and optionally GetCurrentValue to update your state.
-//
-// The Animation notifies a delegate when events of interest occur.
-//
-// The practice is to instantiate a subclass and call Init and any other
-// initialization specific to the subclass, and then call |Start|. The
-// animation uses the current thread's message loop.
+// Base class used in implementing animations. You only need use this class if
+// you're implementing a new animation type, otherwise you'll likely want one of
+// LinearAnimation, SlideAnimation, ThrobAnimation or MultiAnimation.
//
-class Animation {
+// To subclass override Step, which is invoked as the animation progresses and
+// GetCurrentValue() to return the value appropriate to the animation.
+class Animation : public AnimationContainer::Element {
public:
- // Initializes everything except the duration.
- //
- // Caller must make sure to call SetDuration() if they use this
- // constructor; it is preferable to use the full one, but sometimes
- // duration can change between calls to Start() and we need to
- // expose this interface.
- Animation(int frame_rate, AnimationDelegate* delegate);
-
- // Initializes all fields.
- Animation(int duration, int frame_rate, AnimationDelegate* delegate);
+ explicit Animation(base::TimeDelta timer_interval);
virtual ~Animation();
- // Called when the animation progresses. Subclasses override this to
- // efficiently update their state.
- virtual void AnimateToState(double state) = 0;
+ // Starts the animation. Does nothing if the animation is already running.
+ void Start();
+
+ // Stops the animation. Does nothing if the animation is not running.
+ void Stop();
// Gets the value for the current state, according to the animation
- // curve in use. This class provides only for a linear relationship,
- // however subclasses can override this to provide others.
- virtual double GetCurrentValue() const;
+ // curve in use.
+ virtual double GetCurrentValue() const = 0;
// Convenience for returning a value between |start| and |target| based on
// the current value. This is (target - start) * GetCurrentValue() + start.
@@ -86,27 +65,6 @@ class Animation {
gfx::Rect CurrentValueBetween(const gfx::Rect& start_bounds,
const gfx::Rect& target_bounds) const;
- // Start the animation.
- void Start();
-
- // Stop the animation.
- void Stop();
-
- // Skip to the end of the current animation.
- void End();
-
- // Return whether this animation is animating.
- bool IsAnimating() const;
-
- // Changes the length of the animation. This resets the current
- // state of the animation to the beginning.
- void SetDuration(int duration);
-
- // Returns true if rich animations should be rendered.
- // Looks at session type (e.g. remote desktop) and accessibility settings
- // to give guidance for heavy animations such as "start download" arrow.
- static bool ShouldRenderRichAnimation();
-
// Sets the delegate.
void set_delegate(AnimationDelegate* delegate) { delegate_ = delegate; }
@@ -114,39 +72,53 @@ class Animation {
// creating a new AnimationContainer.
void SetContainer(AnimationContainer* container);
+ bool is_animating() const { return is_animating_; }
+
base::TimeDelta timer_interval() const { return timer_interval_; }
- protected:
- // Invoked by the AnimationContainer when the animation is running to advance
- // the animation. Use |time_now| rather than Time::Now to avoid multiple
- // animations running at the same time diverging.
- virtual void Step(base::TimeTicks time_now);
+ // Returns true if rich animations should be rendered.
+ // Looks at session type (e.g. remote desktop) and accessibility settings
+ // to give guidance for heavy animations such as "start download" arrow.
+ static bool ShouldRenderRichAnimation();
- // Calculates the timer interval from the constructor list.
- base::TimeDelta CalculateInterval(int frame_rate);
+ protected:
+ // Invoked from Start to allow subclasses to prepare for the animation.
+ virtual void AnimationStarted() {}
- private:
- friend class AnimationContainer;
+ // Invoked from Stop after we're removed from the container but before the
+ // delegate has been invoked.
+ virtual void AnimationStopped() {}
- // Invoked from AnimationContainer when started.
- void set_start_time(base::TimeTicks start_time) { start_time_ = start_time; }
+ // Invoked from stop to determine if cancel should be invoked. If this returns
+ // true the delegate is notified the animation was canceled, otherwise the
+ // delegate is notified the animation stopped.
+ virtual bool ShouldSendCanceledFromStop() { return false; }
- // Whether or not we are currently animating.
- bool animating_;
+ AnimationContainer* container() { return container_.get(); }
+ base::TimeTicks start_time() const { return start_time_; }
+ AnimationDelegate* delegate() { return delegate_; }
- int frame_rate_;
- base::TimeDelta timer_interval_;
- base::TimeDelta duration_;
+ // AnimationContainer::Element overrides
+ virtual void SetStartTime(base::TimeTicks start_time);
+ virtual void Step(base::TimeTicks time_now) = 0;
+ virtual base::TimeDelta GetTimerInterval() const { return timer_interval_; }
- // Current state, on a scale from 0.0 to 1.0.
- double state_;
+ private:
+ // Interval for the animation.
+ const base::TimeDelta timer_interval_;
- base::TimeTicks start_time_;
+ // If true we're running.
+ bool is_animating_;
+ // Our delegate; may be null.
AnimationDelegate* delegate_;
+ // Container we're in. If non-null we're animating.
scoped_refptr<AnimationContainer> container_;
+ // Time we started at.
+ base::TimeTicks start_time_;
+
DISALLOW_COPY_AND_ASSIGN(Animation);
};
diff --git a/app/animation_container.cc b/app/animation_container.cc
index 221b453..2b41913 100644
--- a/app/animation_container.cc
+++ b/app/animation_container.cc
@@ -16,31 +16,31 @@ AnimationContainer::AnimationContainer()
AnimationContainer::~AnimationContainer() {
// The animations own us and stop themselves before being deleted. If
- // animations_ is not empty, something is wrong.
- DCHECK(animations_.empty());
+ // elements_ is not empty, something is wrong.
+ DCHECK(elements_.empty());
}
-void AnimationContainer::Start(Animation* animation) {
- DCHECK(animations_.count(animation) == 0); // Start should only be invoked
- // if the animation isn't running.
+void AnimationContainer::Start(Element* element) {
+ DCHECK(elements_.count(element) == 0); // Start should only be invoked if the
+ // element isn't running.
- if (animations_.empty()) {
+ if (elements_.empty()) {
last_tick_time_ = TimeTicks::Now();
- SetMinTimerInterval(animation->timer_interval());
- } else if (animation->timer_interval() < min_timer_interval_) {
- SetMinTimerInterval(animation->timer_interval());
+ SetMinTimerInterval(element->GetTimerInterval());
+ } else if (element->GetTimerInterval() < min_timer_interval_) {
+ SetMinTimerInterval(element->GetTimerInterval());
}
- animation->set_start_time(last_tick_time_);
- animations_.insert(animation);
+ element->SetStartTime(last_tick_time_);
+ elements_.insert(element);
}
-void AnimationContainer::Stop(Animation* animation) {
- DCHECK(animations_.count(animation) > 0); // The animation must be running.
+void AnimationContainer::Stop(Element* element) {
+ DCHECK(elements_.count(element) > 0); // The element must be running.
- animations_.erase(animation);
+ elements_.erase(element);
- if (animations_.empty()) {
+ if (elements_.empty()) {
timer_.Stop();
if (observer_)
observer_->AnimationContainerEmpty(this);
@@ -52,25 +52,24 @@ void AnimationContainer::Stop(Animation* animation) {
}
void AnimationContainer::Run() {
- // We notify the observer after updating all the animations. If all the
- // animations are deleted as a result of updating then our ref count would go
- // to zero and we would be deleted before we notify our observer. We add a
- // reference to ourself here to make sure we're still valid after running all
- // the animations.
+ // We notify the observer after updating all the elements. If all the elements
+ // are deleted as a result of updating then our ref count would go to zero and
+ // we would be deleted before we notify our observer. We add a reference to
+ // ourself here to make sure we're still valid after running all the elements.
scoped_refptr<AnimationContainer> this_ref(this);
TimeTicks current_time = TimeTicks::Now();
last_tick_time_ = current_time;
- // Make a copy of the animations to iterate over so that if any animations
- // are removed as part of invoking Step there aren't any problems.
- Animations animations = animations_;
+ // Make a copy of the elements to iterate over so that if any elements are
+ // removed as part of invoking Step there aren't any problems.
+ Elements elements = elements_;
- for (Animations::const_iterator i = animations.begin();
- i != animations.end(); ++i) {
- // Make sure the animation is still valid.
- if (animations_.find(*i) != animations_.end())
+ for (Elements::const_iterator i = elements.begin();
+ i != elements.end(); ++i) {
+ // Make sure the element is still valid.
+ if (elements_.find(*i) != elements_.end())
(*i)->Step(current_time);
}
@@ -79,22 +78,22 @@ void AnimationContainer::Run() {
}
void AnimationContainer::SetMinTimerInterval(base::TimeDelta delta) {
- // This doesn't take into account how far along current animation is, but that
- // shouldn't be a problem for uses of Animation/AnimationContainer.
+ // This doesn't take into account how far along the current element is, but
+ // that shouldn't be a problem for uses of Animation/AnimationContainer.
timer_.Stop();
min_timer_interval_ = delta;
timer_.Start(min_timer_interval_, this, &AnimationContainer::Run);
}
TimeDelta AnimationContainer::GetMinInterval() {
- DCHECK(!animations_.empty());
+ DCHECK(!elements_.empty());
TimeDelta min;
- Animations::const_iterator i = animations_.begin();
- min = (*i)->timer_interval();
- for (++i; i != animations_.end(); ++i) {
- if ((*i)->timer_interval() < min)
- min = (*i)->timer_interval();
+ Elements::const_iterator i = elements_.begin();
+ min = (*i)->GetTimerInterval();
+ for (++i; i != elements_.end(); ++i) {
+ if ((*i)->GetTimerInterval() < min)
+ min = (*i)->GetTimerInterval();
}
return min;
}
diff --git a/app/animation_container.h b/app/animation_container.h
index fcb3fad..98abf28 100644
--- a/app/animation_container.h
+++ b/app/animation_container.h
@@ -11,8 +11,6 @@
#include "base/time.h"
#include "base/timer.h"
-class Animation;
-
// AnimationContainer is used by Animation to manage the underlying timer.
// Internally each Animation creates a single AnimationContainer. You can
// group a set of Animations into the same AnimationContainer by way of
@@ -36,31 +34,53 @@ class AnimationContainer : public base::RefCounted<AnimationContainer> {
virtual void AnimationContainerEmpty(AnimationContainer* container) = 0;
};
+ // Interface for the elements the AnimationContainer contains. This is
+ // implemented by Animation.
+ class Element {
+ public:
+ // Sets the start of the animation. This is invoked from
+ // AnimationContainer::Start.
+ virtual void SetStartTime(base::TimeTicks start_time) = 0;
+
+ // Invoked when the animation is to progress.
+ virtual void Step(base::TimeTicks time_now) = 0;
+
+ // Returns the time interval of the animation. If an Element needs to change
+ // this it should first invoke Stop, then Start.
+ virtual base::TimeDelta GetTimerInterval() const = 0;
+
+ protected:
+ virtual ~Element() {}
+ };
+
AnimationContainer();
+ // Invoked by Animation when it needs to start. Starts the timer if necessary.
+ // NOTE: This is invoked by Animation for you, you shouldn't invoke this
+ // directly.
+ void Start(Element* animation);
+
+ // Invoked by Animation when it needs to stop. If there are no more animations
+ // running the timer stops.
+ // NOTE: This is invoked by Animation for you, you shouldn't invoke this
+ // directly.
+ void Stop(Element* animation);
+
void set_observer(Observer* observer) { observer_ = observer; }
// The time the last animation ran at.
base::TimeTicks last_tick_time() const { return last_tick_time_; }
// Are there any timers running?
- bool is_running() const { return !animations_.empty(); }
+ bool is_running() const { return !elements_.empty(); }
private:
- friend class Animation;
friend class base::RefCounted<AnimationContainer>;
- typedef std::set<Animation*> Animations;
+ typedef std::set<Element*> Elements;
~AnimationContainer();
- // Invoked by Animation when it needs to start. Starts the timer if necessary.
- void Start(Animation* animation);
-
- // Invoked by Animation when it needs to stop. If there are no more animations
- // running the timer stops.
- void Stop(Animation* animation);
-
// Timer callback method.
void Run();
@@ -76,8 +96,8 @@ class AnimationContainer : public base::RefCounted<AnimationContainer> {
// . The time the last animation ran at (::Run was invoked).
base::TimeTicks last_tick_time_;
- // Set of animations being managed.
- Animations animations_;
+ // Set of elements (animations) being managed.
+ Elements elements_;
// Minimum interval the timers run at.
base::TimeDelta min_timer_interval_;
diff --git a/app/animation_container_unittest.cc b/app/animation_container_unittest.cc
index 4046ae3..e4253c6 100644
--- a/app/animation_container_unittest.cc
+++ b/app/animation_container_unittest.cc
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "app/animation.h"
#include "app/animation_container.h"
+#include "app/linear_animation.h"
#include "app/test_animation_delegate.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -23,10 +23,10 @@ class MockObserver : public AnimationContainer::Observer {
DISALLOW_COPY_AND_ASSIGN(MockObserver);
};
-class TestAnimation : public Animation {
+class TestAnimation : public LinearAnimation {
public:
TestAnimation(AnimationDelegate* delegate)
- : Animation(20, 20, delegate) {
+ : LinearAnimation(20, 20, delegate) {
}
virtual void AnimateToState(double state) {
diff --git a/app/animation_unittest.cc b/app/animation_unittest.cc
index 30f9b52..df0b262b 100644
--- a/app/animation_unittest.cc
+++ b/app/animation_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "app/animation.h"
+#include "app/linear_animation.h"
#include "app/test_animation_delegate.h"
#if defined(OS_WIN)
#include "base/win_util.h"
@@ -14,13 +14,15 @@ class AnimationTest: public testing::Test {
MessageLoopForUI message_loop_;
};
+namespace {
+
///////////////////////////////////////////////////////////////////////////////
// RunAnimation
-class RunAnimation : public Animation {
+class RunAnimation : public LinearAnimation {
public:
RunAnimation(int frame_rate, AnimationDelegate* delegate)
- : Animation(frame_rate, delegate) {
+ : LinearAnimation(frame_rate, delegate) {
}
virtual void AnimateToState(double state) {
@@ -32,10 +34,10 @@ class RunAnimation : public Animation {
///////////////////////////////////////////////////////////////////////////////
// CancelAnimation
-class CancelAnimation : public Animation {
+class CancelAnimation : public LinearAnimation {
public:
CancelAnimation(int duration, int frame_rate, AnimationDelegate* delegate)
- : Animation(duration, frame_rate, delegate) {
+ : LinearAnimation(duration, frame_rate, delegate) {
}
virtual void AnimateToState(double state) {
@@ -45,6 +47,35 @@ class CancelAnimation : public Animation {
};
///////////////////////////////////////////////////////////////////////////////
+// EndAnimation
+
+class EndAnimation : public LinearAnimation {
+ public:
+ EndAnimation(int duration, int frame_rate, AnimationDelegate* delegate)
+ : LinearAnimation(duration, frame_rate, delegate) {
+ }
+
+ virtual void AnimateToState(double state) {
+ if (state >= 0.5)
+ End();
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// DeletingAnimationDelegate
+
+// AnimationDelegate implementation that deletes the animation in ended.
+class DeletingAnimationDelegate : public AnimationDelegate {
+ public:
+ virtual void AnimationEnded(const Animation* animation) {
+ delete animation;
+ MessageLoop::current()->Quit();
+ }
+};
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
// LinearCase
TEST_F(AnimationTest, RunCase) {
@@ -68,6 +99,27 @@ TEST_F(AnimationTest, CancelCase) {
EXPECT_TRUE(ad.canceled());
}
+// Lets an animation run, invoking End part way through and make sure we get the
+// right delegate methods invoked.
+TEST_F(AnimationTest, EndCase) {
+ TestAnimationDelegate ad;
+ EndAnimation a2(2000, 150, &ad);
+ a2.Start();
+ MessageLoop::current()->Run();
+
+ EXPECT_TRUE(ad.finished());
+ EXPECT_FALSE(ad.canceled());
+}
+
+// Runs an animation with a delegate that deletes the animation in end.
+TEST_F(AnimationTest, DeleteFromEnd) {
+ DeletingAnimationDelegate delegate;
+ RunAnimation* animation = new RunAnimation(150, &delegate);
+ animation->Start();
+ MessageLoop::current()->Run();
+ // delegate should have deleted animation.
+}
+
TEST_F(AnimationTest, ShouldRenderRichAnimation) {
#if defined(OS_WIN)
if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) {
diff --git a/app/app_base.gypi b/app/app_base.gypi
index 0c68721..b28bc2b 100644
--- a/app/app_base.gypi
+++ b/app/app_base.gypi
@@ -146,6 +146,8 @@
'l10n_util_posix.cc',
'l10n_util_win.cc',
'l10n_util_win.h',
+ 'linear_animation.cc',
+ 'linear_animation.h',
'menus/accelerator.h',
'menus/accelerator_gtk.h',
'menus/menu_model.cc',
@@ -194,6 +196,8 @@
'theme_provider.h',
'throb_animation.cc',
'throb_animation.h',
+ 'tween.cc',
+ 'tween.h',
'x11_util.cc',
'x11_util.h',
'x11_util_internal.h',
diff --git a/app/linear_animation.cc b/app/linear_animation.cc
new file mode 100644
index 0000000..35974af
--- /dev/null
+++ b/app/linear_animation.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "app/linear_animation.h"
+
+#include <math.h>
+
+#include "app/animation_container.h"
+
+using base::Time;
+using base::TimeDelta;
+
+static TimeDelta CalculateInterval(int frame_rate) {
+ int timer_interval = 1000000 / frame_rate;
+ if (timer_interval < 10000)
+ timer_interval = 10000;
+ return TimeDelta::FromMicroseconds(timer_interval);
+}
+
+LinearAnimation::LinearAnimation(int frame_rate,
+ AnimationDelegate* delegate)
+ : Animation(CalculateInterval(frame_rate)),
+ state_(0.0),
+ in_end_(false) {
+ set_delegate(delegate);
+}
+
+LinearAnimation::LinearAnimation(int duration,
+ int frame_rate,
+ AnimationDelegate* delegate)
+ : Animation(CalculateInterval(frame_rate)),
+ duration_(TimeDelta::FromMilliseconds(duration)),
+ state_(0.0),
+ in_end_(false) {
+ set_delegate(delegate);
+ SetDuration(duration);
+}
+
+double LinearAnimation::GetCurrentValue() const {
+ // Default is linear relationship, subclass to adapt.
+ return state_;
+}
+
+void LinearAnimation::End() {
+ if (!is_animating())
+ return;
+
+ // NOTE: We don't use AutoReset here as Stop may end up deleting us (by way
+ // of the delegate).
+ in_end_ = true;
+ Stop();
+}
+
+void LinearAnimation::SetDuration(int duration) {
+ duration_ = TimeDelta::FromMilliseconds(duration);
+ if (duration_ < timer_interval())
+ duration_ = timer_interval();
+ if (is_animating())
+ SetStartTime(container()->last_tick_time());
+}
+
+void LinearAnimation::Step(base::TimeTicks time_now) {
+ TimeDelta elapsed_time = time_now - start_time();
+ state_ = static_cast<double>(elapsed_time.InMicroseconds()) /
+ static_cast<double>(duration_.InMicroseconds());
+ if (state_ >= 1.0)
+ state_ = 1.0;
+
+ AnimateToState(state_);
+
+ if (delegate())
+ delegate()->AnimationProgressed(this);
+
+ if (state_ == 1.0)
+ Stop();
+}
+
+void LinearAnimation::AnimationStopped() {
+ if (!in_end_)
+ return;
+
+ in_end_ = false;
+ // Set state_ to ensure we send ended to delegate and not canceled.
+ state_ = 1;
+ AnimateToState(1.0);
+}
+
+bool LinearAnimation::ShouldSendCanceledFromStop() {
+ return state_ != 1;
+}
diff --git a/app/linear_animation.h b/app/linear_animation.h
new file mode 100644
index 0000000..c4f5707
--- /dev/null
+++ b/app/linear_animation.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Inspired by NSAnimation
+
+#ifndef APP_LINEAR_ANIMATION_H_
+#define APP_LINEAR_ANIMATION_H_
+
+#include "app/animation.h"
+#include "base/time.h"
+
+class AnimationDelegate;
+
+// Linear time bounded animation. As the animation progresses AnimateToState is
+// invoked.
+class LinearAnimation : public Animation {
+ public:
+ // Initializes everything except the duration.
+ //
+ // Caller must make sure to call SetDuration() if they use this
+ // constructor; it is preferable to use the full one, but sometimes
+ // duration can change between calls to Start() and we need to
+ // expose this interface.
+ LinearAnimation(int frame_rate, AnimationDelegate* delegate);
+
+ // Initializes all fields.
+ LinearAnimation(int duration, int frame_rate, AnimationDelegate* delegate);
+
+ // Gets the value for the current state, according to the animation curve in
+ // use. This class provides only for a linear relationship, however subclasses
+ // can override this to provide others.
+ virtual double GetCurrentValue() const;
+
+ // Skip to the end of the current animation.
+ void End();
+
+ // Changes the length of the animation. This resets the current
+ // state of the animation to the beginning.
+ void SetDuration(int duration);
+
+ protected:
+ // Called when the animation progresses. Subclasses override this to
+ // efficiently update their state.
+ virtual void AnimateToState(double state) = 0;
+
+ // Invoked by the AnimationContainer when the animation is running to advance
+ // the animation. Use |time_now| rather than Time::Now to avoid multiple
+ // animations running at the same time diverging.
+ virtual void Step(base::TimeTicks time_now);
+
+ // Overriden to advance to the end (if End was invoked).
+ virtual void AnimationStopped();
+
+ // Overriden to return true if state is not 1.
+ virtual bool ShouldSendCanceledFromStop();
+
+ private:
+ base::TimeDelta duration_;
+
+ // Current state, on a scale from 0.0 to 1.0.
+ double state_;
+
+ // If true, we're in end. This is used to determine if the animation should
+ // be advanced to the end from AnimationStopped.
+ bool in_end_;
+
+ DISALLOW_COPY_AND_ASSIGN(LinearAnimation);
+};
+
+#endif // APP_LINEAR_ANIMATION_H_
diff --git a/app/slide_animation.cc b/app/slide_animation.cc
index ca468d2..c79e3ec 100644
--- a/app/slide_animation.cc
+++ b/app/slide_animation.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -13,9 +13,9 @@ static const int kDefaultFramerateHz = 50;
static const int kDefaultDurationMs = 120;
SlideAnimation::SlideAnimation(AnimationDelegate* target)
- : Animation(kDefaultFramerateHz, target),
+ : LinearAnimation(kDefaultFramerateHz, target),
target_(target),
- tween_type_(EASE_OUT),
+ tween_type_(Tween::EASE_OUT),
showing_(false),
value_start_(0),
value_end_(0),
@@ -84,36 +84,13 @@ void SlideAnimation::AnimateToState(double state) {
if (state > 1.0)
state = 1.0;
- // Make our animation ease-out.
- switch (tween_type_) {
- case EASE_IN:
- state = pow(state, 2);
- break;
- case EASE_IN_OUT:
- if (state < 0.5)
- state = pow(state * 2, 2) / 2.0;
- else
- state = 1.0 - (pow((state - 1.0) * 2, 2) / 2.0);
- break;
- case FAST_IN_OUT:
- state = (pow(state - 0.5, 3) + 0.125) / 0.25;
- break;
- case NONE:
- // state remains linear.
- break;
- case EASE_OUT_SNAP:
- state = 0.95 * (1.0 - pow(1.0 - state, 2));
- break;
- case EASE_OUT:
- default:
- state = 1.0 - pow(1.0 - state, 2);
- break;
- }
+ state = Tween::CalculateValue(tween_type_, state);
value_current_ = value_start_ + (value_end_ - value_start_) * state;
// Implement snapping.
- if (tween_type_ == EASE_OUT_SNAP && fabs(value_current_ - value_end_) <= 0.06)
+ if (tween_type_ == Tween::EASE_OUT_SNAP &&
+ fabs(value_current_ - value_end_) <= 0.06)
value_current_ = value_end_;
// Correct for any overshoot (while state may be capped at 1.0, let's not
diff --git a/app/slide_animation.h b/app/slide_animation.h
index 3cf32d9..0b42792 100644
--- a/app/slide_animation.h
+++ b/app/slide_animation.h
@@ -1,11 +1,12 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef APP_SLIDE_ANIMATION_H_
#define APP_SLIDE_ANIMATION_H_
-#include "app/animation.h"
+#include "app/linear_animation.h"
+#include "app/tween.h"
// Slide Animation
//
@@ -34,24 +35,15 @@
// }
// }
// void Layout() {
-// if (animation_->IsAnimating()) {
+// if (animation_->is_animating()) {
// hover_image_.SetOpacity(animation_->GetCurrentValue());
// }
// }
// private:
// scoped_ptr<SlideAnimation> animation_;
// }
-class SlideAnimation : public Animation {
+class SlideAnimation : public LinearAnimation {
public:
- enum TweenType {
- NONE, // Linear.
- EASE_OUT, // Fast in, slow out (default).
- EASE_IN, // Slow in, fast out.
- EASE_IN_OUT, // Slow in and out, fast in the middle.
- FAST_IN_OUT, // Fast in and out, slow in the middle.
- EASE_OUT_SNAP, // Fast in, slow out, snap to final value.
- };
-
explicit SlideAnimation(AnimationDelegate* target);
virtual ~SlideAnimation();
@@ -70,7 +62,7 @@ class SlideAnimation : public Animation {
// the slide is considered.
virtual void SetSlideDuration(int duration) { slide_duration_ = duration; }
int GetSlideDuration() const { return slide_duration_; }
- void SetTweenType(TweenType tween_type) { tween_type_ = tween_type; }
+ void SetTweenType(Tween::Type tween_type) { tween_type_ = tween_type; }
double GetCurrentValue() const { return value_current_; }
bool IsShowing() const { return showing_; }
@@ -82,7 +74,7 @@ class SlideAnimation : public Animation {
AnimationDelegate* target_;
- TweenType tween_type_;
+ Tween::Type tween_type_;
// Used to determine which way the animation is going.
bool showing_;
diff --git a/app/test_animation_delegate.h b/app/test_animation_delegate.h
index 76f0f3e..0d9554b 100644
--- a/app/test_animation_delegate.h
+++ b/app/test_animation_delegate.h
@@ -15,9 +15,6 @@ class TestAnimationDelegate : public AnimationDelegate {
TestAnimationDelegate() : canceled_(false), finished_(false) {
}
- virtual void AnimationStarted(const Animation* animation) {
- }
-
virtual void AnimationEnded(const Animation* animation) {
finished_ = true;
MessageLoop::current()->Quit();
diff --git a/app/throb_animation.cc b/app/throb_animation.cc
index e8e5730..922d77d 100644
--- a/app/throb_animation.cc
+++ b/app/throb_animation.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -18,7 +18,7 @@ void ThrobAnimation::StartThrobbing(int cycles_til_stop) {
cycles_remaining_ = cycles_til_stop;
throbbing_ = true;
SlideAnimation::SetSlideDuration(throb_duration_);
- if (IsAnimating())
+ if (is_animating())
return; // We're already running, we'll cycle when current loop finishes.
if (IsShowing())
@@ -44,9 +44,9 @@ void ThrobAnimation::Hide() {
}
void ThrobAnimation::Step(base::TimeTicks time_now) {
- Animation::Step(time_now);
+ LinearAnimation::Step(time_now);
- if (!IsAnimating() && throbbing_) {
+ if (!is_animating() && throbbing_) {
// Were throbbing a finished a cycle. Start the next cycle unless we're at
// the end of the cycles, in which case we stop.
cycles_remaining_--;
diff --git a/app/tween.cc b/app/tween.cc
new file mode 100644
index 0000000..57cd83e
--- /dev/null
+++ b/app/tween.cc
@@ -0,0 +1,81 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "app/tween.h"
+
+#include <math.h>
+
+#if defined(OS_WIN)
+#include <float.h>
+#endif
+
+#include "gfx/rect.h"
+
+// static
+double Tween::CalculateValue(Tween::Type type, double state) {
+ DCHECK_GE(state, 0);
+ DCHECK_LE(state, 1);
+
+ switch (type) {
+ case EASE_IN:
+ return pow(state, 2);
+
+ case EASE_IN_OUT:
+ if (state < 0.5)
+ return pow(state * 2, 2) / 2.0;
+ return 1.0 - (pow((state - 1.0) * 2, 2) / 2.0);
+
+ case FAST_IN_OUT:
+ return (pow(state - 0.5, 3) + 0.125) / 0.25;
+
+ case LINEAR:
+ return state;
+
+ case EASE_OUT_SNAP:
+ state = 0.95 * (1.0 - pow(1.0 - state, 2));
+ break;
+
+ case EASE_OUT:
+ return 1.0 - pow(1.0 - state, 2);
+
+ case ZERO:
+ return 0;
+ }
+
+ NOTREACHED();
+ return state;
+}
+
+// static
+double Tween::ValueBetween(double value, double start, double target) {
+ return start + (target - start) * value;
+}
+
+// static
+int Tween::ValueBetween(double value, int start, int target) {
+ if (start == target)
+ return start;
+ double delta = static_cast<double>(target - start);
+ if (delta < 0)
+ delta--;
+ else
+ delta++;
+#if defined(OS_WIN)
+ return start + static_cast<int>(value * _nextafter(delta, 0));
+#else
+ return start + static_cast<int>(value * nextafter(delta, 0));
+#endif
+}
+
+// static
+gfx::Rect Tween::ValueBetween(double value,
+ const gfx::Rect& start_bounds,
+ const gfx::Rect& target_bounds) {
+ return gfx::Rect(ValueBetween(value, start_bounds.x(), target_bounds.x()),
+ ValueBetween(value, start_bounds.y(), target_bounds.y()),
+ ValueBetween(value, start_bounds.width(),
+ target_bounds.width()),
+ ValueBetween(value, start_bounds.height(),
+ target_bounds.height()));
+}
diff --git a/app/tween.h b/app/tween.h
new file mode 100644
index 0000000..41785ef6
--- /dev/null
+++ b/app/tween.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef APP_TWEEN_H_
+#define APP_TWEEN_H_
+
+#include "base/logging.h"
+
+namespace gfx {
+class Rect;
+}
+
+class Tween {
+ public:
+ enum Type {
+ LINEAR, // Linear.
+ EASE_OUT, // Fast in, slow out (default).
+ EASE_IN, // Slow in, fast out.
+ EASE_IN_OUT, // Slow in and out, fast in the middle.
+ FAST_IN_OUT, // Fast in and out, slow in the middle.
+ EASE_OUT_SNAP, // Fast in, slow out, snap to final value.
+ ZERO, // Returns a value of 0 always.
+ };
+
+ // Returns the value based on the tween type. |state| is from 0-1.
+ static double CalculateValue(Type type, double state);
+
+ // Conveniences for getting a value between a start and end point.
+ static double ValueBetween(double value, double start, double target);
+ static int ValueBetween(double value, int start, int target);
+ static gfx::Rect ValueBetween(double value,
+ const gfx::Rect& start_bounds,
+ const gfx::Rect& target_bounds);
+
+ private:
+ Tween();
+ ~Tween();
+
+ DISALLOW_COPY_AND_ASSIGN(Tween);
+};
+
+#endif // APP_TWEEN_H_