diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-05 03:43:55 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-05 03:43:55 +0000 |
commit | 4ce7e15da651c6221720e33dc8c500830d4b6b8a (patch) | |
tree | 376ef1d7e7951dae3376e65f4edee9e7367adc89 /app | |
parent | 5ee3ca64cdf2d00f82a1bc36f36e8ab5e520b4de (diff) | |
download | chromium_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.cc | 172 | ||||
-rw-r--r-- | app/animation.h | 128 | ||||
-rw-r--r-- | app/animation_container.cc | 69 | ||||
-rw-r--r-- | app/animation_container.h | 48 | ||||
-rw-r--r-- | app/animation_container_unittest.cc | 6 | ||||
-rw-r--r-- | app/animation_unittest.cc | 62 | ||||
-rw-r--r-- | app/app_base.gypi | 4 | ||||
-rw-r--r-- | app/linear_animation.cc | 91 | ||||
-rw-r--r-- | app/linear_animation.h | 70 | ||||
-rw-r--r-- | app/slide_animation.cc | 35 | ||||
-rw-r--r-- | app/slide_animation.h | 22 | ||||
-rw-r--r-- | app/test_animation_delegate.h | 3 | ||||
-rw-r--r-- | app/throb_animation.cc | 8 | ||||
-rw-r--r-- | app/tween.cc | 81 | ||||
-rw-r--r-- | app/tween.h | 43 |
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_ |