base/threading: remove ScopedTracker placed for experiments
[chromium-blink-merge.git] / ui / views / animation / ink_drop_animation_controller.cc
blobbfe459dd8fa1e60cc39ca51b63158023407b5902
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.
5 #include "ui/views/animation/ink_drop_animation_controller.h"
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"
18 namespace {
20 // Animation constants
21 const float kMinimumScale = 0.1f;
22 const float kMinimumScaleCenteringOffset = 0.5f - kMinimumScale / 2.0f;
24 const int kHideAnimationDurationFastMs = 100;
25 const int kHideAnimationDurationSlowMs = 1000;
27 const int kShowInkDropAnimationDurationFastMs = 250;
28 const int kShowInkDropAnimationDurationSlowMs = 750;
30 const int kShowLongPressAnimationDurationFastMs = 250;
31 const int kShowLongPressAnimationDurationSlowMs = 2500;
33 const int kRoundedRectCorners = 5;
34 const int kCircleRadius = 30;
36 const SkColor kInkDropColor = SK_ColorLTGRAY;
37 const SkColor kLongPressColor = SkColorSetRGB(182, 182, 182);
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;
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;
59 } // namespace
61 namespace views {
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.
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;
78 // Returns true during both the appearing animation, and the hiding animation.
79 bool IsAnimationActive();
81 // Starts a hide animation, preempting any current animations on |layer_|.
82 void StartHideAnimation();
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();
88 // Hides |background_layer| (without animation) after the current animation
89 // completes.
90 void SetBackgroundToHide(ui::Layer* background_layer);
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 {}
99 bool RequiresNotificationWhenAnimatorDestroyed() const override;
101 // The ui::Layer being observed, which hide animations will be set on.
102 ui::Layer* layer_;
104 // Optional ui::Layer which will be hidden upon the completion of animating
105 // |layer_|
106 ui::Layer* background_layer_;
108 // If true the hide animation will immediately be scheduled upon completion of
109 // the observed animation.
110 bool hide_;
112 DISALLOW_COPY_AND_ASSIGN(AppearAnimationObserver);
115 AppearAnimationObserver::AppearAnimationObserver(ui::Layer* layer, bool hide)
116 : layer_(layer), background_layer_(nullptr), hide_(hide) {
119 AppearAnimationObserver::~AppearAnimationObserver() {
120 StopObserving();
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;
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();
140 void AppearAnimationObserver::StartHideAnimation() {
141 if (background_layer_)
142 background_layer_->SetVisible(false);
143 if (!layer_->GetTargetVisibility())
144 return;
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);
156 void AppearAnimationObserver::HideNowIfDoneOrOnceCompleted() {
157 hide_ = true;
158 if (attached_sequences().empty())
159 StartHideAnimation();
162 void AppearAnimationObserver::SetBackgroundToHide(ui::Layer* background_layer) {
163 background_layer_ = background_layer;
166 void AppearAnimationObserver::OnLayerAnimationEnded(
167 ui::LayerAnimationSequence* sequence) {
168 if (hide_)
169 StartHideAnimation();
172 void AppearAnimationObserver::OnLayerAnimationAborted(
173 ui::LayerAnimationSequence* sequence) {
174 if (hide_)
175 StartHideAnimation();
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;
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;
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;
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;
203 private:
204 // The ui::Layer being rendered to.
205 ui::Layer* layer_;
207 // The color to paint.
208 SkColor color_;
210 // When true renders a circle, otherwise renders a rounded rectangle.
211 bool should_render_circle_;
213 DISALLOW_COPY_AND_ASSIGN(InkDropDelegate);
216 InkDropDelegate::InkDropDelegate(ui::Layer* layer, SkColor color)
217 : layer_(layer), color_(color), should_render_circle_(true) {
220 InkDropDelegate::~InkDropDelegate() {
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);
229 gfx::Rect bounds = layer_->bounds();
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);
241 void InkDropDelegate::OnDelegatedFrameDamage(
242 const gfx::Rect& damage_rect_in_dip) {
245 void InkDropDelegate::OnDeviceScaleFactorChanged(float device_scale_factor) {
248 base::Closure InkDropDelegate::PrepareForLayerBoundsChange() {
249 return base::Closure();
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());
272 InkDropAnimationController::~InkDropAnimationController() {
273 view_->RemovePreTargetHandler(this);
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;
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)));
294 void InkDropAnimationController::AnimateHide() {
295 if (appear_animation_observer_)
296 appear_animation_observer_->HideNowIfDoneOrOnceCompleted();
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;
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));
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);
325 float start_x = layer->bounds().width() * kMinimumScaleCenteringOffset;
326 float start_y = layer->bounds().height() * kMinimumScaleCenteringOffset;
328 gfx::Transform initial_transform;
329 initial_transform.Translate(start_x, start_y);
330 initial_transform.Scale(kMinimumScale, kMinimumScale);
331 layer->SetTransform(initial_transform);
333 ui::LayerAnimator* animator = layer->GetAnimator();
334 ui::ScopedLayerAnimationSettings animation(animator);
335 animation.SetPreemptionStrategy(
336 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
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);
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));
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);
371 void InkDropAnimationController::OnGestureEvent(ui::GestureEvent* event) {
372 if (event->target() != view_)
373 return;
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;
392 } // namespace views