Expand the Material Design hover as it fades out when a ripple is triggered.

BUG=582253
TEST=manual

Review URL: https://codereview.chromium.org/1890243002

Cr-Commit-Position: refs/heads/master@{#389377}
(cherry picked from commit caf80a39907860be5a649a81e5418721faac2f98)

Review URL: https://codereview.chromium.org/1924663007 .

Cr-Commit-Position: refs/branch-heads/2704@{#285}
Cr-Branched-From: 6e53600def8f60d8c632fadc70d7c1939ccea347-refs/heads/master@{#386251}
diff --git a/ui/views/animation/ink_drop_animation_controller_impl.cc b/ui/views/animation/ink_drop_animation_controller_impl.cc
index 963440d..d28a280 100644
--- a/ui/views/animation/ink_drop_animation_controller_impl.cc
+++ b/ui/views/animation/ink_drop_animation_controller_impl.cc
@@ -29,7 +29,7 @@
 
 // The duration, in milliseconds, of the hover state fade out animation when it
 // is triggered by an ink drop ripple animation starting.
-const int kHoverFadeOutBeforeAnimationDurationInMs = 300;
+const int kHoverFadeOutBeforeAnimationDurationInMs = 120;
 
 // The amount of time in milliseconds that |hover_| should delay after a ripple
 // animation before fading in.
@@ -85,7 +85,8 @@
 
   if (ink_drop_state != views::InkDropState::HIDDEN) {
     SetHoveredInternal(false, base::TimeDelta::FromMilliseconds(
-                                  kHoverFadeOutBeforeAnimationDurationInMs));
+                                  kHoverFadeOutBeforeAnimationDurationInMs),
+                       true);
   }
 
   ink_drop_animation_->AnimateToState(ink_drop_state);
@@ -96,7 +97,7 @@
   if (!ink_drop_animation_)
     CreateInkDropAnimation();
 
-  SetHoveredInternal(false, base::TimeDelta());
+  SetHoveredInternal(false, base::TimeDelta(), false);
 
   ink_drop_animation_->SnapToActivated();
 }
@@ -107,7 +108,8 @@
                      is_hovered ? base::TimeDelta::FromMilliseconds(
                                       kHoverFadeInFromUserInputDurationInMs)
                                 : base::TimeDelta::FromMilliseconds(
-                                      kHoverFadeOutFromUserInputDurationInMs));
+                                      kHoverFadeOutFromUserInputDurationInMs),
+                     false);
 }
 
 void InkDropAnimationControllerImpl::DestroyHiddenTargetedAnimations() {
@@ -175,7 +177,8 @@
 
 void InkDropAnimationControllerImpl::SetHoveredInternal(
     bool is_hovered,
-    base::TimeDelta animation_duration) {
+    base::TimeDelta animation_duration,
+    bool explode) {
   StopHoverAfterAnimationTimer();
 
   if (IsHoverFadingInOrVisible() == is_hovered)
@@ -186,7 +189,7 @@
     if (hover_ && !IsVisible())
       hover_->FadeIn(animation_duration);
   } else {
-    hover_->FadeOut(animation_duration);
+    hover_->FadeOut(animation_duration, explode);
   }
 }
 
@@ -210,7 +213,8 @@
 
 void InkDropAnimationControllerImpl::HoverAfterAnimationTimerFired() {
   SetHoveredInternal(true, base::TimeDelta::FromMilliseconds(
-                               kHoverFadeInAfterAnimationDurationInMs));
+                               kHoverFadeInAfterAnimationDurationInMs),
+                     true);
   hover_after_animation_timer_.reset();
 }
 
diff --git a/ui/views/animation/ink_drop_animation_controller_impl.h b/ui/views/animation/ink_drop_animation_controller_impl.h
index 9324b0bf..a8d786d 100644
--- a/ui/views/animation/ink_drop_animation_controller_impl.h
+++ b/ui/views/animation/ink_drop_animation_controller_impl.h
@@ -73,8 +73,11 @@
 
   // Enables or disables the hover state based on |is_hovered| and if an
   // animation is triggered it will be scheduled to have the given
-  // |animation_duration|.
-  void SetHoveredInternal(bool is_hovered, base::TimeDelta animation_duration);
+  // |animation_duration|. If |explode| is true the hover will expand as it
+  // fades out. |explode| is ignored when |is_hovered| is true.
+  void SetHoveredInternal(bool is_hovered,
+                          base::TimeDelta animation_duration,
+                          bool explode);
 
   // Starts the |hover_after_animation_timer_| timer. This will stop the current
   // |hover_after_animation_timer_| instance if it exists.
diff --git a/ui/views/animation/ink_drop_host_view.cc b/ui/views/animation/ink_drop_host_view.cc
index ab55881..317645e 100644
--- a/ui/views/animation/ink_drop_host_view.cc
+++ b/ui/views/animation/ink_drop_host_view.cc
@@ -5,6 +5,7 @@
 #include "ui/views/animation/ink_drop_host_view.h"
 
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/geometry/size_conversions.h"
 #include "ui/views/animation/ink_drop_hover.h"
 #include "ui/views/animation/square_ink_drop_animation.h"
 
@@ -14,6 +15,17 @@
 const int kInkDropSize = 24;
 const int kInkDropLargeCornerRadius = 4;
 
+// The scale factor to compute the large ink drop size.
+const float kLargeInkDropScale = 1.333f;
+
+namespace {
+
+gfx::Size CalculateLargeInkDropSize(const gfx::Size small_size) {
+  return gfx::ScaleToCeiledSize(gfx::Size(small_size), kLargeInkDropScale);
+}
+
+}  // namespace
+
 // static
 const int InkDropHostView::kInkDropSmallCornerRadius = 2;
 
@@ -35,12 +47,10 @@
 }
 
 scoped_ptr<InkDropAnimation> InkDropHostView::CreateInkDropAnimation() const {
-  gfx::Size large_drop(ink_drop_size_.width() * 4 / 3,
-                       ink_drop_size_.height() * 4 / 3);
-
   scoped_ptr<InkDropAnimation> animation(new SquareInkDropAnimation(
-      large_drop, kInkDropLargeCornerRadius, ink_drop_size_,
-      kInkDropSmallCornerRadius, GetInkDropCenter(), GetInkDropBaseColor()));
+      CalculateLargeInkDropSize(ink_drop_size_), kInkDropLargeCornerRadius,
+      ink_drop_size_, kInkDropSmallCornerRadius, GetInkDropCenter(),
+      GetInkDropBaseColor()));
   return animation;
 }
 
@@ -48,6 +58,7 @@
   scoped_ptr<InkDropHover> hover(
       new InkDropHover(ink_drop_size_, kInkDropSmallCornerRadius,
                        GetInkDropCenter(), GetInkDropBaseColor()));
+  hover->set_explode_size(CalculateLargeInkDropSize(ink_drop_size_));
   return hover;
 }
 
diff --git a/ui/views/animation/ink_drop_hover.cc b/ui/views/animation/ink_drop_hover.cc
index be440c9..9bb7b02 100644
--- a/ui/views/animation/ink_drop_hover.cc
+++ b/ui/views/animation/ink_drop_hover.cc
@@ -27,7 +27,10 @@
                            int corner_radius,
                            const gfx::Point& center_point,
                            SkColor color)
-    : last_animation_initiated_was_fade_in_(false),
+    : size_(size),
+      explode_size_(size),
+      center_point_(center_point),
+      last_animation_initiated_was_fade_in_(false),
       layer_delegate_(
           new RoundedRectangleLayerDelegate(color, size, corner_radius)),
       layer_(new ui::Layer()) {
@@ -38,11 +41,6 @@
   layer_->SetOpacity(kHoverVisibleOpacity);
   layer_->SetMasksToBounds(false);
   layer_->set_name("InkDropHover:layer");
-
-  gfx::Transform transform;
-  transform.Translate(center_point.x() - layer_->bounds().CenterPoint().x(),
-                      center_point.y() - layer_->bounds().CenterPoint().y());
-  layer_->SetTransform(transform);
 }
 
 InkDropHover::~InkDropHover() {}
@@ -54,17 +52,21 @@
 void InkDropHover::FadeIn(const base::TimeDelta& duration) {
   layer_->SetOpacity(kHiddenOpacity);
   layer_->SetVisible(true);
-  AnimateFade(FADE_IN, duration);
+  AnimateFade(FADE_IN, duration, size_, size_);
 }
 
-void InkDropHover::FadeOut(const base::TimeDelta& duration) {
-  AnimateFade(FADE_OUT, duration);
+void InkDropHover::FadeOut(const base::TimeDelta& duration, bool explode) {
+  AnimateFade(FADE_OUT, duration, size_, explode ? explode_size_ : size_);
 }
 
 void InkDropHover::AnimateFade(HoverAnimationType animation_type,
-                               const base::TimeDelta& duration) {
+                               const base::TimeDelta& duration,
+                               const gfx::Size& initial_size,
+                               const gfx::Size& target_size) {
   last_animation_initiated_was_fade_in_ = animation_type == FADE_IN;
 
+  layer_->SetTransform(CalculateTransform(initial_size));
+
   // The |animation_observer| will be destroyed when the
   // AnimationStartedCallback() returns true.
   ui::CallbackLayerAnimationObserver* animation_observer =
@@ -77,19 +79,38 @@
   animation.SetTweenType(gfx::Tween::EASE_IN_OUT);
   animation.SetPreemptionStrategy(
       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-  ui::LayerAnimationElement* animation_element =
+
+  ui::LayerAnimationElement* opacity_element =
       ui::LayerAnimationElement::CreateOpacityElement(
           animation_type == FADE_IN ? kHoverVisibleOpacity : kHiddenOpacity,
           duration);
-  ui::LayerAnimationSequence* animation_sequence =
-      new ui::LayerAnimationSequence(animation_element);
-  animation_sequence->AddObserver(animation_observer);
+  ui::LayerAnimationSequence* opacity_sequence =
+      new ui::LayerAnimationSequence(opacity_element);
+  opacity_sequence->AddObserver(animation_observer);
+  animator->StartAnimation(opacity_sequence);
 
-  animator->StartAnimation(animation_sequence);
+  if (initial_size != target_size) {
+    ui::LayerAnimationElement* transform_element =
+        ui::LayerAnimationElement::CreateTransformElement(
+            CalculateTransform(target_size), duration);
+    ui::LayerAnimationSequence* transform_sequence =
+        new ui::LayerAnimationSequence(transform_element);
+    transform_sequence->AddObserver(animation_observer);
+    animator->StartAnimation(transform_sequence);
+  }
 
   animation_observer->SetActive();
 }
 
+gfx::Transform InkDropHover::CalculateTransform(const gfx::Size& size) const {
+  gfx::Transform transform;
+  transform.Translate(center_point_.x(), center_point_.y());
+  transform.Scale(size.width() / size_.width(), size.height() / size_.height());
+  transform.Translate(-layer_delegate_->GetCenterPoint().x(),
+                      -layer_delegate_->GetCenterPoint().y());
+  return transform;
+}
+
 bool InkDropHover::AnimationEndedCallback(
     HoverAnimationType animation_type,
     const ui::CallbackLayerAnimationObserver& observer) {
diff --git a/ui/views/animation/ink_drop_hover.h b/ui/views/animation/ink_drop_hover.h
index 00ed198..1df74c2 100644
--- a/ui/views/animation/ink_drop_hover.h
+++ b/ui/views/animation/ink_drop_hover.h
@@ -10,7 +10,9 @@
 #include "base/time/time.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/size.h"
+#include "ui/gfx/transform.h"
 #include "ui/views/views_export.h"
 
 namespace ui {
@@ -31,6 +33,8 @@
                SkColor color);
   ~InkDropHover();
 
+  void set_explode_size(const gfx::Size& size) { explode_size_ = size; }
+
   // Returns true if the hover animation is either in the process of fading
   // in or is fully visible.
   bool IsFadingInOrVisible() const;
@@ -38,8 +42,9 @@
   // Fades in the hover visual over the given |duration|.
   void FadeIn(const base::TimeDelta& duration);
 
-  // Fades out the hover visual over the given |duration|.
-  void FadeOut(const base::TimeDelta& duration);
+  // Fades out the hover visual over the given |duration|. If |explode| is true
+  // then the hover will animate a size increase in addition to the fade out.
+  void FadeOut(const base::TimeDelta& duration, bool explode);
 
   // The root Layer that can be added in to a Layer tree.
   ui::Layer* layer() { return layer_.get(); }
@@ -47,16 +52,32 @@
  private:
   enum HoverAnimationType { FADE_IN, FADE_OUT };
 
-  // Animates a fade in/out as specified by |animation_type| over the given
+  // Animates a fade in/out as specified by |animation_type| combined with a
+  // transformation from the |initial_size| to the |target_size| over the given
   // |duration|.
   void AnimateFade(HoverAnimationType animation_type,
-                   const base::TimeDelta& duration);
+                   const base::TimeDelta& duration,
+                   const gfx::Size& initial_size,
+                   const gfx::Size& target_size);
+
+  // Calculates the Transform to apply to |layer_| for the given |size|.
+  gfx::Transform CalculateTransform(const gfx::Size& size) const;
 
   // The callback that will be invoked when a fade in/out animation is complete.
   bool AnimationEndedCallback(
       HoverAnimationType animation_type,
       const ui::CallbackLayerAnimationObserver& observer);
 
+  // The size of the hover shape when fully faded in.
+  gfx::Size size_;
+
+  // The target size of the hover shape when it expands during a fade out
+  // animation.
+  gfx::Size explode_size_;
+
+  // The center point of the hover shape in the parent Layer's coordinate space.
+  gfx::PointF center_point_;
+
   // True if the last animation to be initiated was a FADE_IN, and false
   // otherwise.
   bool last_animation_initiated_was_fade_in_;
diff --git a/ui/views/animation/ink_drop_hover_unittest.cc b/ui/views/animation/ink_drop_hover_unittest.cc
index 5657569..7eded56 100644
--- a/ui/views/animation/ink_drop_hover_unittest.cc
+++ b/ui/views/animation/ink_drop_hover_unittest.cc
@@ -51,7 +51,8 @@
   ink_drop_hover->FadeIn(base::TimeDelta::FromMilliseconds(0));
   EXPECT_TRUE(ink_drop_hover->IsFadingInOrVisible());
 
-  ink_drop_hover->FadeOut(base::TimeDelta::FromMilliseconds(0));
+  ink_drop_hover->FadeOut(base::TimeDelta::FromMilliseconds(0),
+                          false /* explode */);
   EXPECT_FALSE(ink_drop_hover->IsFadingInOrVisible());
 }