OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "views/animation/bounds_animator.h" | 5 #include "views/animation/bounds_animator.h" |
6 | 6 |
7 #include "app/slide_animation.h" | 7 #include "app/slide_animation.h" |
8 #include "base/compiler_specific.h" | 8 #include "base/scoped_ptr.h" |
9 #include "views/view.h" | 9 #include "views/view.h" |
10 | 10 |
| 11 // Duration in milliseconds for animations. |
| 12 static const int kAnimationDuration = 200; |
| 13 |
11 namespace views { | 14 namespace views { |
12 | 15 |
13 BoundsAnimator::BoundsAnimator() | 16 BoundsAnimator::BoundsAnimator(View* parent) |
14 : ALLOW_THIS_IN_INITIALIZER_LIST(animation_(new SlideAnimation(this))), | 17 : parent_(parent), |
15 is_slide_(true) { | 18 observer_(NULL), |
| 19 container_(new AnimationContainer()) { |
| 20 container_->set_observer(this); |
16 } | 21 } |
17 | 22 |
18 BoundsAnimator::~BoundsAnimator() { | 23 BoundsAnimator::~BoundsAnimator() { |
19 data_.clear(); | 24 // Reset the delegate so that we don't attempt to notify our observer from |
20 animation_->set_delegate(NULL); | 25 // the destructor. |
21 animation_.reset(); | 26 container_->set_observer(NULL); |
| 27 |
| 28 // Delete all the animations, but don't remove any child views. We assume the |
| 29 // view owns us and is going to be deleted anyway. |
| 30 for (ViewToDataMap::iterator i = data_.begin(); i != data_.end(); ++i) |
| 31 delete i->second.animation; |
22 } | 32 } |
23 | 33 |
24 void BoundsAnimator::AnimateViewTo(View* view, | 34 void BoundsAnimator::AnimateViewTo(View* view, |
25 const gfx::Rect& target, | 35 const gfx::Rect& target, |
26 bool delete_when_done) { | 36 bool delete_when_done) { |
| 37 DCHECK_EQ(view->GetParent(), parent_); |
| 38 |
| 39 scoped_ptr<Animation> current_animation; |
| 40 |
| 41 if (data_.find(view) != data_.end()) { |
| 42 // Currently animating this view, blow away the current animation and |
| 43 // we'll create another animation below. |
| 44 // We delay deleting the view until the end so that we don't prematurely |
| 45 // send out notification that we're done. |
| 46 current_animation.reset(ResetAnimationForView(view)); |
| 47 } else if (target == view->bounds()) { |
| 48 // View is already at the target location, delete it if necessary. |
| 49 if (delete_when_done) |
| 50 delete view; |
| 51 return; |
| 52 } |
| 53 |
27 Data& data = data_[view]; | 54 Data& data = data_[view]; |
28 data.start_bounds = view->bounds(); | 55 data.start_bounds = view->bounds(); |
29 data.target_bounds = target; | 56 data.target_bounds = target; |
| 57 data.animation = CreateAnimation(); |
30 data.delete_when_done = delete_when_done; | 58 data.delete_when_done = delete_when_done; |
| 59 |
| 60 animation_to_view_[data.animation] = view; |
| 61 |
| 62 data.animation->Show(); |
| 63 } |
| 64 |
| 65 void BoundsAnimator::SetAnimationForView(View* view, |
| 66 SlideAnimation* animation) { |
| 67 scoped_ptr<SlideAnimation> animation_wrapper(animation); |
| 68 if (data_.find(view) == data_.end()) |
| 69 return; |
| 70 |
| 71 // We delay deleting the animation until the end so that we don't prematurely |
| 72 // send out notification that we're done. |
| 73 scoped_ptr<Animation> old_animation(ResetAnimationForView(view)); |
| 74 |
| 75 data_[view].animation = animation_wrapper.release(); |
| 76 animation_to_view_[animation] = view; |
| 77 |
| 78 animation->set_delegate(this); |
| 79 animation->SetContainer(container_.get()); |
| 80 animation->Show(); |
| 81 } |
| 82 |
| 83 const SlideAnimation* BoundsAnimator::GetAnimationForView(View* view) { |
| 84 return data_.find(view) == data_.end() ? NULL : data_[view].animation; |
| 85 } |
| 86 |
| 87 void BoundsAnimator::SetAnimationDelegate(View* view, |
| 88 AnimationDelegate* delegate, |
| 89 bool delete_when_done) { |
| 90 DCHECK(IsAnimating(view)); |
| 91 data_[view].delegate = delegate; |
| 92 data_[view].delete_delegate_when_done = delete_when_done; |
| 93 } |
| 94 |
| 95 void BoundsAnimator::StopAnimatingView(View* view) { |
| 96 if (data_.find(view) == data_.end()) |
| 97 return; |
| 98 |
| 99 data_[view].animation->Stop(); |
31 } | 100 } |
32 | 101 |
33 bool BoundsAnimator::IsAnimating(View* view) const { | 102 bool BoundsAnimator::IsAnimating(View* view) const { |
34 return data_.find(view) != data_.end(); | 103 return data_.find(view) != data_.end(); |
35 } | 104 } |
36 | 105 |
37 void BoundsAnimator::Start() { | 106 bool BoundsAnimator::IsAnimating() const { |
38 // Unset the delegate so that we don't attempt to cleanup if the animation is | 107 return !data_.empty(); |
39 // running and we cancel it. | 108 } |
40 animation_->set_delegate(NULL); | 109 |
41 | 110 void BoundsAnimator::Cancel() { |
42 animation_->Stop(); | 111 if (data_.empty()) |
43 | 112 return; |
44 if (is_slide_) { | 113 |
45 // TODO(sky): this is yucky, need a better way to express this. | 114 while (!data_.empty()) |
46 static_cast<SlideAnimation*>(animation_.get())->Hide(); | 115 data_.begin()->second.animation->Stop(); |
47 static_cast<SlideAnimation*>(animation_.get())->Reset(); | 116 |
48 static_cast<SlideAnimation*>(animation_.get())->Show(); | 117 // Invoke AnimationContainerProgressed to force a repaint and notify delegate. |
49 } else { | 118 AnimationContainerProgressed(container_.get()); |
50 animation_->Start(); | 119 } |
51 } | 120 |
52 | 121 SlideAnimation* BoundsAnimator::CreateAnimation() { |
53 animation_->set_delegate(this); | 122 SlideAnimation* animation = new SlideAnimation(this); |
54 } | 123 animation->SetContainer(container_.get()); |
55 | 124 animation->SetSlideDuration(kAnimationDuration); |
56 void BoundsAnimator::Stop() { | 125 animation->SetTweenType(SlideAnimation::EASE_OUT); |
57 animation_->set_delegate(NULL); | 126 return animation; |
58 animation_->Stop(); | 127 } |
59 animation_->set_delegate(this); | 128 |
60 | 129 void BoundsAnimator::RemoveFromMapsAndDelete(View* view) { |
61 DeleteViews(); | 130 DCHECK(data_.count(view) > 0); |
62 data_.clear(); | 131 |
63 } | 132 Data& data = data_[view]; |
64 | 133 animation_to_view_.erase(data.animation); |
65 void BoundsAnimator::SetAnimation(Animation* animation, bool is_slide) { | 134 if (data.delete_when_done) |
66 animation_.reset(animation); | 135 delete view; |
67 is_slide_ = is_slide; | 136 data_.erase(view); |
| 137 } |
| 138 |
| 139 void BoundsAnimator::CleanupData(Data* data) { |
| 140 if (data->delete_delegate_when_done) { |
| 141 delete static_cast<OwnedAnimationDelegate*>(data->delegate); |
| 142 data->delegate = NULL; |
| 143 } |
| 144 |
| 145 delete data->animation; |
| 146 data->animation = NULL; |
| 147 } |
| 148 |
| 149 Animation* BoundsAnimator::ResetAnimationForView(View* view) { |
| 150 if (data_.find(view) == data_.end()) |
| 151 return NULL; |
| 152 |
| 153 Animation* old_animation = data_[view].animation; |
| 154 animation_to_view_.erase(old_animation); |
| 155 data_[view].animation = NULL; |
| 156 // Reset the delegate so that we don't attempt any processing when the |
| 157 // animation calls us back. |
| 158 old_animation->set_delegate(NULL); |
| 159 return old_animation; |
68 } | 160 } |
69 | 161 |
70 void BoundsAnimator::AnimationProgressed(const Animation* animation) { | 162 void BoundsAnimator::AnimationProgressed(const Animation* animation) { |
71 gfx::Rect repaint_bounds; | 163 DCHECK(animation_to_view_.find(animation) != animation_to_view_.end()); |
72 | 164 |
73 for (ViewToDataMap::const_iterator i = data_.begin(); i != data_.end(); ++i) { | 165 View* view = animation_to_view_[animation]; |
74 View* view = i->first; | 166 const Data& data = data_[view]; |
75 gfx::Rect new_bounds = | 167 gfx::Rect new_bounds = |
76 animation_->CurrentValueBetween(i->second.start_bounds, | 168 animation->CurrentValueBetween(data.start_bounds, data.target_bounds); |
77 i->second.target_bounds); | 169 if (new_bounds != view->bounds()) { |
78 if (new_bounds != view->bounds()) { | 170 gfx::Rect total_bounds = new_bounds.Union(view->bounds()); |
79 gfx::Rect total_bounds = new_bounds.Union(view->bounds()); | 171 |
80 if (repaint_bounds.IsEmpty()) | 172 // Build up the region to repaint in repaint_bounds_. We'll do the repaint |
81 repaint_bounds = total_bounds; | 173 // when all animations complete (in AnimationContainerProgressed). |
82 else | 174 if (repaint_bounds_.IsEmpty()) |
83 repaint_bounds = repaint_bounds.Union(total_bounds); | 175 repaint_bounds_ = total_bounds; |
84 view->SetBounds(new_bounds); | 176 else |
85 } | 177 repaint_bounds_ = repaint_bounds_.Union(total_bounds); |
86 } | 178 |
87 | 179 view->SetBounds(new_bounds); |
88 if (!data_.empty() && !repaint_bounds.IsEmpty()) | 180 } |
89 data_.begin()->first->GetParent()->SchedulePaint(repaint_bounds, false); | 181 |
| 182 if (data_[view].delegate) |
| 183 data_[view].delegate->AnimationProgressed(animation); |
90 } | 184 } |
91 | 185 |
92 void BoundsAnimator::AnimationEnded(const Animation* animation) { | 186 void BoundsAnimator::AnimationEnded(const Animation* animation) { |
93 DeleteViews(); | 187 View* view = animation_to_view_[animation]; |
| 188 AnimationDelegate* delegate = data_[view].delegate; |
| 189 |
| 190 // Make a copy of the data as Remove empties out the maps. |
| 191 Data data = data_[view]; |
| 192 |
| 193 RemoveFromMapsAndDelete(view); |
| 194 |
| 195 if (delegate) |
| 196 delegate->AnimationEnded(animation); |
| 197 |
| 198 CleanupData(&data); |
94 } | 199 } |
95 | 200 |
96 void BoundsAnimator::AnimationCanceled(const Animation* animation) { | 201 void BoundsAnimator::AnimationCanceled(const Animation* animation) { |
97 } | 202 View* view = animation_to_view_[animation]; |
98 | 203 AnimationDelegate* delegate = data_[view].delegate; |
99 void BoundsAnimator::DeleteViews() { | 204 |
100 for (ViewToDataMap::iterator i = data_.begin(); i != data_.end(); ++i) { | 205 // Make a copy of the data as Remove empties out the maps. |
101 if (i->second.delete_when_done) { | 206 Data data = data_[view]; |
102 View* view = i->first; | 207 |
103 view->GetParent()->RemoveChildView(view); | 208 RemoveFromMapsAndDelete(view); |
104 delete view; | 209 |
105 } | 210 if (delegate) |
106 } | 211 delegate->AnimationCanceled(animation); |
107 data_.clear(); | 212 |
| 213 CleanupData(&data); |
| 214 } |
| 215 |
| 216 void BoundsAnimator::AnimationContainerProgressed( |
| 217 AnimationContainer* container) { |
| 218 if (!repaint_bounds_.IsEmpty()) { |
| 219 parent_->SchedulePaint(repaint_bounds_, false); |
| 220 repaint_bounds_.SetRect(0, 0, 0, 0); |
| 221 } |
| 222 |
| 223 if (observer_ && !IsAnimating()) { |
| 224 // Notify here rather than from AnimationXXX to avoid deleting the animation |
| 225 // while the animaion is calling us. |
| 226 observer_->OnBoundsAnimatorDone(this); |
| 227 } |
| 228 } |
| 229 |
| 230 void BoundsAnimator::AnimationContainerEmpty(AnimationContainer* container) { |
108 } | 231 } |
109 | 232 |
110 } // namespace views | 233 } // namespace views |
OLD | NEW |