summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-01 23:20:52 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-01 23:20:52 +0000
commit059e7a559572484fb677480ef6b95322cde3b34f (patch)
treed21b6b7393c4d27d1fb2ee172c2a0ac7330138ae
parente3f4cc62db9c967b911b00399674e392c71d0a7d (diff)
downloadchromium_src-059e7a559572484fb677480ef6b95322cde3b34f.zip
chromium_src-059e7a559572484fb677480ef6b95322cde3b34f.tar.gz
chromium_src-059e7a559572484fb677480ef6b95322cde3b34f.tar.bz2
Adds AnimationContainer, which can be used to group a set of
animations to have the same timer. By default each animation has one animationcontainer, but I'm going to change the tab renderer to share the animationcontainer so that the pulse effects happen in unison. Also updated the BoundsAnimator so that I can use it by the TabStrip. BUG=none TEST=none Review URL: http://codereview.chromium.org/1575011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43407 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--app/animation.cc97
-rw-r--r--app/animation.h34
-rw-r--r--app/animation_container.cc93
-rw-r--r--app/animation_container.h92
-rw-r--r--app/animation_container_unittest.cc120
-rw-r--r--app/animation_unittest.cc39
-rw-r--r--app/app.gyp2
-rw-r--r--app/app_base.gypi2
-rw-r--r--app/slide_animation.h6
-rw-r--r--app/test_animation_delegate.h47
-rw-r--r--app/throb_animation.cc5
-rw-r--r--app/throb_animation.h7
-rw-r--r--chrome/browser/gtk/tabs/tab_renderer_gtk.cc2
-rw-r--r--chrome/browser/views/fullscreen_exit_bubble.h1
-rw-r--r--chrome/browser/views/infobars/infobars.h1
-rw-r--r--chrome/browser/views/tabs/side_tab.cc1
-rw-r--r--chrome/browser/views/tabs/tab_renderer.cc2
-rw-r--r--views/animation/bounds_animator.cc235
-rw-r--r--views/animation/bounds_animator.h140
19 files changed, 742 insertions, 184 deletions
diff --git a/app/animation.cc b/app/animation.cc
index f2d95f4..5e9d10c 100644
--- a/app/animation.cc
+++ b/app/animation.cc
@@ -1,10 +1,10 @@
-// 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.
#include "app/animation.h"
-#include "base/message_loop.h"
+#include "app/animation_container.h"
#include "gfx/rect.h"
#if defined(OS_WIN)
@@ -37,10 +37,8 @@ Animation::Animation(int duration,
}
Animation::~Animation() {
-}
-
-void Animation::Reset() {
- start_time_ = Time::Now();
+ if (animating_)
+ container_->Stop(this);
}
double Animation::GetCurrentValue() const {
@@ -69,10 +67,13 @@ gfx::Rect Animation::CurrentValueBetween(const gfx::Rect& start_bounds,
void Animation::Start() {
if (!animating_) {
- start_time_ = Time::Now();
- timer_.Start(timer_interval_, this, &Animation::Run);
+ if (!container_.get())
+ container_ = new AnimationContainer();
animating_ = true;
+
+ container_->Start(this);
+
if (delegate_)
delegate_->AnimationStarted(this);
}
@@ -80,9 +81,11 @@ void Animation::Start() {
void Animation::Stop() {
if (animating_) {
- timer_.Stop();
-
animating_ = false;
+
+ // Notify the container first as the delegate may delete us.
+ container_->Stop(this);
+
if (delegate_) {
if (state_ >= 1.0)
delegate_->AnimationEnded(this);
@@ -94,9 +97,11 @@ void Animation::Stop() {
void Animation::End() {
if (animating_) {
- timer_.Stop();
-
animating_ = false;
+
+ // Notify the container first as the delegate may delete us.
+ container_->Stop(this);
+
AnimateToState(1.0);
if (delegate_)
delegate_->AnimationEnded(this);
@@ -111,34 +116,8 @@ void Animation::SetDuration(int duration) {
duration_ = TimeDelta::FromMilliseconds(duration);
if (duration_ < timer_interval_)
duration_ = timer_interval_;
- start_time_ = Time::Now();
-}
-
-void Animation::Step() {
- 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 Animation::Run() {
- Step();
-}
-
-TimeDelta Animation::CalculateInterval(int frame_rate) {
- int timer_interval = 1000000 / frame_rate;
- if (timer_interval < 10000)
- timer_interval = 10000;
- return TimeDelta::FromMicroseconds(timer_interval);
+ if (animating_)
+ start_time_ = container_->last_tick_time();
}
// static
@@ -160,3 +139,41 @@ 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);
+}
diff --git a/app/animation.h b/app/animation.h
index bfa7fd0..c483e1d 100644
--- a/app/animation.h
+++ b/app/animation.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 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.
// Inspired by NSAnimation
@@ -6,10 +6,11 @@
#ifndef APP_ANIMATION_H_
#define APP_ANIMATION_H_
+#include "base/ref_counted.h"
#include "base/time.h"
-#include "base/timer.h"
class Animation;
+class AnimationContainer;
namespace gfx {
class Rect;
@@ -69,9 +70,6 @@ class Animation {
Animation(int duration, int frame_rate, AnimationDelegate* delegate);
virtual ~Animation();
- // Reset state so that the animation can be started again.
- virtual void Reset();
-
// Called when the animation progresses. Subclasses override this to
// efficiently update their state.
virtual void AnimateToState(double state) = 0;
@@ -112,13 +110,27 @@ class Animation {
// Sets the delegate.
void set_delegate(AnimationDelegate* delegate) { delegate_ = delegate; }
+ // Sets the container used to manage the timer. A value of NULL results in
+ // creating a new AnimationContainer.
+ void SetContainer(AnimationContainer* container);
+
+ base::TimeDelta timer_interval() const { return timer_interval_; }
+
protected:
- // Overriddable, called by Run.
- virtual void Step();
+ // 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);
// Calculates the timer interval from the constructor list.
base::TimeDelta CalculateInterval(int frame_rate);
+ private:
+ friend class AnimationContainer;
+
+ // Invoked from AnimationContainer when started.
+ void set_start_time(base::TimeTicks start_time) { start_time_ = start_time; }
+
// Whether or not we are currently animating.
bool animating_;
@@ -129,15 +141,11 @@ class Animation {
// Current state, on a scale from 0.0 to 1.0.
double state_;
- base::Time start_time_;
+ base::TimeTicks start_time_;
AnimationDelegate* delegate_;
- base::RepeatingTimer<Animation> timer_;
-
- private:
- // Called when the animation's timer expires, calls Step.
- void Run();
+ scoped_refptr<AnimationContainer> container_;
DISALLOW_COPY_AND_ASSIGN(Animation);
};
diff --git a/app/animation_container.cc b/app/animation_container.cc
new file mode 100644
index 0000000..b0cc86a
--- /dev/null
+++ b/app/animation_container.cc
@@ -0,0 +1,93 @@
+// 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/animation_container.h"
+
+#include "app/animation.h"
+
+using base::TimeDelta;
+using base::TimeTicks;
+
+AnimationContainer::AnimationContainer()
+ : last_tick_time_(TimeTicks::Now()),
+ observer_(NULL) {
+}
+
+AnimationContainer::~AnimationContainer() {
+ // The animations own us and stop themselves before being deleted. If
+ // animations_ is not empty, something is wrong.
+ DCHECK(animations_.empty());
+}
+
+void AnimationContainer::Start(Animation* animation) {
+ DCHECK(animations_.count(animation) == 0); // Start should only be invoked
+ // if the animation isn't running.
+
+ if (animations_.empty()) {
+ last_tick_time_ = TimeTicks::Now();
+ SetMinTimerInterval(animation->timer_interval());
+ } else if (animation->timer_interval() < min_timer_interval_) {
+ SetMinTimerInterval(animation->timer_interval());
+ }
+
+ animation->set_start_time(last_tick_time_);
+ animations_.insert(animation);
+}
+
+void AnimationContainer::Stop(Animation* animation) {
+ DCHECK(animations_.count(animation) > 0); // The animation must be running.
+
+ animations_.erase(animation);
+
+ if (animations_.empty()) {
+ timer_.Stop();
+ if (observer_)
+ observer_->AnimationContainerEmpty(this);
+ } else {
+ TimeDelta min_timer_interval = GetMinInterval();
+ if (min_timer_interval > min_timer_interval_)
+ SetMinTimerInterval(min_timer_interval);
+ }
+}
+
+void AnimationContainer::Run() {
+ 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_;
+
+ for (Animations::const_iterator i = animations.begin();
+ i != animations.end(); ++i) {
+ // Make sure the animation is still valid.
+ if (animations_.find(*i) != animations_.end())
+ (*i)->Step(current_time);
+ }
+
+ if (observer_)
+ observer_->AnimationContainerProgressed(this);
+}
+
+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.
+ timer_.Stop();
+ min_timer_interval_ = delta;
+ timer_.Start(min_timer_interval_, this, &AnimationContainer::Run);
+}
+
+TimeDelta AnimationContainer::GetMinInterval() {
+ DCHECK(!animations_.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();
+ }
+ return min;
+}
diff --git a/app/animation_container.h b/app/animation_container.h
new file mode 100644
index 0000000..fcb3fad
--- /dev/null
+++ b/app/animation_container.h
@@ -0,0 +1,92 @@
+// 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_ANIMATION_CONTAINER_H_
+#define APP_ANIMATION_CONTAINER_H_
+
+#include <set>
+
+#include "base/ref_counted.h"
+#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
+// Animation::SetContainer. Grouping a set of Animations into the same
+// AnimationContainer ensures they all update and start at the same time.
+//
+// AnimationContainer is ref counted. Each Animation contained within the
+// AnimationContainer own it.
+class AnimationContainer : public base::RefCounted<AnimationContainer> {
+ public:
+ // The observer is notified after every update of the animations managed by
+ // the container.
+ class Observer {
+ public:
+ // Invoked on every tick of the timer managed by the container and after
+ // all the animations have updated.
+ virtual void AnimationContainerProgressed(
+ AnimationContainer* container) = 0;
+
+ // Invoked when no more animations are being managed by this container.
+ virtual void AnimationContainerEmpty(AnimationContainer* container) = 0;
+ };
+
+ AnimationContainer();
+
+ 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(); }
+
+ private:
+ friend class Animation;
+ friend class base::RefCounted<AnimationContainer>;
+
+ typedef std::set<Animation*> Animations;
+
+ ~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();
+
+ // Sets min_timer_interval_ and restarts the timer.
+ void SetMinTimerInterval(base::TimeDelta delta);
+
+ // Returns the min timer interval of all the timers.
+ base::TimeDelta GetMinInterval();
+
+ // Represents one of two possible values:
+ // . If only a single animation has been started and the timer hasn't yet
+ // fired this is the time the animation was added.
+ // . The time the last animation ran at (::Run was invoked).
+ base::TimeTicks last_tick_time_;
+
+ // Set of animations being managed.
+ Animations animations_;
+
+ // Minimum interval the timers run at.
+ base::TimeDelta min_timer_interval_;
+
+ base::RepeatingTimer<AnimationContainer> timer_;
+
+ Observer* observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(AnimationContainer);
+};
+
+#endif // APP_ANIMATION_CONTAINER_H_
diff --git a/app/animation_container_unittest.cc b/app/animation_container_unittest.cc
new file mode 100644
index 0000000..4046ae3
--- /dev/null
+++ b/app/animation_container_unittest.cc
@@ -0,0 +1,120 @@
+// 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/animation.h"
+#include "app/animation_container.h"
+#include "app/test_animation_delegate.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::AtLeast;
+
+namespace {
+
+class MockObserver : public AnimationContainer::Observer {
+ public:
+ MockObserver() {}
+
+ MOCK_METHOD1(AnimationContainerProgressed, void(AnimationContainer*));
+ MOCK_METHOD1(AnimationContainerEmpty, void(AnimationContainer*));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockObserver);
+};
+
+class TestAnimation : public Animation {
+ public:
+ TestAnimation(AnimationDelegate* delegate)
+ : Animation(20, 20, delegate) {
+ }
+
+ virtual void AnimateToState(double state) {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestAnimation);
+};
+
+} // namespace
+
+class AnimationContainerTest: public testing::Test {
+ private:
+ MessageLoopForUI message_loop_;
+};
+
+// Makes sure the animation ups the ref count of the container and releases it
+// appropriately.
+TEST_F(AnimationContainerTest, Ownership) {
+ TestAnimationDelegate delegate;
+ scoped_refptr<AnimationContainer> container(new AnimationContainer());
+ scoped_ptr<Animation> animation(new TestAnimation(&delegate));
+ animation->SetContainer(container.get());
+ // Setting the container should up the ref count.
+ EXPECT_FALSE(container->HasOneRef());
+
+ animation.reset();
+
+ // Releasing the animation should decrement the ref count.
+ EXPECT_TRUE(container->HasOneRef());
+}
+
+// Makes sure multiple animations are managed correctly.
+TEST_F(AnimationContainerTest, Multi) {
+ TestAnimationDelegate delegate1;
+ TestAnimationDelegate delegate2;
+
+ scoped_refptr<AnimationContainer> container(new AnimationContainer());
+ TestAnimation animation1(&delegate1);
+ TestAnimation animation2(&delegate2);
+ animation1.SetContainer(container.get());
+ animation2.SetContainer(container.get());
+
+ // Start both animations.
+ animation1.Start();
+ EXPECT_TRUE(container->is_running());
+ animation2.Start();
+ EXPECT_TRUE(container->is_running());
+
+ // Run the message loop the delegate quits the message loop when notified.
+ MessageLoop::current()->Run();
+
+ // Both timers should have finished.
+ EXPECT_TRUE(delegate1.finished());
+ EXPECT_TRUE(delegate2.finished());
+
+ // And the container should no longer be runnings.
+ EXPECT_FALSE(container->is_running());
+}
+
+// Makes sure observer is notified appropriately.
+TEST_F(AnimationContainerTest, Observer) {
+ MockObserver observer;
+ TestAnimationDelegate delegate1;
+
+ scoped_refptr<AnimationContainer> container(new AnimationContainer());
+ container->set_observer(&observer);
+ TestAnimation animation1(&delegate1);
+ animation1.SetContainer(container.get());
+
+ // We expect to get these two calls: the animation progressed, and then when
+ // the animation completed the container went empty.
+ EXPECT_CALL(observer, AnimationContainerProgressed(container.get())).Times(
+ AtLeast(1));
+ EXPECT_CALL(observer, AnimationContainerEmpty(container.get())).Times(1);
+
+ // Start the animation.
+ animation1.Start();
+ EXPECT_TRUE(container->is_running());
+
+ // Run the message loop the delegate quits the message loop when notified.
+ MessageLoop::current()->Run();
+
+ // The timer should have finished.
+ EXPECT_TRUE(delegate1.finished());
+
+ // And the container should no longer be runnings.
+ EXPECT_FALSE(container->is_running());
+
+ container->set_observer(NULL);
+}
diff --git a/app/animation_unittest.cc b/app/animation_unittest.cc
index bc1ebe5..30f9b52 100644
--- a/app/animation_unittest.cc
+++ b/app/animation_unittest.cc
@@ -1,9 +1,9 @@
-// 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.
#include "app/animation.h"
-#include "base/message_loop.h"
+#include "app/test_animation_delegate.h"
#if defined(OS_WIN)
#include "base/win_util.h"
#endif
@@ -47,40 +47,6 @@ class CancelAnimation : public Animation {
///////////////////////////////////////////////////////////////////////////////
// LinearCase
-class TestAnimationDelegate : public AnimationDelegate {
- public:
- TestAnimationDelegate() :
- canceled_(false),
- finished_(false) {
- }
-
- virtual void AnimationStarted(const Animation* animation) {
- }
-
- virtual void AnimationEnded(const Animation* animation) {
- finished_ = true;
- MessageLoop::current()->Quit();
- }
-
- virtual void AnimationCanceled(const Animation* animation) {
- finished_ = true;
- canceled_ = true;
- MessageLoop::current()->Quit();
- }
-
- bool finished() {
- return finished_;
- }
-
- bool canceled() {
- return canceled_;
- }
-
- private:
- bool canceled_;
- bool finished_;
-};
-
TEST_F(AnimationTest, RunCase) {
TestAnimationDelegate ad;
RunAnimation a1(150, &ad);
@@ -120,4 +86,3 @@ TEST_F(AnimationTest, ShouldRenderRichAnimation) {
EXPECT_TRUE(Animation::ShouldRenderRichAnimation());
#endif
}
-
diff --git a/app/app.gyp b/app/app.gyp
index d8fef69..f87f721 100644
--- a/app/app.gyp
+++ b/app/app.gyp
@@ -28,6 +28,7 @@
'app_resources',
'../net/net.gyp:net_test_support',
'../skia/skia.gyp:skia',
+ '../testing/gmock.gyp:gmock',
'../testing/gtest.gyp:gtest',
'../third_party/icu/icu.gyp:icui18n',
'../third_party/icu/icu.gyp:icuuc',
@@ -37,6 +38,7 @@
'../third_party/zlib/zlib.gyp:zlib',
],
'sources': [
+ 'animation_container_unittest.cc',
'animation_unittest.cc',
'clipboard/clipboard_unittest.cc',
'l10n_util_mac_unittest.mm',
diff --git a/app/app_base.gypi b/app/app_base.gypi
index 5358a0b..07d5f3c 100644
--- a/app/app_base.gypi
+++ b/app/app_base.gypi
@@ -95,6 +95,8 @@
# Files that are not required for Win64 Native Client loader
'active_window_watcher_x.cc',
'active_window_watcher_x.h',
+ 'animation_container.cc',
+ 'animation_container.h',
'animation.cc',
'animation.h',
'bidi_line_iterator.cc',
diff --git a/app/slide_animation.h b/app/slide_animation.h
index 15357fd..3cf32d9 100644
--- a/app/slide_animation.h
+++ b/app/slide_animation.h
@@ -43,9 +43,6 @@
// }
class SlideAnimation : public Animation {
public:
- explicit SlideAnimation(AnimationDelegate* target);
- virtual ~SlideAnimation();
-
enum TweenType {
NONE, // Linear.
EASE_OUT, // Fast in, slow out (default).
@@ -55,6 +52,9 @@ class SlideAnimation : public Animation {
EASE_OUT_SNAP, // Fast in, slow out, snap to final value.
};
+ explicit SlideAnimation(AnimationDelegate* target);
+ virtual ~SlideAnimation();
+
// Set the animation back to the 0 state.
virtual void Reset();
virtual void Reset(double value);
diff --git a/app/test_animation_delegate.h b/app/test_animation_delegate.h
new file mode 100644
index 0000000..76f0f3e
--- /dev/null
+++ b/app/test_animation_delegate.h
@@ -0,0 +1,47 @@
+// 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_TEST_ANIMATION_DELEGATE_H_
+#define APP_TEST_ANIMATION_DELEGATE_H_
+
+#include "app/animation.h"
+#include "base/message_loop.h"
+
+// Trivial AnimationDelegate implementation. AnimationEnded/Canceled quit the
+// message loop.
+class TestAnimationDelegate : public AnimationDelegate {
+ public:
+ TestAnimationDelegate() : canceled_(false), finished_(false) {
+ }
+
+ virtual void AnimationStarted(const Animation* animation) {
+ }
+
+ virtual void AnimationEnded(const Animation* animation) {
+ finished_ = true;
+ MessageLoop::current()->Quit();
+ }
+
+ virtual void AnimationCanceled(const Animation* animation) {
+ finished_ = true;
+ canceled_ = true;
+ MessageLoop::current()->Quit();
+ }
+
+ bool finished() const {
+ return finished_;
+ }
+
+ bool canceled() const {
+ return canceled_;
+ }
+
+ private:
+ bool canceled_;
+ bool finished_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestAnimationDelegate);
+};
+
+#endif // APP_TEST_ANIMATION_DELEGATE_H_
diff --git a/app/throb_animation.cc b/app/throb_animation.cc
index 28b5763..e8e5730 100644
--- a/app/throb_animation.cc
+++ b/app/throb_animation.cc
@@ -43,8 +43,9 @@ void ThrobAnimation::Hide() {
SlideAnimation::Hide();
}
-void ThrobAnimation::Step() {
- Animation::Step();
+void ThrobAnimation::Step(base::TimeTicks time_now) {
+ Animation::Step(time_now);
+
if (!IsAnimating() && 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.
diff --git a/app/throb_animation.h b/app/throb_animation.h
index 43e4197..2405724 100644
--- a/app/throb_animation.h
+++ b/app/throb_animation.h
@@ -31,9 +31,6 @@ class ThrobAnimation : public SlideAnimation {
virtual void Show();
virtual void Hide();
- // Overriden to continually throb (assuming we're throbbing).
- virtual void Step();
-
// Overridden to maintain the slide duration.
virtual void SetSlideDuration(int duration) { slide_duration_ = duration; }
@@ -41,6 +38,10 @@ class ThrobAnimation : public SlideAnimation {
void set_cycles_remaining(int value) { cycles_remaining_ = value; }
int cycles_remaining() const { return cycles_remaining_; }
+ protected:
+ // Overriden to continually throb (assuming we're throbbing).
+ virtual void Step(base::TimeTicks time_now);
+
private:
// Resets state such that we behave like SlideAnimation.
void ResetForSlide();
diff --git a/chrome/browser/gtk/tabs/tab_renderer_gtk.cc b/chrome/browser/gtk/tabs/tab_renderer_gtk.cc
index ef1df93..20307d3 100644
--- a/chrome/browser/gtk/tabs/tab_renderer_gtk.cc
+++ b/chrome/browser/gtk/tabs/tab_renderer_gtk.cc
@@ -564,7 +564,7 @@ void TabRendererGtk::AnimationEnded(const Animation* animation) {
void TabRendererGtk::StartCrashAnimation() {
if (!crash_animation_.get())
crash_animation_.reset(new FavIconCrashAnimation(this));
- crash_animation_->Reset();
+ crash_animation_->Stop();
crash_animation_->Start();
}
diff --git a/chrome/browser/views/fullscreen_exit_bubble.h b/chrome/browser/views/fullscreen_exit_bubble.h
index 3767518..c9db5bb 100644
--- a/chrome/browser/views/fullscreen_exit_bubble.h
+++ b/chrome/browser/views/fullscreen_exit_bubble.h
@@ -7,6 +7,7 @@
#include "app/slide_animation.h"
#include "base/scoped_ptr.h"
+#include "base/timer.h"
#include "chrome/browser/command_updater.h"
#include "views/controls/link.h"
diff --git a/chrome/browser/views/infobars/infobars.h b/chrome/browser/views/infobars/infobars.h
index 5c0f5f6..56cf48f 100644
--- a/chrome/browser/views/infobars/infobars.h
+++ b/chrome/browser/views/infobars/infobars.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_VIEWS_INFOBARS_INFOBARS_H_
#include "app/animation.h"
+#include "base/task.h"
#include "chrome/browser/tab_contents/infobar_delegate.h"
#include "views/controls/button/button.h"
#include "views/controls/link.h"
diff --git a/chrome/browser/views/tabs/side_tab.cc b/chrome/browser/views/tabs/side_tab.cc
index c11fead..9c45dd2 100644
--- a/chrome/browser/views/tabs/side_tab.cc
+++ b/chrome/browser/views/tabs/side_tab.cc
@@ -6,6 +6,7 @@
#include "app/resource_bundle.h"
#include "app/theme_provider.h"
+#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "gfx/canvas.h"
#include "gfx/path.h"
diff --git a/chrome/browser/views/tabs/tab_renderer.cc b/chrome/browser/views/tabs/tab_renderer.cc
index fe1a1db..b84dc20 100644
--- a/chrome/browser/views/tabs/tab_renderer.cc
+++ b/chrome/browser/views/tabs/tab_renderer.cc
@@ -853,7 +853,7 @@ double TabRenderer::GetThrobValue() {
void TabRenderer::StartCrashAnimation() {
if (!crash_animation_)
crash_animation_ = new FavIconCrashAnimation(this);
- crash_animation_->Reset();
+ crash_animation_->Stop();
crash_animation_->Start();
}
diff --git a/views/animation/bounds_animator.cc b/views/animation/bounds_animator.cc
index 4ea34b0..6dbd105 100644
--- a/views/animation/bounds_animator.cc
+++ b/views/animation/bounds_animator.cc
@@ -5,106 +5,229 @@
#include "views/animation/bounds_animator.h"
#include "app/slide_animation.h"
-#include "base/compiler_specific.h"
+#include "base/scoped_ptr.h"
#include "views/view.h"
+// Duration in milliseconds for animations.
+static const int kAnimationDuration = 200;
+
namespace views {
-BoundsAnimator::BoundsAnimator()
- : ALLOW_THIS_IN_INITIALIZER_LIST(animation_(new SlideAnimation(this))),
- is_slide_(true) {
+BoundsAnimator::BoundsAnimator(View* parent)
+ : parent_(parent),
+ observer_(NULL),
+ container_(new AnimationContainer()) {
+ container_->set_observer(this);
}
BoundsAnimator::~BoundsAnimator() {
- data_.clear();
- animation_->set_delegate(NULL);
- animation_.reset();
+ // Reset the delegate so that we don't attempt to notify our observer from
+ // the destructor.
+ container_->set_observer(NULL);
+
+ // Delete all the animations, but don't remove any child views. We assume the
+ // view owns us and is going to be deleted anyway.
+ for (ViewToDataMap::iterator i = data_.begin(); i != data_.end(); ++i)
+ delete i->second.animation;
}
void BoundsAnimator::AnimateViewTo(View* view,
const gfx::Rect& target,
bool delete_when_done) {
+ DCHECK_EQ(view->GetParent(), parent_);
+
+ scoped_ptr<Animation> current_animation;
+
+ if (data_.find(view) != data_.end()) {
+ // Currently animating this view, blow away the current animation and
+ // we'll create another animation below.
+ // We delay deleting the view until the end so that we don't prematurely
+ // send out notification that we're done.
+ current_animation.reset(ResetAnimationForView(view));
+ } else if (target == view->bounds()) {
+ // View is already at the target location, delete it if necessary.
+ if (delete_when_done)
+ delete view;
+ return;
+ }
+
Data& data = data_[view];
data.start_bounds = view->bounds();
data.target_bounds = target;
+ data.animation = CreateAnimation();
data.delete_when_done = delete_when_done;
+
+ animation_to_view_[data.animation] = view;
+
+ data.animation->Show();
+}
+
+void BoundsAnimator::SetAnimationForView(View* view,
+ SlideAnimation* animation) {
+ scoped_ptr<SlideAnimation> animation_wrapper(animation);
+ if (data_.find(view) == data_.end())
+ return;
+
+ // We delay deleting the animation until the end so that we don't prematurely
+ // send out notification that we're done.
+ scoped_ptr<Animation> old_animation(ResetAnimationForView(view));
+
+ data_[view].animation = animation_wrapper.release();
+ animation_to_view_[animation] = view;
+
+ animation->set_delegate(this);
+ animation->SetContainer(container_.get());
+ animation->Show();
+}
+
+const SlideAnimation* BoundsAnimator::GetAnimationForView(View* view) {
+ return data_.find(view) == data_.end() ? NULL : data_[view].animation;
+}
+
+void BoundsAnimator::SetAnimationDelegate(View* view,
+ AnimationDelegate* delegate,
+ bool delete_when_done) {
+ DCHECK(IsAnimating(view));
+ data_[view].delegate = delegate;
+ data_[view].delete_delegate_when_done = delete_when_done;
+}
+
+void BoundsAnimator::StopAnimatingView(View* view) {
+ if (data_.find(view) == data_.end())
+ return;
+
+ data_[view].animation->Stop();
}
bool BoundsAnimator::IsAnimating(View* view) const {
return data_.find(view) != data_.end();
}
-void BoundsAnimator::Start() {
- // Unset the delegate so that we don't attempt to cleanup if the animation is
- // running and we cancel it.
- animation_->set_delegate(NULL);
+bool BoundsAnimator::IsAnimating() const {
+ return !data_.empty();
+}
- animation_->Stop();
+void BoundsAnimator::Cancel() {
+ if (data_.empty())
+ return;
- if (is_slide_) {
- // TODO(sky): this is yucky, need a better way to express this.
- static_cast<SlideAnimation*>(animation_.get())->Hide();
- static_cast<SlideAnimation*>(animation_.get())->Reset();
- static_cast<SlideAnimation*>(animation_.get())->Show();
- } else {
- animation_->Start();
- }
+ while (!data_.empty())
+ data_.begin()->second.animation->Stop();
- animation_->set_delegate(this);
+ // Invoke AnimationContainerProgressed to force a repaint and notify delegate.
+ AnimationContainerProgressed(container_.get());
}
-void BoundsAnimator::Stop() {
- animation_->set_delegate(NULL);
- animation_->Stop();
- animation_->set_delegate(this);
+SlideAnimation* BoundsAnimator::CreateAnimation() {
+ SlideAnimation* animation = new SlideAnimation(this);
+ animation->SetContainer(container_.get());
+ animation->SetSlideDuration(kAnimationDuration);
+ animation->SetTweenType(SlideAnimation::EASE_OUT);
+ return animation;
+}
+
+void BoundsAnimator::RemoveFromMapsAndDelete(View* view) {
+ DCHECK(data_.count(view) > 0);
+
+ Data& data = data_[view];
+ animation_to_view_.erase(data.animation);
+ if (data.delete_when_done)
+ delete view;
+ data_.erase(view);
+}
- DeleteViews();
- data_.clear();
+void BoundsAnimator::CleanupData(Data* data) {
+ if (data->delete_delegate_when_done) {
+ delete static_cast<OwnedAnimationDelegate*>(data->delegate);
+ data->delegate = NULL;
+ }
+
+ delete data->animation;
+ data->animation = NULL;
}
-void BoundsAnimator::SetAnimation(Animation* animation, bool is_slide) {
- animation_.reset(animation);
- is_slide_ = is_slide;
+Animation* BoundsAnimator::ResetAnimationForView(View* view) {
+ if (data_.find(view) == data_.end())
+ return NULL;
+
+ Animation* old_animation = data_[view].animation;
+ animation_to_view_.erase(old_animation);
+ data_[view].animation = NULL;
+ // Reset the delegate so that we don't attempt any processing when the
+ // animation calls us back.
+ old_animation->set_delegate(NULL);
+ return old_animation;
}
void BoundsAnimator::AnimationProgressed(const Animation* animation) {
- gfx::Rect repaint_bounds;
-
- for (ViewToDataMap::const_iterator i = data_.begin(); i != data_.end(); ++i) {
- View* view = i->first;
- gfx::Rect new_bounds =
- animation_->CurrentValueBetween(i->second.start_bounds,
- i->second.target_bounds);
- if (new_bounds != view->bounds()) {
- gfx::Rect total_bounds = new_bounds.Union(view->bounds());
- if (repaint_bounds.IsEmpty())
- repaint_bounds = total_bounds;
- else
- repaint_bounds = repaint_bounds.Union(total_bounds);
- view->SetBounds(new_bounds);
- }
+ DCHECK(animation_to_view_.find(animation) != animation_to_view_.end());
+
+ View* view = animation_to_view_[animation];
+ const Data& data = data_[view];
+ gfx::Rect new_bounds =
+ animation->CurrentValueBetween(data.start_bounds, data.target_bounds);
+ if (new_bounds != view->bounds()) {
+ gfx::Rect total_bounds = new_bounds.Union(view->bounds());
+
+ // Build up the region to repaint in repaint_bounds_. We'll do the repaint
+ // when all animations complete (in AnimationContainerProgressed).
+ if (repaint_bounds_.IsEmpty())
+ repaint_bounds_ = total_bounds;
+ else
+ repaint_bounds_ = repaint_bounds_.Union(total_bounds);
+
+ view->SetBounds(new_bounds);
}
- if (!data_.empty() && !repaint_bounds.IsEmpty())
- data_.begin()->first->GetParent()->SchedulePaint(repaint_bounds, false);
+ if (data_[view].delegate)
+ data_[view].delegate->AnimationProgressed(animation);
}
void BoundsAnimator::AnimationEnded(const Animation* animation) {
- DeleteViews();
+ View* view = animation_to_view_[animation];
+ AnimationDelegate* delegate = data_[view].delegate;
+
+ // Make a copy of the data as Remove empties out the maps.
+ Data data = data_[view];
+
+ RemoveFromMapsAndDelete(view);
+
+ if (delegate)
+ delegate->AnimationEnded(animation);
+
+ CleanupData(&data);
}
void BoundsAnimator::AnimationCanceled(const Animation* animation) {
+ View* view = animation_to_view_[animation];
+ AnimationDelegate* delegate = data_[view].delegate;
+
+ // Make a copy of the data as Remove empties out the maps.
+ Data data = data_[view];
+
+ RemoveFromMapsAndDelete(view);
+
+ if (delegate)
+ delegate->AnimationCanceled(animation);
+
+ CleanupData(&data);
}
-void BoundsAnimator::DeleteViews() {
- for (ViewToDataMap::iterator i = data_.begin(); i != data_.end(); ++i) {
- if (i->second.delete_when_done) {
- View* view = i->first;
- view->GetParent()->RemoveChildView(view);
- delete view;
- }
+void BoundsAnimator::AnimationContainerProgressed(
+ AnimationContainer* container) {
+ if (!repaint_bounds_.IsEmpty()) {
+ parent_->SchedulePaint(repaint_bounds_, false);
+ repaint_bounds_.SetRect(0, 0, 0, 0);
+ }
+
+ if (observer_ && !IsAnimating()) {
+ // Notify here rather than from AnimationXXX to avoid deleting the animation
+ // while the animaion is calling us.
+ observer_->OnBoundsAnimatorDone(this);
}
- data_.clear();
+}
+
+void BoundsAnimator::AnimationContainerEmpty(AnimationContainer* container) {
}
} // namespace views
diff --git a/views/animation/bounds_animator.h b/views/animation/bounds_animator.h
index c996a98..ab2fde9 100644
--- a/views/animation/bounds_animator.h
+++ b/views/animation/bounds_animator.h
@@ -8,77 +8,161 @@
#include <map>
#include "app/animation.h"
-#include "base/scoped_ptr.h"
+#include "app/animation_container.h"
#include "gfx/rect.h"
+class SlideAnimation;
+
namespace views {
+class BoundsAnimator;
class View;
+class BoundsAnimatorObserver {
+ public:
+ // Invoked when all animations are complete.
+ virtual void OnBoundsAnimatorDone(BoundsAnimator* animator) = 0;
+};
+
// Bounds animator is responsible for animating the bounds of a view from the
// the views current location and size to a target position and size. To use
// BoundsAnimator invoke AnimateViewTo for the set of views you want to
-// animate, followed by Start to start the animation.
-class BoundsAnimator : public AnimationDelegate {
+// animate.
+//
+// BoundsAnimator internally creates an animation for each view. If you need
+// a specific animation invoke SetAnimationForView after invoking AnimateViewTo.
+// You can attach an AnimationDelegate to the individual animation for a view
+// by way of SetAnimationDelegate. Additionally you can attach an observer to
+// the BoundsAnimator that is notified when all animations are complete.
+class BoundsAnimator : public AnimationDelegate,
+ public AnimationContainer::Observer {
public:
- BoundsAnimator();
+ // If |delete_when_done| is set to true in |SetAnimationDelegate| the
+ // |AnimationDelegate| must subclass this class.
+ class OwnedAnimationDelegate : public AnimationDelegate {
+ public:
+ virtual ~OwnedAnimationDelegate() {}
+ };
+
+ explicit BoundsAnimator(View* view);
~BoundsAnimator();
- // Schedules |view| to animate from it's current bounds to |target|. If
+ // Starts animating |view| from its current bounds to |target|. If
// |delete_when_done| is true the view is deleted when the animation
- // completes. Invoke Start to start the animation.
+ // completes. If there is already an animation running for the view it's
+ // stopped and a new one started.
void AnimateViewTo(View* view,
const gfx::Rect& target,
bool delete_when_done);
+ // Sets the animation for the specified view. BoundsAnimator takes ownership
+ // of the specified animation.
+ void SetAnimationForView(View* view, SlideAnimation* animation);
+
+ // Returns the animation for the specified view. BoundsAnimator owns the
+ // returned Animation.
+ const SlideAnimation* GetAnimationForView(View* view);
+
+ // Stops animating the specified view. If the view was scheduled for deletion
+ // it is deleted.
+ void StopAnimatingView(View* view);
+
+ // Sets the delegate for the animation created for the specified view. If
+ // |delete_when_done| is true the |delegate| is deleted when done and
+ // |delegate| must subclass OwnedAnimationDelegate.
+ void SetAnimationDelegate(View* view,
+ AnimationDelegate* delegate,
+ bool delete_when_done);
+
// Returns true if BoundsAnimator is animating the bounds of |view|.
bool IsAnimating(View* view) const;
- // Starts the animation.
- void Start();
+ // Returns true if BoundsAnimator is animating any view.
+ bool IsAnimating() const;
- // Stops the animation.
- void Stop();
+ // Cancels all animations, leaving the views at their current location and
+ // size. Any views marked for deletion are deleted.
+ void Cancel();
- // Sets the animation to use when animating changes. BoundsAnimator takes
- // ownership of |animation|. Set |is_slide| to true if |animation| is a
- // SlideAnimation.
- void SetAnimation(Animation* animation, bool is_slide);
-
- // AnimationDelegate overrides.
- virtual void AnimationProgressed(const Animation* animation);
- virtual void AnimationEnded(const Animation* animation);
- virtual void AnimationCanceled(const Animation* animation);
+ void set_observer(BoundsAnimatorObserver* observer) {
+ observer_ = observer;
+ }
private:
// Tracks data about the view being animated.
struct Data {
- Data() : delete_when_done(false) {}
+ Data()
+ : delete_when_done(false),
+ delete_delegate_when_done(false),
+ animation(NULL),
+ delegate(NULL) {}
// Should the view be deleted when done?
bool delete_when_done;
+ // If true the delegate is deleted when done.
+ bool delete_delegate_when_done;
+
// The initial bounds.
gfx::Rect start_bounds;
// Target bounds.
gfx::Rect target_bounds;
+
+ // The animation. We own this.
+ SlideAnimation* animation;
+
+ // Additional delegate for the animation, may be null.
+ AnimationDelegate* delegate;
};
typedef std::map<View*, Data> ViewToDataMap;
- // Empties data_, deleting any views that have been marked as needing to be
- // deleted.
- void DeleteViews();
+ typedef std::map<const Animation*, View*> AnimationToViewMap;
+
+ // Creates the animation to use for animating views.
+ SlideAnimation* CreateAnimation();
+
+ // Removes references to |view| and its animation as well as deleting |view|
+ // (if necessary). This does NOT delete the animation or delegate.
+ void RemoveFromMapsAndDelete(View* view);
+
+ // Does the necessary cleanup for |data|.
+ void CleanupData(Data* data);
+
+ // Used when changing the animation for a view. This resets the maps for
+ // the animation used by view and returns the current animation. Ownership
+ // of the returned animation passes to the caller.
+ Animation* ResetAnimationForView(View* view);
+
+ // AnimationDelegate overrides.
+ virtual void AnimationProgressed(const Animation* animation);
+ virtual void AnimationEnded(const Animation* animation);
+ virtual void AnimationCanceled(const Animation* animation);
+
+ // AnimationContainer::Observer overrides.
+ virtual void AnimationContainerProgressed(AnimationContainer* container);
+ virtual void AnimationContainerEmpty(AnimationContainer* container);
+
+ // Parent of all views being animated.
+ View* parent_;
+
+ BoundsAnimatorObserver* observer_;
+
+ // All animations we create up with the same container.
+ scoped_refptr<AnimationContainer> container_;
- // Mapes from view being animated to info about the view.
+ // Maps from view being animated to info about the view.
ViewToDataMap data_;
- // The animation.
- scoped_ptr<Animation> animation_;
+ // Makes from animation to view.
+ AnimationToViewMap animation_to_view_;
- // Is |animation_| a SlideAnimation?
- bool is_slide_;
+ // As the animations we created update (AnimationProgressed is invoked) this
+ // is updated. When all the animations have completed for a given tick of
+ // the timer (AnimationContainerProgressed is invoked) the parent_ is asked
+ // to repaint these bounds.
+ gfx::Rect repaint_bounds_;
DISALLOW_COPY_AND_ASSIGN(BoundsAnimator);
};