bounds_animator.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/views/animation/bounds_animator.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "base/containers/contains.h"
  8. #include "base/observer_list.h"
  9. #include "ui/gfx/animation/animation_container.h"
  10. #include "ui/gfx/animation/slide_animation.h"
  11. #include "ui/gfx/animation/tween.h"
  12. #include "ui/gfx/geometry/transform_util.h"
  13. #include "ui/views/animation/bounds_animator_observer.h"
  14. #include "ui/views/view.h"
  15. #include "ui/views/widget/widget.h"
  16. // This should be after all other #includes.
  17. #if defined(_WINDOWS_) // Detect whether windows.h was included.
  18. #include "base/win/windows_h_disallowed.h"
  19. #endif // defined(_WINDOWS_)
  20. namespace views {
  21. BoundsAnimator::BoundsAnimator(View* parent, bool use_transforms)
  22. : AnimationDelegateViews(parent),
  23. parent_(parent),
  24. use_transforms_(use_transforms),
  25. container_(new gfx::AnimationContainer()) {}
  26. BoundsAnimator::~BoundsAnimator() {
  27. // Delete all the animations, but don't remove any child views. We assume the
  28. // view owns us and is going to be deleted anyway. However, deleting a
  29. // delegate may results in removing a child view, so empty the data_ first so
  30. // that it won't call AnimationCanceled().
  31. ViewToDataMap data;
  32. data.swap(data_);
  33. for (auto& entry : data)
  34. CleanupData(false, &entry.second);
  35. }
  36. void BoundsAnimator::AnimateViewTo(
  37. View* view,
  38. const gfx::Rect& target,
  39. std::unique_ptr<gfx::AnimationDelegate> delegate) {
  40. DCHECK(view);
  41. DCHECK_EQ(view->parent(), parent_);
  42. const bool is_animating = IsAnimating(view);
  43. // Return early if the existing animation on |view| has the same target
  44. // bounds.
  45. if (is_animating && target == data_[view].target_bounds) {
  46. // If this animation specifies a different delegate, swap them out.
  47. if (delegate && delegate != data_[view].delegate)
  48. SetAnimationDelegate(view, std::move(delegate));
  49. return;
  50. }
  51. Data existing_data;
  52. if (is_animating) {
  53. DCHECK(base::Contains(data_, view));
  54. const bool used_transforms = data_[view].target_transform.has_value();
  55. if (used_transforms) {
  56. // Using transforms means a view does not have the proper bounds until an
  57. // animation is complete or canceled. So here we cancel the animation so
  58. // that the bounds can be updated. Note that this means that callers who
  59. // want to set bounds (i.e. View::SetBoundsRect()) directly before calling
  60. // this function will have to explicitly call StopAnimatingView() before
  61. // doing so.
  62. StopAnimatingView(view);
  63. } else {
  64. // Don't immediately delete the animation, that might trigger a callback
  65. // from the animation container.
  66. existing_data = RemoveFromMaps(view);
  67. }
  68. }
  69. // NOTE: we don't check if the view is already at the target location. Doing
  70. // so leads to odd cases where no animations may be present after invoking
  71. // AnimateViewTo. AnimationProgressed does nothing when the bounds of the
  72. // view don't change.
  73. Data& data = data_[view];
  74. data.start_bounds = view->bounds();
  75. data.target_bounds = target;
  76. data.animation = CreateAnimation();
  77. data.delegate = std::move(delegate);
  78. // If the start bounds are empty we cannot derive a transform from start to
  79. // target. Views with existing transforms are not supported. Default back to
  80. // using the bounds update animation in these cases.
  81. // Note that transform is not used if bounds animation requires scaling.
  82. // Because for some views, their children cannot be scaled with the same scale
  83. // factor. For example, ShelfAppButton's size in normal state and dense state
  84. // is 56 and 48 respectively while the size of icon image, the child of
  85. // ShelfAppButton, is 44 and 36 respectively.
  86. if (use_transforms_ && !data.start_bounds.IsEmpty() &&
  87. view->GetTransform().IsIdentity() &&
  88. data.start_bounds.size() == data.target_bounds.size()) {
  89. // Calculate the target transform. Note that we don't reset the transform if
  90. // there already was one, otherwise users will end up with visual bounds
  91. // different than what they set.
  92. // Note that View::SetTransform() does not handle RTL, which is different
  93. // from View::SetBounds(). So mirror the start bounds and target bounds
  94. // manually if necessary.
  95. const gfx::Transform target_transform = gfx::TransformBetweenRects(
  96. gfx::RectF(parent_->GetMirroredRect(data.start_bounds)),
  97. gfx::RectF(parent_->GetMirroredRect(data.target_bounds)));
  98. data.target_transform = target_transform;
  99. }
  100. animation_to_view_[data.animation.get()] = view;
  101. data.animation->Show();
  102. CleanupData(true, &existing_data);
  103. }
  104. void BoundsAnimator::SetTargetBounds(View* view, const gfx::Rect& target) {
  105. const auto i = data_.find(view);
  106. if (i == data_.end())
  107. AnimateViewTo(view, target);
  108. else
  109. i->second.target_bounds = target;
  110. }
  111. gfx::Rect BoundsAnimator::GetTargetBounds(const View* view) const {
  112. const auto i = data_.find(view);
  113. return (i == data_.end()) ? view->bounds() : i->second.target_bounds;
  114. }
  115. const gfx::SlideAnimation* BoundsAnimator::GetAnimationForView(View* view) {
  116. const auto i = data_.find(view);
  117. return (i == data_.end()) ? nullptr : i->second.animation.get();
  118. }
  119. void BoundsAnimator::SetAnimationDelegate(
  120. View* view,
  121. std::unique_ptr<AnimationDelegate> delegate) {
  122. const auto i = data_.find(view);
  123. DCHECK(i != data_.end());
  124. i->second.delegate = std::move(delegate);
  125. }
  126. void BoundsAnimator::StopAnimatingView(View* view) {
  127. const auto i = data_.find(view);
  128. if (i != data_.end())
  129. i->second.animation->Stop();
  130. }
  131. bool BoundsAnimator::IsAnimating(View* view) const {
  132. return data_.find(view) != data_.end();
  133. }
  134. bool BoundsAnimator::IsAnimating() const {
  135. return !data_.empty();
  136. }
  137. void BoundsAnimator::Complete() {
  138. if (data_.empty())
  139. return;
  140. while (!data_.empty())
  141. data_.begin()->second.animation->End();
  142. // Invoke AnimationContainerProgressed to force a repaint and notify delegate.
  143. AnimationContainerProgressed(container_.get());
  144. }
  145. void BoundsAnimator::Cancel() {
  146. if (data_.empty())
  147. return;
  148. while (!data_.empty())
  149. data_.begin()->second.animation->Stop();
  150. // Invoke AnimationContainerProgressed to force a repaint and notify delegate.
  151. AnimationContainerProgressed(container_.get());
  152. }
  153. void BoundsAnimator::SetAnimationDuration(base::TimeDelta duration) {
  154. animation_duration_ = duration;
  155. }
  156. void BoundsAnimator::AddObserver(BoundsAnimatorObserver* observer) {
  157. observers_.AddObserver(observer);
  158. }
  159. void BoundsAnimator::RemoveObserver(BoundsAnimatorObserver* observer) {
  160. observers_.RemoveObserver(observer);
  161. }
  162. std::unique_ptr<gfx::SlideAnimation> BoundsAnimator::CreateAnimation() {
  163. auto animation = std::make_unique<gfx::SlideAnimation>(this);
  164. animation->SetContainer(container_.get());
  165. animation->SetSlideDuration(animation_duration_);
  166. animation->SetTweenType(tween_type_);
  167. return animation;
  168. }
  169. BoundsAnimator::Data::Data() = default;
  170. BoundsAnimator::Data::Data(Data&&) = default;
  171. BoundsAnimator::Data& BoundsAnimator::Data::operator=(Data&&) = default;
  172. BoundsAnimator::Data::~Data() = default;
  173. BoundsAnimator::Data BoundsAnimator::RemoveFromMaps(View* view) {
  174. const auto i = data_.find(view);
  175. DCHECK(i != data_.end());
  176. DCHECK_GT(animation_to_view_.count(i->second.animation.get()), 0u);
  177. Data old_data = std::move(i->second);
  178. data_.erase(view);
  179. animation_to_view_.erase(old_data.animation.get());
  180. return old_data;
  181. }
  182. void BoundsAnimator::CleanupData(bool send_cancel, Data* data) {
  183. if (send_cancel && data->delegate)
  184. data->delegate->AnimationCanceled(data->animation.get());
  185. data->delegate.reset();
  186. if (data->animation) {
  187. data->animation->set_delegate(nullptr);
  188. data->animation.reset();
  189. }
  190. }
  191. std::unique_ptr<gfx::Animation> BoundsAnimator::ResetAnimationForView(
  192. View* view) {
  193. const auto i = data_.find(view);
  194. if (i == data_.end())
  195. return nullptr;
  196. std::unique_ptr<gfx::Animation> old_animation =
  197. std::move(i->second.animation);
  198. animation_to_view_.erase(old_animation.get());
  199. // Reset the delegate so that we don't attempt any processing when the
  200. // animation calls us back.
  201. old_animation->set_delegate(nullptr);
  202. return old_animation;
  203. }
  204. void BoundsAnimator::AnimationEndedOrCanceled(const gfx::Animation* animation,
  205. AnimationEndType type) {
  206. DCHECK(animation_to_view_.find(animation) != animation_to_view_.end());
  207. View* view = animation_to_view_[animation];
  208. DCHECK(view);
  209. // Notify the delegate so it has a chance to paint the final state of a
  210. // completed animation.
  211. if (type == AnimationEndType::kEnded) {
  212. DCHECK_EQ(animation->GetCurrentValue(), 1.0);
  213. AnimationProgressed(animation);
  214. }
  215. // Save the data for later clean up.
  216. Data data = RemoveFromMaps(view);
  217. if (data.target_transform) {
  218. if (type == AnimationEndType::kEnded) {
  219. // Set the bounds at the end of the animation and reset the transform.
  220. view->SetBoundsRect(data.target_bounds);
  221. } else {
  222. DCHECK_EQ(AnimationEndType::kCanceled, type);
  223. // Get the existing transform and apply it to the start bounds which is
  224. // the current bounds of the view. This will place the bounds at the place
  225. // where the animation stopped. See comment in AnimateViewTo() for details
  226. // as to why GetMirroredRect() is used.
  227. const gfx::Transform transform = view->GetTransform();
  228. gfx::RectF bounds_f(parent_->GetMirroredRect(view->bounds()));
  229. transform.TransformRect(&bounds_f);
  230. view->SetBoundsRect(
  231. parent_->GetMirroredRect(gfx::ToRoundedRect(bounds_f)));
  232. }
  233. view->SetTransform(gfx::Transform());
  234. }
  235. if (data.delegate) {
  236. if (type == AnimationEndType::kEnded) {
  237. data.delegate->AnimationEnded(animation);
  238. } else {
  239. DCHECK_EQ(AnimationEndType::kCanceled, type);
  240. data.delegate->AnimationCanceled(animation);
  241. }
  242. }
  243. CleanupData(false, &data);
  244. }
  245. void BoundsAnimator::AnimationProgressed(const gfx::Animation* animation) {
  246. DCHECK(animation_to_view_.find(animation) != animation_to_view_.end());
  247. View* view = animation_to_view_[animation];
  248. DCHECK(view);
  249. const Data& data = data_[view];
  250. if (data.target_transform) {
  251. const gfx::Transform current_transform = gfx::Tween::TransformValueBetween(
  252. animation->GetCurrentValue(), gfx::Transform(), *data.target_transform);
  253. view->SetTransform(current_transform);
  254. } else {
  255. gfx::Rect new_bounds =
  256. animation->CurrentValueBetween(data.start_bounds, data.target_bounds);
  257. if (new_bounds != view->bounds()) {
  258. gfx::Rect total_bounds = gfx::UnionRects(new_bounds, view->bounds());
  259. // Build up the region to repaint in repaint_bounds_. We'll do the repaint
  260. // when all animations complete (in AnimationContainerProgressed).
  261. repaint_bounds_.Union(total_bounds);
  262. view->SetBoundsRect(new_bounds);
  263. }
  264. }
  265. if (data.delegate)
  266. data.delegate->AnimationProgressed(animation);
  267. }
  268. void BoundsAnimator::AnimationEnded(const gfx::Animation* animation) {
  269. AnimationEndedOrCanceled(animation, AnimationEndType::kEnded);
  270. }
  271. void BoundsAnimator::AnimationCanceled(const gfx::Animation* animation) {
  272. AnimationEndedOrCanceled(animation, AnimationEndType::kCanceled);
  273. }
  274. void BoundsAnimator::AnimationContainerProgressed(
  275. gfx::AnimationContainer* container) {
  276. if (!repaint_bounds_.IsEmpty()) {
  277. // Adjust for rtl.
  278. repaint_bounds_.set_x(parent_->GetMirroredXWithWidthInView(
  279. repaint_bounds_.x(), repaint_bounds_.width()));
  280. parent_->SchedulePaintInRect(repaint_bounds_);
  281. repaint_bounds_.SetRect(0, 0, 0, 0);
  282. }
  283. for (BoundsAnimatorObserver& observer : observers_)
  284. observer.OnBoundsAnimatorProgressed(this);
  285. if (!IsAnimating()) {
  286. // Notify here rather than from AnimationXXX to avoid deleting the animation
  287. // while the animation is calling us.
  288. for (BoundsAnimatorObserver& observer : observers_)
  289. observer.OnBoundsAnimatorDone(this);
  290. }
  291. }
  292. void BoundsAnimator::AnimationContainerEmpty(
  293. gfx::AnimationContainer* container) {}
  294. void BoundsAnimator::OnChildViewRemoved(views::View* observed_view,
  295. views::View* removed) {
  296. DCHECK_EQ(parent_, observed_view);
  297. const auto iter = data_.find(removed);
  298. if (iter == data_.end())
  299. return;
  300. AnimationCanceled(iter->second.animation.get());
  301. }
  302. base::TimeDelta BoundsAnimator::GetAnimationDurationForReporting() const {
  303. return GetAnimationDuration();
  304. }
  305. } // namespace views