blob: 7684647a1c065d769c628ba54dc0e4925960ec59 [file] [log] [blame]
// Copyright (c) 2012 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 UI_VIEWS_ANIMATION_BOUNDS_ANIMATOR_H_
#define UI_VIEWS_ANIMATION_BOUNDS_ANIMATOR_H_
#include <map>
#include <memory>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
#include "ui/gfx/animation/animation_container.h"
#include "ui/gfx/animation/animation_container_observer.h"
#include "ui/gfx/animation/tween.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/animation/animation_delegate_views.h"
#include "ui/views/views_export.h"
namespace gfx {
class SlideAnimation;
}
namespace views {
class BoundsAnimatorObserver;
class View;
// 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.
//
// 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.
//
// There is an option to apply transforms on the view instead of repainting and
// relayouting each animation tick. This should be used if the size of the view
// is not changing. It can be considered, but may have funny looking visuals for
// other cases, depending on the content. If layers are not present, they are
// created and destroyed as necessary.
class VIEWS_EXPORT BoundsAnimator : public AnimationDelegateViews {
public:
explicit BoundsAnimator(View* view, bool use_transforms = false);
~BoundsAnimator() override;
// Starts animating |view| from its current bounds to |target|. If there is
// already an animation running for the view it's stopped and a new one
// started. If an AnimationDelegate has been set for |view| it is removed
// (after being notified that the animation was canceled).
void AnimateViewTo(
View* view,
const gfx::Rect& target,
std::unique_ptr<gfx::AnimationDelegate> delegate = nullptr);
// Similar to |AnimateViewTo|, but does not reset the animation, only the
// target bounds. If |view| is not being animated this is the same as
// invoking |AnimateViewTo|.
void SetTargetBounds(View* view, const gfx::Rect& target);
// Returns the target bounds for the specified view. If |view| is not
// animating its current bounds is returned.
gfx::Rect GetTargetBounds(const View* view) const;
// Returns the animation for the specified view. BoundsAnimator owns the
// returned Animation.
const gfx::SlideAnimation* GetAnimationForView(View* view);
// Stops animating the specified view.
void StopAnimatingView(View* view);
// Sets the delegate for the animation for the specified view.
void SetAnimationDelegate(View* view,
std::unique_ptr<gfx::AnimationDelegate> delegate);
// Returns true if BoundsAnimator is animating the bounds of |view|.
bool IsAnimating(View* view) const;
// Returns true if BoundsAnimator is animating any view.
bool IsAnimating() const;
// Cancels all animations, leaving the views at their current location and
// size. Any views marked for deletion are deleted.
void Cancel();
// Overrides default animation duration.
void SetAnimationDuration(base::TimeDelta duration);
// Gets the currently used animation duration.
base::TimeDelta GetAnimationDuration() const { return animation_duration_; }
// Sets the tween type for new animations. Default is EASE_OUT.
void set_tween_type(gfx::Tween::Type type) { tween_type_ = type; }
void AddObserver(BoundsAnimatorObserver* observer);
void RemoveObserver(BoundsAnimatorObserver* observer);
gfx::AnimationContainer* container() { return container_.get(); }
protected:
// Creates the animation to use for animating views.
virtual std::unique_ptr<gfx::SlideAnimation> CreateAnimation();
private:
// Tracks data about the view being animated.
struct Data {
Data();
Data(Data&&);
Data& operator=(Data&&);
~Data();
// The initial bounds.
gfx::Rect start_bounds;
// Target bounds.
gfx::Rect target_bounds;
// The animation.
std::unique_ptr<gfx::SlideAnimation> animation;
// Delegate for the animation, may be nullptr.
std::unique_ptr<gfx::AnimationDelegate> delegate;
// Will only exist if |use_transforms_| is true.
absl::optional<gfx::Transform> target_transform;
};
// Used by AnimationEndedOrCanceled.
enum class AnimationEndType { kEnded, kCanceled };
using ViewToDataMap = std::map<const View*, Data>;
using AnimationToViewMap = std::map<const gfx::Animation*, View*>;
// Removes references to |view| and its animation. Returns the data for the
// caller to handle cleanup.
Data RemoveFromMaps(View* view);
// Does the necessary cleanup for |data|. If |send_cancel| is true and a
// delegate has been installed on |data| AnimationCanceled is invoked on it.
void CleanupData(bool send_cancel, 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.
std::unique_ptr<gfx::Animation> ResetAnimationForView(View* view);
// Invoked from AnimationEnded and AnimationCanceled.
void AnimationEndedOrCanceled(const gfx::Animation* animation,
AnimationEndType type);
// AnimationDelegateViews overrides.
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationEnded(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
void AnimationContainerProgressed(
gfx::AnimationContainer* container) override;
void AnimationContainerEmpty(gfx::AnimationContainer* container) override;
void OnChildViewRemoved(views::View* observed_view,
views::View* child) override;
base::TimeDelta GetAnimationDurationForReporting() const override;
// Parent of all views being animated.
View* parent_;
// A more performant version of the bounds animations which updates the
// transform of the views and therefore skips repainting and relayouting until
// the end of the animation. Note that this may not look as good as the
// regular version, depending on the content and the source and destination
// bounds. In the case the provided source bounds is empty, we cannot derive a
// transform so that particular view will still use a bounds animation, even
// with this flag on.
const bool use_transforms_;
base::ObserverList<BoundsAnimatorObserver>::Unchecked observers_;
// All animations we create up with the same container.
scoped_refptr<gfx::AnimationContainer> container_;
// Maps from view being animated to info about the view.
ViewToDataMap data_;
// Maps from animation to view.
AnimationToViewMap animation_to_view_;
// As the animations we create 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_;
base::TimeDelta animation_duration_ = base::TimeDelta::FromMilliseconds(200);
gfx::Tween::Type tween_type_ = gfx::Tween::EASE_OUT;
DISALLOW_COPY_AND_ASSIGN(BoundsAnimator);
};
} // namespace views
#endif // UI_VIEWS_ANIMATION_BOUNDS_ANIMATOR_H_