summaryrefslogtreecommitdiffstats
path: root/views
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 /views
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
Diffstat (limited to 'views')
-rw-r--r--views/animation/bounds_animator.cc235
-rw-r--r--views/animation/bounds_animator.h140
2 files changed, 291 insertions, 84 deletions
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);
};