| 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 |