Extracted InkDropAnimator class and InkDropState enum from InkDropAnimationController.
NOTE: This is based on this review: https://codereview.chromium.org/1280953003/ .
Follow up review: https://codereview.chromium.org/1298513003/
The InkDropAnimationController is becoming overloaded and the ink drop animation is only going to become more complex. Thus it will be easier to iterate on it if it becomes contained in it's own .h/.cc files.
TEST=Tested clicking, touching, and long pressing all the affected
ToolbarButton's with and without material design enabled. Verified
reasonable visuals and no crashes.
BUG=517903
Review URL: https://codereview.chromium.org/1286693004
Cr-Commit-Position: refs/heads/master@{#344589}
diff --git a/ui/views/animation/ink_drop_animation.cc b/ui/views/animation/ink_drop_animation.cc
new file mode 100644
index 0000000..b02fcd6
--- /dev/null
+++ b/ui/views/animation/ink_drop_animation.cc
@@ -0,0 +1,340 @@
+// Copyright 2015 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 "ui/views/animation/ink_drop_animation.h"
+
+#include "base/command_line.h"
+#include "ui/base/ui_base_switches.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/compositor/layer_animation_sequence.h"
+#include "ui/compositor/paint_recorder.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/views/animation/ink_drop_delegate.h"
+#include "ui/views/view.h"
+
+namespace {
+
+// Animation constants
+const float kMinimumScale = 0.1f;
+const float kMinimumScaleCenteringOffset = 0.5f - kMinimumScale / 2.0f;
+
+const int kHideAnimationDurationFastMs = 100;
+const int kHideAnimationDurationSlowMs = 1000;
+
+const int kShowInkDropAnimationDurationFastMs = 250;
+const int kShowInkDropAnimationDurationSlowMs = 750;
+
+const int kShowLongPressAnimationDurationFastMs = 250;
+const int kShowLongPressAnimationDurationSlowMs = 2500;
+
+const int kRoundedRectCorners = 5;
+const int kCircleRadius = 30;
+
+const SkColor kInkDropColor = SK_ColorLTGRAY;
+const SkColor kLongPressColor = SkColorSetRGB(182, 182, 182);
+
+// Checks CommandLine switches to determine if the visual feedback should be
+// circular.
+bool UseCircularFeedback() {
+ static bool circular =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ (::switches::kMaterialDesignInkDrop)) !=
+ ::switches::kMaterialDesignInkDropSquare;
+ return circular;
+}
+
+// Checks CommandLine switches to determine if the visual feedback should have
+// a fast animations speed.
+bool UseFastAnimations() {
+ static bool fast =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ (::switches::kMaterialDesignInkDropAnimationSpeed)) !=
+ ::switches::kMaterialDesignInkDropAnimationSpeedSlow;
+ return fast;
+}
+
+} // namespace
+
+namespace views {
+
+// An animation observer that should be set on animations of the provided
+// ui::Layer. Can be used to either start a hide animation, or to trigger one
+// upon completion of the current animation.
+//
+// Sequential animations with PreemptionStrategy::ENQUEUE_NEW_ANIMATION cannot
+// be used as the observed animation can complete before user input is received
+// which determines if the hide animation should run.
+class AppearAnimationObserver : public ui::LayerAnimationObserver {
+ public:
+ // Will automatically start a hide animation of |layer| if |hide| is true.
+ // Otherwise StartHideAnimation() or HideNowIfDoneOrOnceCompleted() must be
+ // called.
+ AppearAnimationObserver(ui::Layer* layer, bool hide);
+ ~AppearAnimationObserver() override;
+
+ // Returns true during both the appearing animation, and the hiding animation.
+ bool IsAnimationActive();
+
+ // Starts a hide animation, preempting any current animations on |layer_|.
+ void StartHideAnimation();
+
+ // Starts a hide animation if |layer_| is no longer animating. Otherwise the
+ // hide animation will be started once the current animation is completed.
+ void HideNowIfDoneOrOnceCompleted();
+
+ // Hides |background_layer| (without animation) after the current animation
+ // completes.
+ void SetBackgroundToHide(ui::Layer* background_layer);
+
+ private:
+ // ui::ImplicitAnimationObserver:
+ void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override;
+ void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
+ void OnLayerAnimationScheduled(
+ ui::LayerAnimationSequence* sequence) override {}
+
+ bool RequiresNotificationWhenAnimatorDestroyed() const override;
+
+ // The ui::Layer being observed, which hide animations will be set on.
+ ui::Layer* layer_;
+
+ // Optional ui::Layer which will be hidden upon the completion of animating
+ // |layer_|
+ ui::Layer* background_layer_;
+
+ // If true the hide animation will immediately be scheduled upon completion of
+ // the observed animation.
+ bool hide_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppearAnimationObserver);
+};
+
+AppearAnimationObserver::AppearAnimationObserver(ui::Layer* layer, bool hide)
+ : layer_(layer), background_layer_(nullptr), hide_(hide) {}
+
+AppearAnimationObserver::~AppearAnimationObserver() {
+ StopObserving();
+}
+
+bool AppearAnimationObserver::IsAnimationActive() {
+ // Initial animation ongoing
+ if (!attached_sequences().empty())
+ return true;
+ // Maintain the animation until told to hide.
+ if (!hide_)
+ return true;
+
+ // Check the state of the triggered hide animation
+ return layer_->GetAnimator()->IsAnimatingProperty(
+ ui::LayerAnimationElement::OPACITY) &&
+ layer_->GetTargetOpacity() == 0.0f &&
+ layer_->GetAnimator()->IsAnimatingProperty(
+ ui::LayerAnimationElement::VISIBILITY) &&
+ !layer_->GetTargetVisibility();
+}
+
+void AppearAnimationObserver::StartHideAnimation() {
+ if (background_layer_)
+ background_layer_->SetVisible(false);
+ if (!layer_->GetTargetVisibility())
+ return;
+
+ ui::ScopedLayerAnimationSettings animation(layer_->GetAnimator());
+ animation.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
+ UseFastAnimations() ? kHideAnimationDurationFastMs
+ : kHideAnimationDurationSlowMs));
+ animation.SetPreemptionStrategy(
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+ layer_->SetOpacity(0.0f);
+ layer_->SetVisible(false);
+}
+
+void AppearAnimationObserver::HideNowIfDoneOrOnceCompleted() {
+ hide_ = true;
+ if (attached_sequences().empty())
+ StartHideAnimation();
+}
+
+void AppearAnimationObserver::SetBackgroundToHide(ui::Layer* background_layer) {
+ background_layer_ = background_layer;
+}
+
+void AppearAnimationObserver::OnLayerAnimationEnded(
+ ui::LayerAnimationSequence* sequence) {
+ if (hide_)
+ StartHideAnimation();
+}
+
+void AppearAnimationObserver::OnLayerAnimationAborted(
+ ui::LayerAnimationSequence* sequence) {
+ if (hide_)
+ StartHideAnimation();
+}
+
+bool AppearAnimationObserver::RequiresNotificationWhenAnimatorDestroyed()
+ const {
+ // Ensures that OnImplicitAnimationsCompleted is called even if the observed
+ // animation is deleted. Allows for setting the proper state on |layer_|.
+ return true;
+}
+
+InkDropAnimation::InkDropAnimation()
+ : root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)),
+ ink_drop_layer_(new ui::Layer()),
+ appear_animation_observer_(nullptr),
+ long_press_layer_(new ui::Layer()),
+ long_press_animation_observer_(nullptr),
+ ink_drop_bounds_(0, 0, 0, 0) {
+ ink_drop_delegate_.reset(new InkDropDelegate(ink_drop_layer_.get(),
+ kInkDropColor, kCircleRadius,
+ kRoundedRectCorners));
+ long_press_delegate_.reset(new InkDropDelegate(long_press_layer_.get(),
+ kLongPressColor, kCircleRadius,
+ kRoundedRectCorners));
+
+ SetupAnimationLayer(long_press_layer_.get(), long_press_delegate_.get());
+ SetupAnimationLayer(ink_drop_layer_.get(), ink_drop_delegate_.get());
+
+ root_layer_->Add(ink_drop_layer_.get());
+ root_layer_->Add(long_press_layer_.get());
+}
+
+InkDropAnimation::~InkDropAnimation() {}
+
+void InkDropAnimation::AnimateToState(InkDropState state) {
+ // TODO(bruthig): Do not transition if we are already in |state| and restrict
+ // any state transition that don't make sense or wouldn't look visually
+ // appealing.
+ switch (state) {
+ case InkDropState::HIDDEN:
+ AnimateHide();
+ break;
+ case InkDropState::ACTION_PENDING:
+ AnimateTapDown();
+ break;
+ case InkDropState::QUICK_ACTION:
+ AnimateTapDown();
+ AnimateHide();
+ break;
+ case InkDropState::SLOW_ACTION:
+ AnimateLongPress();
+ break;
+ case InkDropState::ACTIVATED:
+ AnimateLongPress();
+ break;
+ }
+}
+
+void InkDropAnimation::SetInkDropSize(const gfx::Size& size) {
+ SetInkDropBounds(gfx::Rect(ink_drop_bounds_.origin(), size));
+}
+
+gfx::Rect InkDropAnimation::GetInkDropBounds() const {
+ return ink_drop_bounds_;
+}
+
+void InkDropAnimation::SetInkDropBounds(const gfx::Rect& bounds) {
+ ink_drop_bounds_ = bounds;
+ SetLayerBounds(ink_drop_layer_.get());
+ SetLayerBounds(long_press_layer_.get());
+}
+
+void InkDropAnimation::AnimateTapDown() {
+ if ((appear_animation_observer_ &&
+ appear_animation_observer_->IsAnimationActive()) ||
+ (long_press_animation_observer_ &&
+ long_press_animation_observer_->IsAnimationActive())) {
+ // Only one animation at a time. Subsequent tap downs are ignored until the
+ // current animation completes.
+ return;
+ }
+ appear_animation_observer_.reset(
+ new AppearAnimationObserver(ink_drop_layer_.get(), false));
+ AnimateShow(ink_drop_layer_.get(), appear_animation_observer_.get(),
+ base::TimeDelta::FromMilliseconds(
+ (UseFastAnimations() ? kShowInkDropAnimationDurationFastMs
+ : kShowInkDropAnimationDurationSlowMs)));
+}
+
+void InkDropAnimation::AnimateHide() {
+ if (appear_animation_observer_ &&
+ appear_animation_observer_->IsAnimationActive()) {
+ appear_animation_observer_->HideNowIfDoneOrOnceCompleted();
+ } else if (long_press_animation_observer_) {
+ long_press_animation_observer_->HideNowIfDoneOrOnceCompleted();
+ }
+}
+
+void InkDropAnimation::AnimateLongPress() {
+ // Only one animation at a time. Subsequent long presses are ignored until the
+ // current animation completes.
+ if (long_press_animation_observer_ &&
+ long_press_animation_observer_->IsAnimationActive()) {
+ return;
+ }
+ appear_animation_observer_.reset();
+ long_press_animation_observer_.reset(
+ new AppearAnimationObserver(long_press_layer_.get(), false));
+ long_press_animation_observer_->SetBackgroundToHide(ink_drop_layer_.get());
+ AnimateShow(long_press_layer_.get(), long_press_animation_observer_.get(),
+ base::TimeDelta::FromMilliseconds(
+ UseFastAnimations() ? kShowLongPressAnimationDurationFastMs
+ : kShowLongPressAnimationDurationSlowMs));
+}
+
+void InkDropAnimation::AnimateShow(ui::Layer* layer,
+ AppearAnimationObserver* observer,
+ base::TimeDelta duration) {
+ layer->SetVisible(true);
+ layer->SetOpacity(1.0f);
+
+ float start_x = ink_drop_bounds_.x() +
+ layer->bounds().width() * kMinimumScaleCenteringOffset;
+ float start_y = ink_drop_bounds_.y() +
+ layer->bounds().height() * kMinimumScaleCenteringOffset;
+
+ gfx::Transform initial_transform;
+ initial_transform.Translate(start_x, start_y);
+ initial_transform.Scale(kMinimumScale, kMinimumScale);
+ layer->SetTransform(initial_transform);
+
+ ui::LayerAnimator* animator = layer->GetAnimator();
+ ui::ScopedLayerAnimationSettings animation(animator);
+ animation.SetPreemptionStrategy(
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+
+ gfx::Transform target_transform;
+ target_transform.Translate(ink_drop_bounds_.x(), ink_drop_bounds_.y());
+ ui::LayerAnimationElement* element =
+ ui::LayerAnimationElement::CreateTransformElement(target_transform,
+ duration);
+ ui::LayerAnimationSequence* sequence =
+ new ui::LayerAnimationSequence(element);
+ sequence->AddObserver(observer);
+ animator->StartAnimation(sequence);
+}
+
+void InkDropAnimation::SetLayerBounds(ui::Layer* layer) {
+ bool circle = UseCircularFeedback();
+ gfx::Size size = ink_drop_bounds_.size();
+ float circle_width = circle ? 2.0f * kCircleRadius : size.width();
+ float circle_height = circle ? 2.0f * kCircleRadius : size.height();
+ float circle_x = circle ? (size.width() - circle_width) * 0.5f : 0;
+ float circle_y = circle ? (size.height() - circle_height) * 0.5f : 0;
+ layer->SetBounds(gfx::Rect(circle_x, circle_y, circle_width, circle_height));
+}
+
+void InkDropAnimation::SetupAnimationLayer(ui::Layer* layer,
+ InkDropDelegate* delegate) {
+ layer->SetFillsBoundsOpaquely(false);
+ layer->set_delegate(delegate);
+ layer->SetVisible(false);
+ layer->SetBounds(gfx::Rect());
+ delegate->set_should_render_circle(UseCircularFeedback());
+}
+
+} // namespace views
diff --git a/ui/views/animation/ink_drop_animation.h b/ui/views/animation/ink_drop_animation.h
new file mode 100644
index 0000000..9c50f3a4
--- /dev/null
+++ b/ui/views/animation/ink_drop_animation.h
@@ -0,0 +1,101 @@
+// Copyright 2015 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_INK_DROP_ANIMATION_H_
+#define UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/views/animation/ink_drop_state.h"
+#include "ui/views/views_export.h"
+
+namespace ui {
+class Layer;
+} // namespace ui
+
+namespace views {
+class AppearAnimationObserver;
+class InkDropDelegate;
+
+// An ink drop animation that animates between the different InkDropStates.
+class VIEWS_EXPORT InkDropAnimation {
+ public:
+ InkDropAnimation();
+ ~InkDropAnimation();
+
+ // The root that can be added in to a Layer tree.
+ ui::Layer* root_layer() { return root_layer_.get(); }
+
+ // Animates from the current |state_| to |state|.
+ void AnimateToState(InkDropState state);
+
+ // Sets the size of the ink drop.
+ void SetInkDropSize(const gfx::Size& size);
+
+ // Returns the ink drop bounds.
+ gfx::Rect GetInkDropBounds() const;
+
+ // Sets the bounds for the ink drop. |bounds| are in the coordinate space of
+ // the parent ui::Layer that the ink drop layer is added to.
+ void SetInkDropBounds(const gfx::Rect& bounds);
+
+ private:
+ // Starts the animation of a touch event.
+ void AnimateTapDown();
+
+ // Schedules the hide animation of |ink_drop_layer_| for once its current
+ // animation has completed. If |ink_drop_layer_| is not animating, the hide
+ // animation begins immediately.
+ void AnimateHide();
+
+ // Starts the animation of a long press, and cancels hiding |ink_drop_layer_|
+ // until the long press has completed.
+ void AnimateLongPress();
+
+ // Starts the showing animation on |layer|, with a |duration| in milliseconds.
+ void AnimateShow(ui::Layer* layer,
+ AppearAnimationObserver* observer,
+ base::TimeDelta duration);
+
+ // Sets the bounds for |layer|.
+ void SetLayerBounds(ui::Layer* layer);
+
+ // Initializes |layer|'s properties.
+ void SetupAnimationLayer(ui::Layer* layer, InkDropDelegate* delegate);
+
+ // The root layer that parents the animating layers.
+ scoped_ptr<ui::Layer> root_layer_;
+
+ // The layer used for animating a user touch.
+ scoped_ptr<ui::Layer> ink_drop_layer_;
+
+ // ui::LayerDelegate responsible for painting to |ink_drop_layer_|.
+ scoped_ptr<InkDropDelegate> ink_drop_delegate_;
+
+ // ui::ImplicitAnimationObserver which observes |ink_drop_layer_| and can be
+ // used to automatically trigger a hide animation upon completion.
+ scoped_ptr<AppearAnimationObserver> appear_animation_observer_;
+
+ // The layer used for animating a long press.
+ scoped_ptr<ui::Layer> long_press_layer_;
+
+ // ui::LayerDelegate responsible for painting to |long_press_layer_|.
+ scoped_ptr<InkDropDelegate> long_press_delegate_;
+
+ // ui::ImplicitAnimationObserver which observers |long_press_layer_| and can
+ // be used to automatically trigger a hide animation upon completion.
+ scoped_ptr<AppearAnimationObserver> long_press_animation_observer_;
+
+ // The bounds of the ink drop layers. Defined in the coordinate space of the
+ // parent ui::Layer that the ink drop layers were added to.
+ gfx::Rect ink_drop_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(InkDropAnimation);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_H_
diff --git a/ui/views/animation/ink_drop_animation_controller.h b/ui/views/animation/ink_drop_animation_controller.h
index 40ca7477..57266822 100644
--- a/ui/views/animation/ink_drop_animation_controller.h
+++ b/ui/views/animation/ink_drop_animation_controller.h
@@ -11,6 +11,7 @@
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
+#include "ui/views/animation/ink_drop_state.h"
#include "ui/views/views_export.h"
namespace ui {
@@ -23,24 +24,6 @@
class InkDropHost;
class View;
-// The different states that the ink drop animation can be animated to.
-enum class InkDropState {
- // The ink drop is not visible.
- HIDDEN,
- // The view is being interacted with but the action to be triggered has not
- // yet been determined.
- ACTION_PENDING,
- // The quick action for the view has been triggered. e.g. A tap gesture to
- // click a button.
- QUICK_ACTION,
- // The slow action for the view has been triggered. e.g. A long press to bring
- // up a menu.
- SLOW_ACTION,
- // An active state for a view that is no longer being interacted with. e.g. A
- // pressed button that is showing a menu.
- ACTIVATED
-};
-
// Base class for an ink drop animation which is hosted by an InkDropHost.
class VIEWS_EXPORT InkDropAnimationController {
public:
diff --git a/ui/views/animation/ink_drop_animation_controller_impl.cc b/ui/views/animation/ink_drop_animation_controller_impl.cc
index 2d5a25736..08e3888 100644
--- a/ui/views/animation/ink_drop_animation_controller_impl.cc
+++ b/ui/views/animation/ink_drop_animation_controller_impl.cc
@@ -4,351 +4,40 @@
#include "ui/views/animation/ink_drop_animation_controller_impl.h"
-#include "base/command_line.h"
-#include "ui/base/ui_base_switches.h"
-#include "ui/compositor/layer.h"
-#include "ui/compositor/layer_animation_observer.h"
-#include "ui/compositor/layer_animation_sequence.h"
-#include "ui/compositor/paint_recorder.h"
-#include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/events/event.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/geometry/size.h"
-#include "ui/views/animation/ink_drop_delegate.h"
+#include "ui/views/animation/ink_drop_animation.h"
#include "ui/views/animation/ink_drop_host.h"
-#include "ui/views/view.h"
-
-namespace {
-
-// Animation constants
-const float kMinimumScale = 0.1f;
-const float kMinimumScaleCenteringOffset = 0.5f - kMinimumScale / 2.0f;
-
-const int kHideAnimationDurationFastMs = 100;
-const int kHideAnimationDurationSlowMs = 1000;
-
-const int kShowInkDropAnimationDurationFastMs = 250;
-const int kShowInkDropAnimationDurationSlowMs = 750;
-
-const int kShowLongPressAnimationDurationFastMs = 250;
-const int kShowLongPressAnimationDurationSlowMs = 2500;
-
-const int kRoundedRectCorners = 5;
-const int kCircleRadius = 30;
-
-const SkColor kInkDropColor = SK_ColorLTGRAY;
-const SkColor kLongPressColor = SkColorSetRGB(182, 182, 182);
-
-// Checks CommandLine switches to determine if the visual feedback should be
-// circular.
-bool UseCircularFeedback() {
- static bool circular =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- (::switches::kMaterialDesignInkDrop)) !=
- ::switches::kMaterialDesignInkDropSquare;
- return circular;
-}
-
-// Checks CommandLine switches to determine if the visual feedback should have
-// a fast animations speed.
-bool UseFastAnimations() {
- static bool fast =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- (::switches::kMaterialDesignInkDropAnimationSpeed)) !=
- ::switches::kMaterialDesignInkDropAnimationSpeedSlow;
- return fast;
-}
-
-} // namespace
namespace views {
-// An animation observer that should be set on animations of the provided
-// ui::Layer. Can be used to either start a hide animation, or to trigger one
-// upon completion of the current animation.
-//
-// Sequential animations with PreemptionStrategy::ENQUEUE_NEW_ANIMATION cannot
-// be used as the observed animation can complete before user input is received
-// which determines if the hide animation should run.
-class AppearAnimationObserver : public ui::LayerAnimationObserver {
- public:
- // Will automatically start a hide animation of |layer| if |hide| is true.
- // Otherwise StartHideAnimation() or HideNowIfDoneOrOnceCompleted() must be
- // called.
- AppearAnimationObserver(ui::Layer* layer, bool hide);
- ~AppearAnimationObserver() override;
-
- // Returns true during both the appearing animation, and the hiding animation.
- bool IsAnimationActive();
-
- // Starts a hide animation, preempting any current animations on |layer_|.
- void StartHideAnimation();
-
- // Starts a hide animation if |layer_| is no longer animating. Otherwise the
- // hide animation will be started once the current animation is completed.
- void HideNowIfDoneOrOnceCompleted();
-
- // Hides |background_layer| (without animation) after the current animation
- // completes.
- void SetBackgroundToHide(ui::Layer* background_layer);
-
- private:
- // ui::ImplicitAnimationObserver:
- void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override;
- void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
- void OnLayerAnimationScheduled(
- ui::LayerAnimationSequence* sequence) override {}
-
- bool RequiresNotificationWhenAnimatorDestroyed() const override;
-
- // The ui::Layer being observed, which hide animations will be set on.
- ui::Layer* layer_;
-
- // Optional ui::Layer which will be hidden upon the completion of animating
- // |layer_|
- ui::Layer* background_layer_;
-
- // If true the hide animation will immediately be scheduled upon completion of
- // the observed animation.
- bool hide_;
-
- DISALLOW_COPY_AND_ASSIGN(AppearAnimationObserver);
-};
-
-AppearAnimationObserver::AppearAnimationObserver(ui::Layer* layer, bool hide)
- : layer_(layer), background_layer_(nullptr), hide_(hide) {}
-
-AppearAnimationObserver::~AppearAnimationObserver() {
- StopObserving();
-}
-
-bool AppearAnimationObserver::IsAnimationActive() {
- // Initial animation ongoing
- if (!attached_sequences().empty())
- return true;
- // Maintain the animation until told to hide.
- if (!hide_)
- return true;
-
- // Check the state of the triggered hide animation
- return layer_->GetAnimator()->IsAnimatingProperty(
- ui::LayerAnimationElement::OPACITY) &&
- layer_->GetTargetOpacity() == 0.0f &&
- layer_->GetAnimator()->IsAnimatingProperty(
- ui::LayerAnimationElement::VISIBILITY) &&
- !layer_->GetTargetVisibility();
-}
-
-void AppearAnimationObserver::StartHideAnimation() {
- if (background_layer_)
- background_layer_->SetVisible(false);
- if (!layer_->GetTargetVisibility())
- return;
-
- ui::ScopedLayerAnimationSettings animation(layer_->GetAnimator());
- animation.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
- UseFastAnimations() ? kHideAnimationDurationFastMs
- : kHideAnimationDurationSlowMs));
- animation.SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
- layer_->SetOpacity(0.0f);
- layer_->SetVisible(false);
-}
-
-void AppearAnimationObserver::HideNowIfDoneOrOnceCompleted() {
- hide_ = true;
- if (attached_sequences().empty())
- StartHideAnimation();
-}
-
-void AppearAnimationObserver::SetBackgroundToHide(ui::Layer* background_layer) {
- background_layer_ = background_layer;
-}
-
-void AppearAnimationObserver::OnLayerAnimationEnded(
- ui::LayerAnimationSequence* sequence) {
- if (hide_)
- StartHideAnimation();
-}
-
-void AppearAnimationObserver::OnLayerAnimationAborted(
- ui::LayerAnimationSequence* sequence) {
- if (hide_)
- StartHideAnimation();
-}
-
-bool AppearAnimationObserver::RequiresNotificationWhenAnimatorDestroyed()
- const {
- // Ensures that OnImplicitAnimationsCompleted is called even if the observed
- // animation is deleted. Allows for setting the proper state on |layer_|.
- return true;
-}
-
InkDropAnimationControllerImpl::InkDropAnimationControllerImpl(
InkDropHost* ink_drop_host)
: ink_drop_host_(ink_drop_host),
- root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)),
- ink_drop_layer_(new ui::Layer()),
- appear_animation_observer_(nullptr),
- long_press_layer_(new ui::Layer()),
- long_press_animation_observer_(nullptr),
- ink_drop_bounds_(0, 0, 0, 0) {
- ink_drop_delegate_.reset(new InkDropDelegate(ink_drop_layer_.get(),
- kInkDropColor, kCircleRadius,
- kRoundedRectCorners));
- long_press_delegate_.reset(new InkDropDelegate(long_press_layer_.get(),
- kLongPressColor, kCircleRadius,
- kRoundedRectCorners));
-
- SetupAnimationLayer(long_press_layer_.get(), long_press_delegate_.get());
- SetupAnimationLayer(ink_drop_layer_.get(), ink_drop_delegate_.get());
-
- root_layer_->Add(ink_drop_layer_.get());
- root_layer_->Add(long_press_layer_.get());
-
+ ink_drop_animation_(new InkDropAnimation()) {
// TODO(bruthig): Change this to only be called before the ink drop becomes
// active. See www.crbug.com/522175.
- ink_drop_host_->AddInkDropLayer(root_layer_.get());
+ ink_drop_host_->AddInkDropLayer(ink_drop_animation_->root_layer());
}
InkDropAnimationControllerImpl::~InkDropAnimationControllerImpl() {
// TODO(bruthig): Change this to be called when the ink drop becomes hidden.
// See www.crbug.com/522175.
- ink_drop_host_->RemoveInkDropLayer(root_layer_.get());
+ ink_drop_host_->RemoveInkDropLayer(ink_drop_animation_->root_layer());
}
void InkDropAnimationControllerImpl::AnimateToState(InkDropState state) {
- // TODO(bruthig): Do not transition if we are already in |state| and restrict
- // any state transition that don't make sense or wouldn't look visually
- // appealing.
- switch (state) {
- case InkDropState::HIDDEN:
- AnimateHide();
- break;
- case InkDropState::ACTION_PENDING:
- AnimateTapDown();
- break;
- case InkDropState::QUICK_ACTION:
- AnimateTapDown();
- AnimateHide();
- break;
- case InkDropState::SLOW_ACTION:
- AnimateLongPress();
- break;
- case InkDropState::ACTIVATED:
- AnimateLongPress();
- break;
- }
+ ink_drop_animation_->AnimateToState(state);
}
void InkDropAnimationControllerImpl::SetInkDropSize(const gfx::Size& size) {
- SetInkDropBounds(gfx::Rect(ink_drop_bounds_.origin(), size));
+ ink_drop_animation_->SetInkDropSize(size);
}
gfx::Rect InkDropAnimationControllerImpl::GetInkDropBounds() const {
- return ink_drop_bounds_;
+ return ink_drop_animation_->GetInkDropBounds();
}
void InkDropAnimationControllerImpl::SetInkDropBounds(const gfx::Rect& bounds) {
- ink_drop_bounds_ = bounds;
- SetLayerBounds(ink_drop_layer_.get());
- SetLayerBounds(long_press_layer_.get());
-}
-
-void InkDropAnimationControllerImpl::AnimateTapDown() {
- if ((appear_animation_observer_ &&
- appear_animation_observer_->IsAnimationActive()) ||
- (long_press_animation_observer_ &&
- long_press_animation_observer_->IsAnimationActive())) {
- // Only one animation at a time. Subsequent tap downs are ignored until the
- // current animation completes.
- return;
- }
- appear_animation_observer_.reset(
- new AppearAnimationObserver(ink_drop_layer_.get(), false));
- AnimateShow(ink_drop_layer_.get(), appear_animation_observer_.get(),
- base::TimeDelta::FromMilliseconds(
- (UseFastAnimations() ? kShowInkDropAnimationDurationFastMs
- : kShowInkDropAnimationDurationSlowMs)));
-}
-
-void InkDropAnimationControllerImpl::AnimateHide() {
- if (appear_animation_observer_ &&
- appear_animation_observer_->IsAnimationActive()) {
- appear_animation_observer_->HideNowIfDoneOrOnceCompleted();
- } else if (long_press_animation_observer_) {
- long_press_animation_observer_->HideNowIfDoneOrOnceCompleted();
- }
-}
-
-void InkDropAnimationControllerImpl::AnimateLongPress() {
- // Only one animation at a time. Subsequent long presses are ignored until the
- // current animation completes.
- if (long_press_animation_observer_ &&
- long_press_animation_observer_->IsAnimationActive()) {
- return;
- }
- appear_animation_observer_.reset();
- long_press_animation_observer_.reset(
- new AppearAnimationObserver(long_press_layer_.get(), false));
- long_press_animation_observer_->SetBackgroundToHide(ink_drop_layer_.get());
- AnimateShow(long_press_layer_.get(), long_press_animation_observer_.get(),
- base::TimeDelta::FromMilliseconds(
- UseFastAnimations() ? kShowLongPressAnimationDurationFastMs
- : kShowLongPressAnimationDurationSlowMs));
-}
-
-void InkDropAnimationControllerImpl::AnimateShow(
- ui::Layer* layer,
- AppearAnimationObserver* observer,
- base::TimeDelta duration) {
- layer->SetVisible(true);
- layer->SetOpacity(1.0f);
-
- float start_x = ink_drop_bounds_.x() +
- layer->bounds().width() * kMinimumScaleCenteringOffset;
- float start_y = ink_drop_bounds_.y() +
- layer->bounds().height() * kMinimumScaleCenteringOffset;
-
- gfx::Transform initial_transform;
- initial_transform.Translate(start_x, start_y);
- initial_transform.Scale(kMinimumScale, kMinimumScale);
- layer->SetTransform(initial_transform);
-
- ui::LayerAnimator* animator = layer->GetAnimator();
- ui::ScopedLayerAnimationSettings animation(animator);
- animation.SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-
- gfx::Transform target_transform;
- target_transform.Translate(ink_drop_bounds_.x(), ink_drop_bounds_.y());
- ui::LayerAnimationElement* element =
- ui::LayerAnimationElement::CreateTransformElement(target_transform,
- duration);
- ui::LayerAnimationSequence* sequence =
- new ui::LayerAnimationSequence(element);
- sequence->AddObserver(observer);
- animator->StartAnimation(sequence);
-}
-
-void InkDropAnimationControllerImpl::SetLayerBounds(ui::Layer* layer) {
- bool circle = UseCircularFeedback();
- gfx::Size size = ink_drop_bounds_.size();
- float circle_width = circle ? 2.0f * kCircleRadius : size.width();
- float circle_height = circle ? 2.0f * kCircleRadius : size.height();
- float circle_x = circle ? (size.width() - circle_width) * 0.5f : 0;
- float circle_y = circle ? (size.height() - circle_height) * 0.5f : 0;
- layer->SetBounds(gfx::Rect(circle_x, circle_y, circle_width, circle_height));
-}
-
-void InkDropAnimationControllerImpl::SetupAnimationLayer(
- ui::Layer* layer,
- InkDropDelegate* delegate) {
- layer->SetFillsBoundsOpaquely(false);
- layer->set_delegate(delegate);
- layer->SetVisible(false);
- layer->SetBounds(gfx::Rect());
- delegate->set_should_render_circle(UseCircularFeedback());
+ ink_drop_animation_->SetInkDropBounds(bounds);
}
} // namespace views
diff --git a/ui/views/animation/ink_drop_animation_controller_impl.h b/ui/views/animation/ink_drop_animation_controller_impl.h
index c41f2612..b12a738 100644
--- a/ui/views/animation/ink_drop_animation_controller_impl.h
+++ b/ui/views/animation/ink_drop_animation_controller_impl.h
@@ -6,22 +6,14 @@
#define UI_VIEWS_ANIMATION_INK_DROP_ANIMATION_CONTROLLER_IMPL_H_
#include "base/memory/scoped_ptr.h"
-#include "base/time/time.h"
-#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/animation/ink_drop_animation_controller.h"
#include "ui/views/views_export.h"
-namespace ui {
-class Layer;
-}
-
namespace views {
-class AppearAnimationObserver;
-class InkDropDelegate;
+class InkDropAnimation;
class InkDropHost;
-class View;
// Controls an ink drop animation which is hosted by an InkDropHost.
class VIEWS_EXPORT InkDropAnimationControllerImpl
@@ -39,57 +31,14 @@
void SetInkDropBounds(const gfx::Rect& bounds) override;
private:
- // Starts the animation of a touch event.
- void AnimateTapDown();
-
- // Schedules the hide animation of |ink_drop_layer_| for once its current
- // animation has completed. If |ink_drop_layer_| is not animating, the hide
- // animation begins immediately.
- void AnimateHide();
-
- // Starts the animation of a long press, and cancels hiding |ink_drop_layer_|
- // until the long press has completed.
- void AnimateLongPress();
-
- // Starts the showing animation on |layer|, with a |duration| in milliseconds.
- void AnimateShow(ui::Layer* layer,
- AppearAnimationObserver* observer,
- base::TimeDelta duration);
-
- // Sets the bounds for |layer|.
- void SetLayerBounds(ui::Layer* layer);
-
- // Initializes |layer|'s properties.
- void SetupAnimationLayer(ui::Layer* layer, InkDropDelegate* delegate);
-
+ // The host of the ink drop.
InkDropHost* ink_drop_host_;
- // The root layer that parents the animating layers.
- scoped_ptr<ui::Layer> root_layer_;
-
- // The layer used for animating a user touch.
- scoped_ptr<ui::Layer> ink_drop_layer_;
-
- // ui::LayerDelegate responsible for painting to |ink_drop_layer_|.
- scoped_ptr<InkDropDelegate> ink_drop_delegate_;
-
- // ui::ImplicitAnimationObserver which observes |ink_drop_layer_| and can be
- // used to automatically trigger a hide animation upon completion.
- scoped_ptr<AppearAnimationObserver> appear_animation_observer_;
-
- // The layer used for animating a long press.
- scoped_ptr<ui::Layer> long_press_layer_;
-
- // ui::LayerDelegate responsible for painting to |long_press_layer_|.
- scoped_ptr<InkDropDelegate> long_press_delegate_;
-
- // ui::ImplicitAnimationObserver which observers |long_press_layer_| and can
- // be used to automatically trigger a hide animation upon completion.
- scoped_ptr<AppearAnimationObserver> long_press_animation_observer_;
-
- // The bounds of the ink drop layers. Defined in the coordinate space of the
- // parent ui::Layer that the ink drop layers were added to.
- gfx::Rect ink_drop_bounds_;
+ // TODO(bruthig): It will be expensive to maintain InkDropAnimation instances
+ // when they are not actually being used. Consider creating InkDropAnimations
+ // on an as-needed basis and if construction is also expensive then consider
+ // creating an InkDropAnimationPool. See www.crbug.com/522175.
+ scoped_ptr<InkDropAnimation> ink_drop_animation_;
DISALLOW_COPY_AND_ASSIGN(InkDropAnimationControllerImpl);
};
diff --git a/ui/views/animation/ink_drop_state.h b/ui/views/animation/ink_drop_state.h
new file mode 100644
index 0000000..d88f022
--- /dev/null
+++ b/ui/views/animation/ink_drop_state.h
@@ -0,0 +1,30 @@
+// Copyright 2015 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_INK_DROP_STATE_H_
+#define UI_VIEWS_ANIMATION_INK_DROP_STATE_H_
+
+namespace views {
+
+// The different states that the ink drop animation can be animated to.
+enum class InkDropState {
+ // The ink drop is not visible.
+ HIDDEN,
+ // The view is being interacted with but the action to be triggered has not
+ // yet been determined.
+ ACTION_PENDING,
+ // The quick action for the view has been triggered. e.g. A tap gesture to
+ // click a button.
+ QUICK_ACTION,
+ // The slow action for the view has been triggered. e.g. A long press to bring
+ // up a menu.
+ SLOW_ACTION,
+ // An active state for a view that is no longer being interacted with. e.g. A
+ // pressed button that is showing a menu.
+ ACTIVATED
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_ANIMATION_INK_DROP_STATE_H_
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index 303dd228..99e0b3f 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -14,14 +14,17 @@
'accessible_pane_view.h',
'animation/bounds_animator.cc',
'animation/bounds_animator.h',
- 'animation/ink_drop_animation_controller_impl.cc',
- 'animation/ink_drop_animation_controller_impl.h',
+ 'animation/ink_drop_animation.cc',
+ 'animation/ink_drop_animation.h',
'animation/ink_drop_animation_controller.h',
'animation/ink_drop_animation_controller_factory.cc',
'animation/ink_drop_animation_controller_factory.h',
- 'animation/ink_drop_host.h',
+ 'animation/ink_drop_animation_controller_impl.cc',
+ 'animation/ink_drop_animation_controller_impl.h',
'animation/ink_drop_delegate.cc',
'animation/ink_drop_delegate.h',
+ 'animation/ink_drop_host.h',
+ 'animation/ink_drop_state.h',
'animation/scroll_animator.cc',
'animation/scroll_animator.h',
'background.cc',