summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);
};