| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/animation/ink_drop_animation_controller_impl.h" | 5 #include "ui/views/animation/ink_drop_animation_controller_impl.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "ui/views/animation/ink_drop_animation.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/gfx/geometry/size.h" | |
| 17 #include "ui/views/animation/ink_drop_delegate.h" | |
| 18 #include "ui/views/animation/ink_drop_host.h" | 8 #include "ui/views/animation/ink_drop_host.h" |
| 19 #include "ui/views/view.h" | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 // Animation constants | |
| 24 const float kMinimumScale = 0.1f; | |
| 25 const float kMinimumScaleCenteringOffset = 0.5f - kMinimumScale / 2.0f; | |
| 26 | |
| 27 const int kHideAnimationDurationFastMs = 100; | |
| 28 const int kHideAnimationDurationSlowMs = 1000; | |
| 29 | |
| 30 const int kShowInkDropAnimationDurationFastMs = 250; | |
| 31 const int kShowInkDropAnimationDurationSlowMs = 750; | |
| 32 | |
| 33 const int kShowLongPressAnimationDurationFastMs = 250; | |
| 34 const int kShowLongPressAnimationDurationSlowMs = 2500; | |
| 35 | |
| 36 const int kRoundedRectCorners = 5; | |
| 37 const int kCircleRadius = 30; | |
| 38 | |
| 39 const SkColor kInkDropColor = SK_ColorLTGRAY; | |
| 40 const SkColor kLongPressColor = SkColorSetRGB(182, 182, 182); | |
| 41 | |
| 42 // Checks CommandLine switches to determine if the visual feedback should be | |
| 43 // circular. | |
| 44 bool UseCircularFeedback() { | |
| 45 static bool circular = | |
| 46 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 47 (::switches::kMaterialDesignInkDrop)) != | |
| 48 ::switches::kMaterialDesignInkDropSquare; | |
| 49 return circular; | |
| 50 } | |
| 51 | |
| 52 // Checks CommandLine switches to determine if the visual feedback should have | |
| 53 // a fast animations speed. | |
| 54 bool UseFastAnimations() { | |
| 55 static bool fast = | |
| 56 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 57 (::switches::kMaterialDesignInkDropAnimationSpeed)) != | |
| 58 ::switches::kMaterialDesignInkDropAnimationSpeedSlow; | |
| 59 return fast; | |
| 60 } | |
| 61 | |
| 62 } // namespace | |
| 63 | 9 |
| 64 namespace views { | 10 namespace views { |
| 65 | 11 |
| 66 // An animation observer that should be set on animations of the provided | |
| 67 // ui::Layer. Can be used to either start a hide animation, or to trigger one | |
| 68 // upon completion of the current animation. | |
| 69 // | |
| 70 // Sequential animations with PreemptionStrategy::ENQUEUE_NEW_ANIMATION cannot | |
| 71 // be used as the observed animation can complete before user input is received | |
| 72 // which determines if the hide animation should run. | |
| 73 class AppearAnimationObserver : public ui::LayerAnimationObserver { | |
| 74 public: | |
| 75 // Will automatically start a hide animation of |layer| if |hide| is true. | |
| 76 // Otherwise StartHideAnimation() or HideNowIfDoneOrOnceCompleted() must be | |
| 77 // called. | |
| 78 AppearAnimationObserver(ui::Layer* layer, bool hide); | |
| 79 ~AppearAnimationObserver() override; | |
| 80 | |
| 81 // Returns true during both the appearing animation, and the hiding animation. | |
| 82 bool IsAnimationActive(); | |
| 83 | |
| 84 // Starts a hide animation, preempting any current animations on |layer_|. | |
| 85 void StartHideAnimation(); | |
| 86 | |
| 87 // Starts a hide animation if |layer_| is no longer animating. Otherwise the | |
| 88 // hide animation will be started once the current animation is completed. | |
| 89 void HideNowIfDoneOrOnceCompleted(); | |
| 90 | |
| 91 // Hides |background_layer| (without animation) after the current animation | |
| 92 // completes. | |
| 93 void SetBackgroundToHide(ui::Layer* background_layer); | |
| 94 | |
| 95 private: | |
| 96 // ui::ImplicitAnimationObserver: | |
| 97 void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override; | |
| 98 void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override; | |
| 99 void OnLayerAnimationScheduled( | |
| 100 ui::LayerAnimationSequence* sequence) override {} | |
| 101 | |
| 102 bool RequiresNotificationWhenAnimatorDestroyed() const override; | |
| 103 | |
| 104 // The ui::Layer being observed, which hide animations will be set on. | |
| 105 ui::Layer* layer_; | |
| 106 | |
| 107 // Optional ui::Layer which will be hidden upon the completion of animating | |
| 108 // |layer_| | |
| 109 ui::Layer* background_layer_; | |
| 110 | |
| 111 // If true the hide animation will immediately be scheduled upon completion of | |
| 112 // the observed animation. | |
| 113 bool hide_; | |
| 114 | |
| 115 DISALLOW_COPY_AND_ASSIGN(AppearAnimationObserver); | |
| 116 }; | |
| 117 | |
| 118 AppearAnimationObserver::AppearAnimationObserver(ui::Layer* layer, bool hide) | |
| 119 : layer_(layer), background_layer_(nullptr), hide_(hide) {} | |
| 120 | |
| 121 AppearAnimationObserver::~AppearAnimationObserver() { | |
| 122 StopObserving(); | |
| 123 } | |
| 124 | |
| 125 bool AppearAnimationObserver::IsAnimationActive() { | |
| 126 // Initial animation ongoing | |
| 127 if (!attached_sequences().empty()) | |
| 128 return true; | |
| 129 // Maintain the animation until told to hide. | |
| 130 if (!hide_) | |
| 131 return true; | |
| 132 | |
| 133 // Check the state of the triggered hide animation | |
| 134 return layer_->GetAnimator()->IsAnimatingProperty( | |
| 135 ui::LayerAnimationElement::OPACITY) && | |
| 136 layer_->GetTargetOpacity() == 0.0f && | |
| 137 layer_->GetAnimator()->IsAnimatingProperty( | |
| 138 ui::LayerAnimationElement::VISIBILITY) && | |
| 139 !layer_->GetTargetVisibility(); | |
| 140 } | |
| 141 | |
| 142 void AppearAnimationObserver::StartHideAnimation() { | |
| 143 if (background_layer_) | |
| 144 background_layer_->SetVisible(false); | |
| 145 if (!layer_->GetTargetVisibility()) | |
| 146 return; | |
| 147 | |
| 148 ui::ScopedLayerAnimationSettings animation(layer_->GetAnimator()); | |
| 149 animation.SetTransitionDuration(base::TimeDelta::FromMilliseconds( | |
| 150 UseFastAnimations() ? kHideAnimationDurationFastMs | |
| 151 : kHideAnimationDurationSlowMs)); | |
| 152 animation.SetPreemptionStrategy( | |
| 153 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
| 154 layer_->SetOpacity(0.0f); | |
| 155 layer_->SetVisible(false); | |
| 156 } | |
| 157 | |
| 158 void AppearAnimationObserver::HideNowIfDoneOrOnceCompleted() { | |
| 159 hide_ = true; | |
| 160 if (attached_sequences().empty()) | |
| 161 StartHideAnimation(); | |
| 162 } | |
| 163 | |
| 164 void AppearAnimationObserver::SetBackgroundToHide(ui::Layer* background_layer) { | |
| 165 background_layer_ = background_layer; | |
| 166 } | |
| 167 | |
| 168 void AppearAnimationObserver::OnLayerAnimationEnded( | |
| 169 ui::LayerAnimationSequence* sequence) { | |
| 170 if (hide_) | |
| 171 StartHideAnimation(); | |
| 172 } | |
| 173 | |
| 174 void AppearAnimationObserver::OnLayerAnimationAborted( | |
| 175 ui::LayerAnimationSequence* sequence) { | |
| 176 if (hide_) | |
| 177 StartHideAnimation(); | |
| 178 } | |
| 179 | |
| 180 bool AppearAnimationObserver::RequiresNotificationWhenAnimatorDestroyed() | |
| 181 const { | |
| 182 // Ensures that OnImplicitAnimationsCompleted is called even if the observed | |
| 183 // animation is deleted. Allows for setting the proper state on |layer_|. | |
| 184 return true; | |
| 185 } | |
| 186 | |
| 187 InkDropAnimationControllerImpl::InkDropAnimationControllerImpl( | 12 InkDropAnimationControllerImpl::InkDropAnimationControllerImpl( |
| 188 InkDropHost* ink_drop_host) | 13 InkDropHost* ink_drop_host) |
| 189 : ink_drop_host_(ink_drop_host), | 14 : ink_drop_host_(ink_drop_host), |
| 190 root_layer_(new ui::Layer(ui::LAYER_NOT_DRAWN)), | 15 ink_drop_animation_(new InkDropAnimation()) { |
| 191 ink_drop_layer_(new ui::Layer()), | |
| 192 appear_animation_observer_(nullptr), | |
| 193 long_press_layer_(new ui::Layer()), | |
| 194 long_press_animation_observer_(nullptr), | |
| 195 ink_drop_bounds_(0, 0, 0, 0) { | |
| 196 ink_drop_delegate_.reset(new InkDropDelegate(ink_drop_layer_.get(), | |
| 197 kInkDropColor, kCircleRadius, | |
| 198 kRoundedRectCorners)); | |
| 199 long_press_delegate_.reset(new InkDropDelegate(long_press_layer_.get(), | |
| 200 kLongPressColor, kCircleRadius, | |
| 201 kRoundedRectCorners)); | |
| 202 | |
| 203 SetupAnimationLayer(long_press_layer_.get(), long_press_delegate_.get()); | |
| 204 SetupAnimationLayer(ink_drop_layer_.get(), ink_drop_delegate_.get()); | |
| 205 | |
| 206 root_layer_->Add(ink_drop_layer_.get()); | |
| 207 root_layer_->Add(long_press_layer_.get()); | |
| 208 | |
| 209 // TODO(bruthig): Change this to only be called before the ink drop becomes | 16 // TODO(bruthig): Change this to only be called before the ink drop becomes |
| 210 // active. See www.crbug.com/522175. | 17 // active. See www.crbug.com/522175. |
| 211 ink_drop_host_->AddInkDropLayer(root_layer_.get()); | 18 ink_drop_host_->AddInkDropLayer(ink_drop_animation_->root_layer()); |
| 212 } | 19 } |
| 213 | 20 |
| 214 InkDropAnimationControllerImpl::~InkDropAnimationControllerImpl() { | 21 InkDropAnimationControllerImpl::~InkDropAnimationControllerImpl() { |
| 215 // TODO(bruthig): Change this to be called when the ink drop becomes hidden. | 22 // TODO(bruthig): Change this to be called when the ink drop becomes hidden. |
| 216 // See www.crbug.com/522175. | 23 // See www.crbug.com/522175. |
| 217 ink_drop_host_->RemoveInkDropLayer(root_layer_.get()); | 24 ink_drop_host_->RemoveInkDropLayer(ink_drop_animation_->root_layer()); |
| 218 } | 25 } |
| 219 | 26 |
| 220 void InkDropAnimationControllerImpl::AnimateToState(InkDropState state) { | 27 void InkDropAnimationControllerImpl::AnimateToState(InkDropState state) { |
| 221 // TODO(bruthig): Do not transition if we are already in |state| and restrict | 28 ink_drop_animation_->AnimateToState(state); |
| 222 // any state transition that don't make sense or wouldn't look visually | |
| 223 // appealing. | |
| 224 switch (state) { | |
| 225 case InkDropState::HIDDEN: | |
| 226 AnimateHide(); | |
| 227 break; | |
| 228 case InkDropState::ACTION_PENDING: | |
| 229 AnimateTapDown(); | |
| 230 break; | |
| 231 case InkDropState::QUICK_ACTION: | |
| 232 AnimateTapDown(); | |
| 233 AnimateHide(); | |
| 234 break; | |
| 235 case InkDropState::SLOW_ACTION: | |
| 236 AnimateLongPress(); | |
| 237 break; | |
| 238 case InkDropState::ACTIVATED: | |
| 239 AnimateLongPress(); | |
| 240 break; | |
| 241 } | |
| 242 } | 29 } |
| 243 | 30 |
| 244 void InkDropAnimationControllerImpl::SetInkDropSize(const gfx::Size& size) { | 31 void InkDropAnimationControllerImpl::SetInkDropSize(const gfx::Size& size) { |
| 245 SetInkDropBounds(gfx::Rect(ink_drop_bounds_.origin(), size)); | 32 ink_drop_animation_->SetInkDropSize(size); |
| 246 } | 33 } |
| 247 | 34 |
| 248 gfx::Rect InkDropAnimationControllerImpl::GetInkDropBounds() const { | 35 gfx::Rect InkDropAnimationControllerImpl::GetInkDropBounds() const { |
| 249 return ink_drop_bounds_; | 36 return ink_drop_animation_->GetInkDropBounds(); |
| 250 } | 37 } |
| 251 | 38 |
| 252 void InkDropAnimationControllerImpl::SetInkDropBounds(const gfx::Rect& bounds) { | 39 void InkDropAnimationControllerImpl::SetInkDropBounds(const gfx::Rect& bounds) { |
| 253 ink_drop_bounds_ = bounds; | 40 ink_drop_animation_->SetInkDropBounds(bounds); |
| 254 SetLayerBounds(ink_drop_layer_.get()); | |
| 255 SetLayerBounds(long_press_layer_.get()); | |
| 256 } | |
| 257 | |
| 258 void InkDropAnimationControllerImpl::AnimateTapDown() { | |
| 259 if ((appear_animation_observer_ && | |
| 260 appear_animation_observer_->IsAnimationActive()) || | |
| 261 (long_press_animation_observer_ && | |
| 262 long_press_animation_observer_->IsAnimationActive())) { | |
| 263 // Only one animation at a time. Subsequent tap downs are ignored until the | |
| 264 // current animation completes. | |
| 265 return; | |
| 266 } | |
| 267 appear_animation_observer_.reset( | |
| 268 new AppearAnimationObserver(ink_drop_layer_.get(), false)); | |
| 269 AnimateShow(ink_drop_layer_.get(), appear_animation_observer_.get(), | |
| 270 base::TimeDelta::FromMilliseconds( | |
| 271 (UseFastAnimations() ? kShowInkDropAnimationDurationFastMs | |
| 272 : kShowInkDropAnimationDurationSlowMs))); | |
| 273 } | |
| 274 | |
| 275 void InkDropAnimationControllerImpl::AnimateHide() { | |
| 276 if (appear_animation_observer_ && | |
| 277 appear_animation_observer_->IsAnimationActive()) { | |
| 278 appear_animation_observer_->HideNowIfDoneOrOnceCompleted(); | |
| 279 } else if (long_press_animation_observer_) { | |
| 280 long_press_animation_observer_->HideNowIfDoneOrOnceCompleted(); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 void InkDropAnimationControllerImpl::AnimateLongPress() { | |
| 285 // Only one animation at a time. Subsequent long presses are ignored until the | |
| 286 // current animation completes. | |
| 287 if (long_press_animation_observer_ && | |
| 288 long_press_animation_observer_->IsAnimationActive()) { | |
| 289 return; | |
| 290 } | |
| 291 appear_animation_observer_.reset(); | |
| 292 long_press_animation_observer_.reset( | |
| 293 new AppearAnimationObserver(long_press_layer_.get(), false)); | |
| 294 long_press_animation_observer_->SetBackgroundToHide(ink_drop_layer_.get()); | |
| 295 AnimateShow(long_press_layer_.get(), long_press_animation_observer_.get(), | |
| 296 base::TimeDelta::FromMilliseconds( | |
| 297 UseFastAnimations() ? kShowLongPressAnimationDurationFastMs | |
| 298 : kShowLongPressAnimationDurationSlowMs)); | |
| 299 } | |
| 300 | |
| 301 void InkDropAnimationControllerImpl::AnimateShow( | |
| 302 ui::Layer* layer, | |
| 303 AppearAnimationObserver* observer, | |
| 304 base::TimeDelta duration) { | |
| 305 layer->SetVisible(true); | |
| 306 layer->SetOpacity(1.0f); | |
| 307 | |
| 308 float start_x = ink_drop_bounds_.x() + | |
| 309 layer->bounds().width() * kMinimumScaleCenteringOffset; | |
| 310 float start_y = ink_drop_bounds_.y() + | |
| 311 layer->bounds().height() * kMinimumScaleCenteringOffset; | |
| 312 | |
| 313 gfx::Transform initial_transform; | |
| 314 initial_transform.Translate(start_x, start_y); | |
| 315 initial_transform.Scale(kMinimumScale, kMinimumScale); | |
| 316 layer->SetTransform(initial_transform); | |
| 317 | |
| 318 ui::LayerAnimator* animator = layer->GetAnimator(); | |
| 319 ui::ScopedLayerAnimationSettings animation(animator); | |
| 320 animation.SetPreemptionStrategy( | |
| 321 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
| 322 | |
| 323 gfx::Transform target_transform; | |
| 324 target_transform.Translate(ink_drop_bounds_.x(), ink_drop_bounds_.y()); | |
| 325 ui::LayerAnimationElement* element = | |
| 326 ui::LayerAnimationElement::CreateTransformElement(target_transform, | |
| 327 duration); | |
| 328 ui::LayerAnimationSequence* sequence = | |
| 329 new ui::LayerAnimationSequence(element); | |
| 330 sequence->AddObserver(observer); | |
| 331 animator->StartAnimation(sequence); | |
| 332 } | |
| 333 | |
| 334 void InkDropAnimationControllerImpl::SetLayerBounds(ui::Layer* layer) { | |
| 335 bool circle = UseCircularFeedback(); | |
| 336 gfx::Size size = ink_drop_bounds_.size(); | |
| 337 float circle_width = circle ? 2.0f * kCircleRadius : size.width(); | |
| 338 float circle_height = circle ? 2.0f * kCircleRadius : size.height(); | |
| 339 float circle_x = circle ? (size.width() - circle_width) * 0.5f : 0; | |
| 340 float circle_y = circle ? (size.height() - circle_height) * 0.5f : 0; | |
| 341 layer->SetBounds(gfx::Rect(circle_x, circle_y, circle_width, circle_height)); | |
| 342 } | |
| 343 | |
| 344 void InkDropAnimationControllerImpl::SetupAnimationLayer( | |
| 345 ui::Layer* layer, | |
| 346 InkDropDelegate* delegate) { | |
| 347 layer->SetFillsBoundsOpaquely(false); | |
| 348 layer->set_delegate(delegate); | |
| 349 layer->SetVisible(false); | |
| 350 layer->SetBounds(gfx::Rect()); | |
| 351 delegate->set_should_render_circle(UseCircularFeedback()); | |
| 352 } | 41 } |
| 353 | 42 |
| 354 } // namespace views | 43 } // namespace views |
| OLD | NEW |