| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright 2015 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 |  | 
| 5 #include "ui/views/animation/ink_drop_animation_controller.h" |  | 
| 6 |  | 
| 7 #include "base/command_line.h" |  | 
| 8 #include "ui/base/ui_base_switches.h" |  | 
| 9 #include "ui/compositor/layer.h" |  | 
| 10 #include "ui/compositor/layer_animation_observer.h" |  | 
| 11 #include "ui/compositor/layer_animation_sequence.h" |  | 
| 12 #include "ui/compositor/paint_recorder.h" |  | 
| 13 #include "ui/compositor/scoped_layer_animation_settings.h" |  | 
| 14 #include "ui/events/event.h" |  | 
| 15 #include "ui/gfx/canvas.h" |  | 
| 16 #include "ui/views/view.h" |  | 
| 17 |  | 
| 18 namespace { |  | 
| 19 |  | 
| 20 // Animation constants |  | 
| 21 const float kMinimumScale = 0.1f; |  | 
| 22 const float kMinimumScaleCenteringOffset = 0.5f - kMinimumScale / 2.0f; |  | 
| 23 |  | 
| 24 const int kHideAnimationDurationFastMs = 100; |  | 
| 25 const int kHideAnimationDurationSlowMs = 1000; |  | 
| 26 |  | 
| 27 const int kShowInkDropAnimationDurationFastMs = 250; |  | 
| 28 const int kShowInkDropAnimationDurationSlowMs = 750; |  | 
| 29 |  | 
| 30 const int kShowLongPressAnimationDurationFastMs = 250; |  | 
| 31 const int kShowLongPressAnimationDurationSlowMs = 2500; |  | 
| 32 |  | 
| 33 const int kRoundedRectCorners = 5; |  | 
| 34 const int kCircleRadius = 30; |  | 
| 35 |  | 
| 36 const SkColor kInkDropColor = SK_ColorLTGRAY; |  | 
| 37 const SkColor kLongPressColor = SkColorSetRGB(182, 182, 182); |  | 
| 38 |  | 
| 39 // Checks CommandLine switches to determine if the visual feedback should be |  | 
| 40 // circular. |  | 
| 41 bool UseCircularFeedback() { |  | 
| 42   static bool circular = |  | 
| 43       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |  | 
| 44           (::switches::kMaterialDesignInkDrop)) != |  | 
| 45       ::switches::kMaterialDesignInkDropSquare; |  | 
| 46   return circular; |  | 
| 47 } |  | 
| 48 |  | 
| 49 // Checks CommandLine switches to determine if the visual feedback should have |  | 
| 50 // a fast animations speed. |  | 
| 51 bool UseFastAnimations() { |  | 
| 52   static bool fast = |  | 
| 53       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |  | 
| 54           (::switches::kMaterialDesignInkDropAnimationSpeed)) != |  | 
| 55       ::switches::kMaterialDesignInkDropAnimationSpeedSlow; |  | 
| 56   return fast; |  | 
| 57 } |  | 
| 58 |  | 
| 59 }  // namespace |  | 
| 60 |  | 
| 61 namespace views { |  | 
| 62 |  | 
| 63 // An animation observer that should be set on animations of the provided |  | 
| 64 // ui::Layer. Can be used to either start a hide animation, or to trigger one |  | 
| 65 // upon completion of the current animation. |  | 
| 66 // |  | 
| 67 // Sequential animations with PreemptionStrategy::ENQUEUE_NEW_ANIMATION cannot |  | 
| 68 // be used as the observed animation can complete before user input is received |  | 
| 69 // which determines if the hide animation should run. |  | 
| 70 class AppearAnimationObserver : public ui::LayerAnimationObserver { |  | 
| 71  public: |  | 
| 72   // Will automatically start a hide animation of |layer| if |hide| is true. |  | 
| 73   // Otherwise StartHideAnimation() or HideNowIfDoneOrOnceCompleted() must be |  | 
| 74   // called. |  | 
| 75   AppearAnimationObserver(ui::Layer* layer, bool hide); |  | 
| 76   ~AppearAnimationObserver() override; |  | 
| 77 |  | 
| 78   // Returns true during both the appearing animation, and the hiding animation. |  | 
| 79   bool IsAnimationActive(); |  | 
| 80 |  | 
| 81   // Starts a hide animation, preempting any current animations on |layer_|. |  | 
| 82   void StartHideAnimation(); |  | 
| 83 |  | 
| 84   // Starts a hide animation if |layer_| is no longer animating. Otherwise the |  | 
| 85   // hide animation will be started once the current animation is completed. |  | 
| 86   void HideNowIfDoneOrOnceCompleted(); |  | 
| 87 |  | 
| 88   // Hides |background_layer| (without animation) after the current animation |  | 
| 89   // completes. |  | 
| 90   void SetBackgroundToHide(ui::Layer* background_layer); |  | 
| 91 |  | 
| 92  private: |  | 
| 93   // ui::ImplicitAnimationObserver: |  | 
| 94   void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override; |  | 
| 95   void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override; |  | 
| 96   void OnLayerAnimationScheduled( |  | 
| 97       ui::LayerAnimationSequence* sequence) override {} |  | 
| 98 |  | 
| 99   bool RequiresNotificationWhenAnimatorDestroyed() const override; |  | 
| 100 |  | 
| 101   // The ui::Layer being observed, which hide animations will be set on. |  | 
| 102   ui::Layer* layer_; |  | 
| 103 |  | 
| 104   // Optional ui::Layer which will be hidden upon the completion of animating |  | 
| 105   // |layer_| |  | 
| 106   ui::Layer* background_layer_; |  | 
| 107 |  | 
| 108   // If true the hide animation will immediately be scheduled upon completion of |  | 
| 109   // the observed animation. |  | 
| 110   bool hide_; |  | 
| 111 |  | 
| 112   DISALLOW_COPY_AND_ASSIGN(AppearAnimationObserver); |  | 
| 113 }; |  | 
| 114 |  | 
| 115 AppearAnimationObserver::AppearAnimationObserver(ui::Layer* layer, bool hide) |  | 
| 116     : layer_(layer), background_layer_(nullptr), hide_(hide) { |  | 
| 117 } |  | 
| 118 |  | 
| 119 AppearAnimationObserver::~AppearAnimationObserver() { |  | 
| 120   StopObserving(); |  | 
| 121 } |  | 
| 122 |  | 
| 123 bool AppearAnimationObserver::IsAnimationActive() { |  | 
| 124   // Initial animation ongoing |  | 
| 125   if (!attached_sequences().empty()) |  | 
| 126     return true; |  | 
| 127   // Maintain the animation until told to hide. |  | 
| 128   if (!hide_) |  | 
| 129     return true; |  | 
| 130 |  | 
| 131   // Check the state of the triggered hide animation |  | 
| 132   return layer_->GetAnimator()->IsAnimatingProperty( |  | 
| 133              ui::LayerAnimationElement::OPACITY) && |  | 
| 134          layer_->GetTargetOpacity() == 0.0f && |  | 
| 135          layer_->GetAnimator()->IsAnimatingProperty( |  | 
| 136              ui::LayerAnimationElement::VISIBILITY) && |  | 
| 137          !layer_->GetTargetVisibility(); |  | 
| 138 } |  | 
| 139 |  | 
| 140 void AppearAnimationObserver::StartHideAnimation() { |  | 
| 141   if (background_layer_) |  | 
| 142     background_layer_->SetVisible(false); |  | 
| 143   if (!layer_->GetTargetVisibility()) |  | 
| 144     return; |  | 
| 145 |  | 
| 146   ui::ScopedLayerAnimationSettings animation(layer_->GetAnimator()); |  | 
| 147   animation.SetTransitionDuration(base::TimeDelta::FromMilliseconds( |  | 
| 148       UseFastAnimations() ? kHideAnimationDurationFastMs |  | 
| 149                           : kHideAnimationDurationSlowMs)); |  | 
| 150   animation.SetPreemptionStrategy( |  | 
| 151       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |  | 
| 152   layer_->SetOpacity(0.0f); |  | 
| 153   layer_->SetVisible(false); |  | 
| 154 } |  | 
| 155 |  | 
| 156 void AppearAnimationObserver::HideNowIfDoneOrOnceCompleted() { |  | 
| 157   hide_ = true; |  | 
| 158   if (attached_sequences().empty()) |  | 
| 159     StartHideAnimation(); |  | 
| 160 } |  | 
| 161 |  | 
| 162 void AppearAnimationObserver::SetBackgroundToHide(ui::Layer* background_layer) { |  | 
| 163   background_layer_ = background_layer; |  | 
| 164 } |  | 
| 165 |  | 
| 166 void AppearAnimationObserver::OnLayerAnimationEnded( |  | 
| 167     ui::LayerAnimationSequence* sequence) { |  | 
| 168   if (hide_) |  | 
| 169     StartHideAnimation(); |  | 
| 170 } |  | 
| 171 |  | 
| 172 void AppearAnimationObserver::OnLayerAnimationAborted( |  | 
| 173     ui::LayerAnimationSequence* sequence) { |  | 
| 174   if (hide_) |  | 
| 175     StartHideAnimation(); |  | 
| 176 } |  | 
| 177 |  | 
| 178 bool AppearAnimationObserver::RequiresNotificationWhenAnimatorDestroyed() |  | 
| 179     const { |  | 
| 180   // Ensures that OnImplicitAnimationsCompleted is called even if the observed |  | 
| 181   // animation is deleted. Allows for setting the proper state on |layer_|. |  | 
| 182   return true; |  | 
| 183 } |  | 
| 184 |  | 
| 185 // Renders the visual feedback. Will render a circle if |circle_| is true, |  | 
| 186 // otherwise renders a rounded rectangle. |  | 
| 187 class InkDropDelegate : public ui::LayerDelegate { |  | 
| 188  public: |  | 
| 189   InkDropDelegate(ui::Layer* layer, SkColor color); |  | 
| 190   ~InkDropDelegate() override; |  | 
| 191 |  | 
| 192   // Sets the visual style of the feedback. |  | 
| 193   void set_should_render_circle(bool should_render_circle) { |  | 
| 194     should_render_circle_ = should_render_circle; |  | 
| 195   } |  | 
| 196 |  | 
| 197   // ui::LayerDelegate: |  | 
| 198   void OnPaintLayer(const ui::PaintContext& context) override; |  | 
| 199   void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override; |  | 
| 200   void OnDeviceScaleFactorChanged(float device_scale_factor) override; |  | 
| 201   base::Closure PrepareForLayerBoundsChange() override; |  | 
| 202 |  | 
| 203  private: |  | 
| 204   // The ui::Layer being rendered to. |  | 
| 205   ui::Layer* layer_; |  | 
| 206 |  | 
| 207   // The color to paint. |  | 
| 208   SkColor color_; |  | 
| 209 |  | 
| 210   // When true renders a circle, otherwise renders a rounded rectangle. |  | 
| 211   bool should_render_circle_; |  | 
| 212 |  | 
| 213   DISALLOW_COPY_AND_ASSIGN(InkDropDelegate); |  | 
| 214 }; |  | 
| 215 |  | 
| 216 InkDropDelegate::InkDropDelegate(ui::Layer* layer, SkColor color) |  | 
| 217     : layer_(layer), color_(color), should_render_circle_(true) { |  | 
| 218 } |  | 
| 219 |  | 
| 220 InkDropDelegate::~InkDropDelegate() { |  | 
| 221 } |  | 
| 222 |  | 
| 223 void InkDropDelegate::OnPaintLayer(const ui::PaintContext& context) { |  | 
| 224   SkPaint paint; |  | 
| 225   paint.setColor(color_); |  | 
| 226   paint.setFlags(SkPaint::kAntiAlias_Flag); |  | 
| 227   paint.setStyle(SkPaint::kFill_Style); |  | 
| 228 |  | 
| 229   gfx::Rect bounds = layer_->bounds(); |  | 
| 230 |  | 
| 231   ui::PaintRecorder recorder(context, layer_->size()); |  | 
| 232   gfx::Canvas* canvas = recorder.canvas(); |  | 
| 233   if (should_render_circle_) { |  | 
| 234     gfx::Point midpoint(bounds.width() * 0.5f, bounds.height() * 0.5f); |  | 
| 235     canvas->DrawCircle(midpoint, kCircleRadius, paint); |  | 
| 236   } else { |  | 
| 237     canvas->DrawRoundRect(bounds, kRoundedRectCorners, paint); |  | 
| 238   } |  | 
| 239 } |  | 
| 240 |  | 
| 241 void InkDropDelegate::OnDelegatedFrameDamage( |  | 
| 242     const gfx::Rect& damage_rect_in_dip) { |  | 
| 243 } |  | 
| 244 |  | 
| 245 void InkDropDelegate::OnDeviceScaleFactorChanged(float device_scale_factor) { |  | 
| 246 } |  | 
| 247 |  | 
| 248 base::Closure InkDropDelegate::PrepareForLayerBoundsChange() { |  | 
| 249   return base::Closure(); |  | 
| 250 } |  | 
| 251 |  | 
| 252 InkDropAnimationController::InkDropAnimationController(views::View* view) |  | 
| 253     : ink_drop_layer_(new ui::Layer()), |  | 
| 254       ink_drop_delegate_( |  | 
| 255           new InkDropDelegate(ink_drop_layer_.get(), kInkDropColor)), |  | 
| 256       appear_animation_observer_(nullptr), |  | 
| 257       long_press_layer_(new ui::Layer()), |  | 
| 258       long_press_delegate_( |  | 
| 259           new InkDropDelegate(long_press_layer_.get(), kLongPressColor)), |  | 
| 260       long_press_animation_observer_(nullptr), |  | 
| 261       view_(view) { |  | 
| 262   view_->SetPaintToLayer(true); |  | 
| 263   view_->AddPreTargetHandler(this); |  | 
| 264   ui::Layer* layer = view_->layer(); |  | 
| 265   layer->SetMasksToBounds(!UseCircularFeedback()); |  | 
| 266   SetupAnimationLayer(layer, long_press_layer_.get(), |  | 
| 267                       long_press_delegate_.get()); |  | 
| 268   SetupAnimationLayer(layer, ink_drop_layer_.get(), ink_drop_delegate_.get()); |  | 
| 269   ink_drop_delegate_->set_should_render_circle(UseCircularFeedback()); |  | 
| 270 } |  | 
| 271 |  | 
| 272 InkDropAnimationController::~InkDropAnimationController() { |  | 
| 273   view_->RemovePreTargetHandler(this); |  | 
| 274 } |  | 
| 275 |  | 
| 276 void InkDropAnimationController::AnimateTapDown() { |  | 
| 277   if ((appear_animation_observer_ && |  | 
| 278        appear_animation_observer_->IsAnimationActive()) || |  | 
| 279       (long_press_animation_observer_ && |  | 
| 280        long_press_animation_observer_->IsAnimationActive())) { |  | 
| 281     // Only one animation at a time. Subsequent tap downs are ignored until the |  | 
| 282     // current animation completes. |  | 
| 283     return; |  | 
| 284   } |  | 
| 285   appear_animation_observer_.reset( |  | 
| 286       new AppearAnimationObserver(ink_drop_layer_.get(), false)); |  | 
| 287   AnimateShow(ink_drop_layer_.get(), appear_animation_observer_.get(), |  | 
| 288               UseCircularFeedback(), |  | 
| 289               base::TimeDelta::FromMilliseconds( |  | 
| 290                   (UseFastAnimations() ? kShowInkDropAnimationDurationFastMs |  | 
| 291                                        : kShowInkDropAnimationDurationSlowMs))); |  | 
| 292 } |  | 
| 293 |  | 
| 294 void InkDropAnimationController::AnimateHide() { |  | 
| 295   if (appear_animation_observer_) |  | 
| 296     appear_animation_observer_->HideNowIfDoneOrOnceCompleted(); |  | 
| 297 } |  | 
| 298 |  | 
| 299 void InkDropAnimationController::AnimateLongPress() { |  | 
| 300   // Only one animation at a time. Subsequent long presses are ignored until the |  | 
| 301   // current animation completes. |  | 
| 302   if (long_press_animation_observer_ && |  | 
| 303       long_press_animation_observer_->IsAnimationActive()) { |  | 
| 304     return; |  | 
| 305   } |  | 
| 306   appear_animation_observer_.reset(); |  | 
| 307   long_press_animation_observer_.reset( |  | 
| 308       new AppearAnimationObserver(long_press_layer_.get(), true)); |  | 
| 309   long_press_animation_observer_->SetBackgroundToHide(ink_drop_layer_.get()); |  | 
| 310   AnimateShow(long_press_layer_.get(), long_press_animation_observer_.get(), |  | 
| 311               true, |  | 
| 312               base::TimeDelta::FromMilliseconds( |  | 
| 313                   UseFastAnimations() ? kShowLongPressAnimationDurationFastMs |  | 
| 314                                       : kShowLongPressAnimationDurationSlowMs)); |  | 
| 315 } |  | 
| 316 |  | 
| 317 void InkDropAnimationController::AnimateShow(ui::Layer* layer, |  | 
| 318                                              AppearAnimationObserver* observer, |  | 
| 319                                              bool circle, |  | 
| 320                                              base::TimeDelta duration) { |  | 
| 321   SetLayerBounds(layer, circle, view_->width(), view_->height()); |  | 
| 322   layer->SetVisible(true); |  | 
| 323   layer->SetOpacity(1.0f); |  | 
| 324 |  | 
| 325   float start_x = layer->bounds().width() * kMinimumScaleCenteringOffset; |  | 
| 326   float start_y = layer->bounds().height() * kMinimumScaleCenteringOffset; |  | 
| 327 |  | 
| 328   gfx::Transform initial_transform; |  | 
| 329   initial_transform.Translate(start_x, start_y); |  | 
| 330   initial_transform.Scale(kMinimumScale, kMinimumScale); |  | 
| 331   layer->SetTransform(initial_transform); |  | 
| 332 |  | 
| 333   ui::LayerAnimator* animator = layer->GetAnimator(); |  | 
| 334   ui::ScopedLayerAnimationSettings animation(animator); |  | 
| 335   animation.SetPreemptionStrategy( |  | 
| 336       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |  | 
| 337 |  | 
| 338   gfx::Transform identity_transform; |  | 
| 339   ui::LayerAnimationElement* element = |  | 
| 340       ui::LayerAnimationElement::CreateTransformElement(identity_transform, |  | 
| 341                                                         duration); |  | 
| 342   ui::LayerAnimationSequence* sequence = |  | 
| 343       new ui::LayerAnimationSequence(element); |  | 
| 344   sequence->AddObserver(observer); |  | 
| 345   animator->StartAnimation(sequence); |  | 
| 346 } |  | 
| 347 |  | 
| 348 void InkDropAnimationController::SetLayerBounds(ui::Layer* layer, |  | 
| 349                                                 bool circle, |  | 
| 350                                                 int width, |  | 
| 351                                                 int height) { |  | 
| 352   float circle_width = circle ? 2.0f * kCircleRadius : width; |  | 
| 353   float circle_height = circle ? 2.0f * kCircleRadius : height; |  | 
| 354   float circle_x = circle ? (width - circle_width) * 0.5f : 0; |  | 
| 355   float circle_y = circle ? (height - circle_height) * 0.5f : 0; |  | 
| 356   layer->SetBounds(gfx::Rect(circle_x, circle_y, circle_width, circle_height)); |  | 
| 357 } |  | 
| 358 |  | 
| 359 void InkDropAnimationController::SetupAnimationLayer( |  | 
| 360     ui::Layer* parent, |  | 
| 361     ui::Layer* layer, |  | 
| 362     InkDropDelegate* delegate) { |  | 
| 363   layer->SetFillsBoundsOpaquely(false); |  | 
| 364   layer->set_delegate(delegate); |  | 
| 365   layer->SetVisible(false); |  | 
| 366   layer->SetBounds(gfx::Rect()); |  | 
| 367   parent->Add(layer); |  | 
| 368   parent->StackAtBottom(layer); |  | 
| 369 } |  | 
| 370 |  | 
| 371 void InkDropAnimationController::OnGestureEvent(ui::GestureEvent* event) { |  | 
| 372   if (event->target() != view_) |  | 
| 373     return; |  | 
| 374 |  | 
| 375   switch (event->type()) { |  | 
| 376     case ui::ET_GESTURE_TAP_DOWN: |  | 
| 377       AnimateTapDown(); |  | 
| 378       break; |  | 
| 379     case ui::ET_GESTURE_LONG_PRESS: |  | 
| 380       AnimateLongPress(); |  | 
| 381       break; |  | 
| 382     case ui::ET_GESTURE_END: |  | 
| 383     case ui::ET_GESTURE_TAP_CANCEL: |  | 
| 384     case ui::ET_GESTURE_TAP: |  | 
| 385       AnimateHide(); |  | 
| 386       break; |  | 
| 387     default: |  | 
| 388       break; |  | 
| 389   } |  | 
| 390 } |  | 
| 391 |  | 
| 392 }  // namespace views |  | 
| OLD | NEW | 
|---|