1 // Copyright (c) 2012 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/corewm/window_animations.h" 6 7 #include <math.h> 8 9 #include <algorithm> 10 #include <vector> 11 12 #include "base/command_line.h" 13 #include "base/compiler_specific.h" 14 #include "base/logging.h" 15 #include "base/message_loop/message_loop.h" 16 #include "base/stl_util.h" 17 #include "base/time/time.h" 18 #include "ui/aura/client/animation_host.h" 19 #include "ui/aura/client/aura_constants.h" 20 #include "ui/aura/window.h" 21 #include "ui/aura/window_delegate.h" 22 #include "ui/aura/window_observer.h" 23 #include "ui/aura/window_property.h" 24 #include "ui/compositor/compositor_observer.h" 25 #include "ui/compositor/layer.h" 26 #include "ui/compositor/layer_animation_observer.h" 27 #include "ui/compositor/layer_animation_sequence.h" 28 #include "ui/compositor/layer_animator.h" 29 #include "ui/compositor/scoped_layer_animation_settings.h" 30 #include "ui/gfx/interpolated_transform.h" 31 #include "ui/gfx/rect_conversions.h" 32 #include "ui/gfx/screen.h" 33 #include "ui/gfx/vector3d_f.h" 34 #include "ui/views/corewm/corewm_switches.h" 35 #include "ui/views/corewm/window_util.h" 36 #include "ui/views/view.h" 37 #include "ui/views/widget/widget.h" 38 39 DECLARE_WINDOW_PROPERTY_TYPE(int) 40 DECLARE_WINDOW_PROPERTY_TYPE(views::corewm::WindowVisibilityAnimationType) 41 DECLARE_WINDOW_PROPERTY_TYPE(views::corewm::WindowVisibilityAnimationTransition) 42 DECLARE_WINDOW_PROPERTY_TYPE(float) 43 DECLARE_EXPORTED_WINDOW_PROPERTY_TYPE(VIEWS_EXPORT, bool) 44 45 using aura::Window; 46 using base::TimeDelta; 47 using ui::Layer; 48 49 namespace views { 50 namespace corewm { 51 namespace { 52 const float kWindowAnimation_Vertical_TranslateY = 15.f; 53 } // namespace 54 55 DEFINE_WINDOW_PROPERTY_KEY(int, 56 kWindowVisibilityAnimationTypeKey, 57 WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT); 58 DEFINE_WINDOW_PROPERTY_KEY(int, kWindowVisibilityAnimationDurationKey, 0); 59 DEFINE_WINDOW_PROPERTY_KEY(WindowVisibilityAnimationTransition, 60 kWindowVisibilityAnimationTransitionKey, 61 ANIMATE_BOTH); 62 DEFINE_WINDOW_PROPERTY_KEY(float, 63 kWindowVisibilityAnimationVerticalPositionKey, 64 kWindowAnimation_Vertical_TranslateY); 65 66 namespace { 67 68 const int kDefaultAnimationDurationForMenuMS = 150; 69 70 const float kWindowAnimation_HideOpacity = 0.f; 71 const float kWindowAnimation_ShowOpacity = 1.f; 72 const float kWindowAnimation_TranslateFactor = 0.5f; 73 const float kWindowAnimation_ScaleFactor = .95f; 74 75 const int kWindowAnimation_Rotate_DurationMS = 180; 76 const int kWindowAnimation_Rotate_OpacityDurationPercent = 90; 77 const float kWindowAnimation_Rotate_TranslateY = -20.f; 78 const float kWindowAnimation_Rotate_PerspectiveDepth = 500.f; 79 const float kWindowAnimation_Rotate_DegreesX = 5.f; 80 const float kWindowAnimation_Rotate_ScaleFactor = .99f; 81 82 const float kWindowAnimation_Bounce_Scale = 1.02f; 83 const int kWindowAnimation_Bounce_DurationMS = 180; 84 const int kWindowAnimation_Bounce_GrowShrinkDurationPercent = 40; 85 86 base::TimeDelta GetWindowVisibilityAnimationDuration(aura::Window* window) { 87 int duration = 88 window->GetProperty(kWindowVisibilityAnimationDurationKey); 89 if (duration == 0 && window->type() == aura::client::WINDOW_TYPE_MENU) { 90 return base::TimeDelta::FromMilliseconds( 91 kDefaultAnimationDurationForMenuMS); 92 } 93 return TimeDelta::FromInternalValue(duration); 94 } 95 96 // Gets/sets the WindowVisibilityAnimationType associated with a window. 97 // TODO(beng): redundant/fold into method on public api? 98 int GetWindowVisibilityAnimationType(aura::Window* window) { 99 int type = window->GetProperty(kWindowVisibilityAnimationTypeKey); 100 if (type == WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT) { 101 return (window->type() == aura::client::WINDOW_TYPE_MENU || 102 window->type() == aura::client::WINDOW_TYPE_TOOLTIP) ? 103 WINDOW_VISIBILITY_ANIMATION_TYPE_FADE : 104 WINDOW_VISIBILITY_ANIMATION_TYPE_DROP; 105 } 106 return type; 107 } 108 109 // Observes a hide animation. 110 // A window can be hidden for a variety of reasons. Sometimes, Hide() will be 111 // called and life is simple. Sometimes, the window is actually bound to a 112 // views::Widget and that Widget is closed, and life is a little more 113 // complicated. When a Widget is closed the aura::Window* is actually not 114 // destroyed immediately - it is actually just immediately hidden and then 115 // destroyed when the stack unwinds. To handle this case, we start the hide 116 // animation immediately when the window is hidden, then when the window is 117 // subsequently destroyed this object acquires ownership of the window's layer, 118 // so that it can continue animating it until the animation completes. 119 // Regardless of whether or not the window is destroyed, this object deletes 120 // itself when the animation completes. 121 class HidingWindowAnimationObserver : public ui::ImplicitAnimationObserver, 122 public aura::WindowObserver { 123 public: 124 explicit HidingWindowAnimationObserver(aura::Window* window) 125 : window_(window) { 126 window_->AddObserver(this); 127 } 128 virtual ~HidingWindowAnimationObserver() { 129 STLDeleteElements(&layers_); 130 } 131 132 private: 133 // Overridden from ui::ImplicitAnimationObserver: 134 virtual void OnImplicitAnimationsCompleted() OVERRIDE { 135 // Window may have been destroyed by this point. 136 if (window_) { 137 aura::client::AnimationHost* animation_host = 138 aura::client::GetAnimationHost(window_); 139 if (animation_host) 140 animation_host->OnWindowHidingAnimationCompleted(); 141 window_->RemoveObserver(this); 142 } 143 delete this; 144 } 145 146 // Overridden from aura::WindowObserver: 147 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { 148 DCHECK_EQ(window, window_); 149 DCHECK(layers_.empty()); 150 AcquireAllLayers(window_); 151 152 // If the Widget has views with layers, then it is necessary to take 153 // ownership of those layers too. 154 views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window_); 155 const views::Widget* const_widget = widget; 156 if (widget && const_widget->GetRootView() && widget->GetContentsView()) 157 AcquireAllViewLayers(widget->GetContentsView()); 158 window_->RemoveObserver(this); 159 window_ = NULL; 160 } 161 162 void AcquireAllLayers(aura::Window* window) { 163 ui::Layer* layer = window->AcquireLayer(); 164 DCHECK(layer); 165 layers_.push_back(layer); 166 for (aura::Window::Windows::const_iterator it = window->children().begin(); 167 it != window->children().end(); 168 ++it) 169 AcquireAllLayers(*it); 170 } 171 172 void AcquireAllViewLayers(views::View* view) { 173 for (int i = 0; i < view->child_count(); ++i) 174 AcquireAllViewLayers(view->child_at(i)); 175 if (view->layer()) { 176 ui::Layer* layer = view->RecreateLayer(); 177 if (layer) { 178 layer->SuppressPaint(); 179 layers_.push_back(layer); 180 } 181 } 182 } 183 184 aura::Window* window_; 185 std::vector<ui::Layer*> layers_; 186 187 DISALLOW_COPY_AND_ASSIGN(HidingWindowAnimationObserver); 188 }; 189 190 void GetTransformRelativeToRoot(ui::Layer* layer, gfx::Transform* transform) { 191 const Layer* root = layer; 192 while (root->parent()) 193 root = root->parent(); 194 layer->GetTargetTransformRelativeTo(root, transform); 195 } 196 197 gfx::Rect GetLayerWorldBoundsAfterTransform(ui::Layer* layer, 198 const gfx::Transform& transform) { 199 gfx::Transform in_world = transform; 200 GetTransformRelativeToRoot(layer, &in_world); 201 202 gfx::RectF transformed = layer->bounds(); 203 in_world.TransformRect(&transformed); 204 205 return gfx::ToEnclosingRect(transformed); 206 } 207 208 // Augment the host window so that the enclosing bounds of the full 209 // animation will fit inside of it. 210 void AugmentWindowSize(aura::Window* window, 211 const gfx::Transform& start_transform, 212 const gfx::Transform& end_transform) { 213 aura::client::AnimationHost* animation_host = 214 aura::client::GetAnimationHost(window); 215 if (!animation_host) 216 return; 217 218 gfx::Rect world_at_start = 219 GetLayerWorldBoundsAfterTransform(window->layer(), start_transform); 220 gfx::Rect world_at_end = 221 GetLayerWorldBoundsAfterTransform(window->layer(), end_transform); 222 gfx::Rect union_in_window_space = 223 gfx::UnionRects(world_at_start, world_at_end); 224 gfx::Rect current_bounds = window->bounds(); 225 gfx::Rect result(union_in_window_space.x() - current_bounds.x(), 226 union_in_window_space.y() - current_bounds.y(), 227 union_in_window_space.width() - current_bounds.width(), 228 union_in_window_space.height() - current_bounds.height()); 229 animation_host->SetHostTransitionBounds(result); 230 } 231 232 // Shows a window using an animation, animating its opacity from 0.f to 1.f, 233 // its visibility to true, and its transform from |start_transform| to 234 // |end_transform|. 235 void AnimateShowWindowCommon(aura::Window* window, 236 const gfx::Transform& start_transform, 237 const gfx::Transform& end_transform) { 238 window->layer()->set_delegate(window); 239 240 AugmentWindowSize(window, start_transform, end_transform); 241 242 window->layer()->SetOpacity(kWindowAnimation_HideOpacity); 243 window->layer()->SetTransform(start_transform); 244 window->layer()->SetVisible(true); 245 246 { 247 // Property sets within this scope will be implicitly animated. 248 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); 249 base::TimeDelta duration = GetWindowVisibilityAnimationDuration(window); 250 if (duration.ToInternalValue() > 0) 251 settings.SetTransitionDuration(duration); 252 253 window->layer()->SetTransform(end_transform); 254 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity); 255 } 256 } 257 258 // Hides a window using an animation, animating its opacity from 1.f to 0.f, 259 // its visibility to false, and its transform to |end_transform|. 260 void AnimateHideWindowCommon(aura::Window* window, 261 const gfx::Transform& end_transform) { 262 AugmentWindowSize(window, gfx::Transform(), end_transform); 263 window->layer()->set_delegate(NULL); 264 265 // Property sets within this scope will be implicitly animated. 266 ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); 267 settings.AddObserver(new HidingWindowAnimationObserver(window)); 268 269 base::TimeDelta duration = GetWindowVisibilityAnimationDuration(window); 270 if (duration.ToInternalValue() > 0) 271 settings.SetTransitionDuration(duration); 272 273 window->layer()->SetOpacity(kWindowAnimation_HideOpacity); 274 window->layer()->SetTransform(end_transform); 275 window->layer()->SetVisible(false); 276 } 277 278 static gfx::Transform GetScaleForWindow(aura::Window* window) { 279 gfx::Rect bounds = window->bounds(); 280 gfx::Transform scale = gfx::GetScaleTransform( 281 gfx::Point(kWindowAnimation_TranslateFactor * bounds.width(), 282 kWindowAnimation_TranslateFactor * bounds.height()), 283 kWindowAnimation_ScaleFactor); 284 return scale; 285 } 286 287 // Show/Hide windows using a shrink animation. 288 void AnimateShowWindow_Drop(aura::Window* window) { 289 AnimateShowWindowCommon(window, GetScaleForWindow(window), gfx::Transform()); 290 } 291 292 void AnimateHideWindow_Drop(aura::Window* window) { 293 AnimateHideWindowCommon(window, GetScaleForWindow(window)); 294 } 295 296 // Show/Hide windows using a vertical Glenimation. 297 void AnimateShowWindow_Vertical(aura::Window* window) { 298 gfx::Transform transform; 299 transform.Translate(0, window->GetProperty( 300 kWindowVisibilityAnimationVerticalPositionKey)); 301 AnimateShowWindowCommon(window, transform, gfx::Transform()); 302 } 303 304 void AnimateHideWindow_Vertical(aura::Window* window) { 305 gfx::Transform transform; 306 transform.Translate(0, window->GetProperty( 307 kWindowVisibilityAnimationVerticalPositionKey)); 308 AnimateHideWindowCommon(window, transform); 309 } 310 311 // Show/Hide windows using a fade. 312 void AnimateShowWindow_Fade(aura::Window* window) { 313 AnimateShowWindowCommon(window, gfx::Transform(), gfx::Transform()); 314 } 315 316 void AnimateHideWindow_Fade(aura::Window* window) { 317 AnimateHideWindowCommon(window, gfx::Transform()); 318 } 319 320 ui::LayerAnimationElement* CreateGrowShrinkElement( 321 aura::Window* window, bool grow) { 322 scoped_ptr<ui::InterpolatedTransform> scale(new ui::InterpolatedScale( 323 gfx::Point3F(kWindowAnimation_Bounce_Scale, 324 kWindowAnimation_Bounce_Scale, 325 1), 326 gfx::Point3F(1, 1, 1))); 327 scoped_ptr<ui::InterpolatedTransform> scale_about_pivot( 328 new ui::InterpolatedTransformAboutPivot( 329 gfx::Point(window->bounds().width() * 0.5, 330 window->bounds().height() * 0.5), 331 scale.release())); 332 scale_about_pivot->SetReversed(grow); 333 scoped_ptr<ui::LayerAnimationElement> transition( 334 ui::LayerAnimationElement::CreateInterpolatedTransformElement( 335 scale_about_pivot.release(), 336 base::TimeDelta::FromMilliseconds( 337 kWindowAnimation_Bounce_DurationMS * 338 kWindowAnimation_Bounce_GrowShrinkDurationPercent / 100))); 339 transition->set_tween_type(grow ? ui::Tween::EASE_OUT : ui::Tween::EASE_IN); 340 return transition.release(); 341 } 342 343 void AnimateBounce(aura::Window* window) { 344 ui::ScopedLayerAnimationSettings scoped_settings( 345 window->layer()->GetAnimator()); 346 scoped_settings.SetPreemptionStrategy( 347 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS); 348 window->layer()->set_delegate(window); 349 scoped_ptr<ui::LayerAnimationSequence> sequence( 350 new ui::LayerAnimationSequence); 351 sequence->AddElement(CreateGrowShrinkElement(window, true)); 352 ui::LayerAnimationElement::AnimatableProperties paused_properties; 353 paused_properties.insert(ui::LayerAnimationElement::BOUNDS); 354 sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement( 355 paused_properties, 356 base::TimeDelta::FromMilliseconds( 357 kWindowAnimation_Bounce_DurationMS * 358 (100 - 2 * kWindowAnimation_Bounce_GrowShrinkDurationPercent) / 359 100))); 360 sequence->AddElement(CreateGrowShrinkElement(window, false)); 361 window->layer()->GetAnimator()->StartAnimation(sequence.release()); 362 } 363 364 void AddLayerAnimationsForRotate(aura::Window* window, bool show) { 365 window->layer()->set_delegate(window); 366 if (show) 367 window->layer()->SetOpacity(kWindowAnimation_HideOpacity); 368 369 base::TimeDelta duration = base::TimeDelta::FromMilliseconds( 370 kWindowAnimation_Rotate_DurationMS); 371 372 if (!show) { 373 new HidingWindowAnimationObserver(window); 374 window->layer()->GetAnimator()->SchedulePauseForProperties( 375 duration * (100 - kWindowAnimation_Rotate_OpacityDurationPercent) / 100, 376 ui::LayerAnimationElement::OPACITY, 377 -1); 378 } 379 scoped_ptr<ui::LayerAnimationElement> opacity( 380 ui::LayerAnimationElement::CreateOpacityElement( 381 show ? kWindowAnimation_ShowOpacity : kWindowAnimation_HideOpacity, 382 duration * kWindowAnimation_Rotate_OpacityDurationPercent / 100)); 383 opacity->set_tween_type(ui::Tween::EASE_IN_OUT); 384 window->layer()->GetAnimator()->ScheduleAnimation( 385 new ui::LayerAnimationSequence(opacity.release())); 386 387 float xcenter = window->bounds().width() * 0.5; 388 389 gfx::Transform transform; 390 transform.Translate(xcenter, 0); 391 transform.ApplyPerspectiveDepth(kWindowAnimation_Rotate_PerspectiveDepth); 392 transform.Translate(-xcenter, 0); 393 scoped_ptr<ui::InterpolatedTransform> perspective( 394 new ui::InterpolatedConstantTransform(transform)); 395 396 scoped_ptr<ui::InterpolatedTransform> scale( 397 new ui::InterpolatedScale(1, kWindowAnimation_Rotate_ScaleFactor)); 398 scoped_ptr<ui::InterpolatedTransform> scale_about_pivot( 399 new ui::InterpolatedTransformAboutPivot( 400 gfx::Point(xcenter, kWindowAnimation_Rotate_TranslateY), 401 scale.release())); 402 403 scoped_ptr<ui::InterpolatedTransform> translation( 404 new ui::InterpolatedTranslation(gfx::Point(), gfx::Point( 405 0, kWindowAnimation_Rotate_TranslateY))); 406 407 scoped_ptr<ui::InterpolatedTransform> rotation( 408 new ui::InterpolatedAxisAngleRotation( 409 gfx::Vector3dF(1, 0, 0), 0, kWindowAnimation_Rotate_DegreesX)); 410 411 scale_about_pivot->SetChild(perspective.release()); 412 translation->SetChild(scale_about_pivot.release()); 413 rotation->SetChild(translation.release()); 414 rotation->SetReversed(show); 415 416 scoped_ptr<ui::LayerAnimationElement> transition( 417 ui::LayerAnimationElement::CreateInterpolatedTransformElement( 418 rotation.release(), duration)); 419 420 window->layer()->GetAnimator()->ScheduleAnimation( 421 new ui::LayerAnimationSequence(transition.release())); 422 } 423 424 void AnimateShowWindow_Rotate(aura::Window* window) { 425 AddLayerAnimationsForRotate(window, true); 426 } 427 428 void AnimateHideWindow_Rotate(aura::Window* window) { 429 AddLayerAnimationsForRotate(window, false); 430 } 431 432 bool AnimateShowWindow(aura::Window* window) { 433 if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_SHOW)) { 434 if (HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE)) { 435 // Since hide animation may have changed opacity and transform, 436 // reset them to show the window. 437 window->layer()->set_delegate(window); 438 window->layer()->SetOpacity(kWindowAnimation_ShowOpacity); 439 window->layer()->SetTransform(gfx::Transform()); 440 } 441 return false; 442 } 443 444 switch (GetWindowVisibilityAnimationType(window)) { 445 case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP: 446 AnimateShowWindow_Drop(window); 447 return true; 448 case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL: 449 AnimateShowWindow_Vertical(window); 450 return true; 451 case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE: 452 AnimateShowWindow_Fade(window); 453 return true; 454 case WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE: 455 AnimateShowWindow_Rotate(window); 456 return true; 457 default: 458 return false; 459 } 460 } 461 462 bool AnimateHideWindow(aura::Window* window) { 463 if (!HasWindowVisibilityAnimationTransition(window, ANIMATE_HIDE)) { 464 if (HasWindowVisibilityAnimationTransition(window, ANIMATE_SHOW)) { 465 // Since show animation may have changed opacity and transform, 466 // reset them, though the change should be hidden. 467 window->layer()->set_delegate(NULL); 468 window->layer()->SetOpacity(kWindowAnimation_HideOpacity); 469 window->layer()->SetTransform(gfx::Transform()); 470 } 471 return false; 472 } 473 474 switch (GetWindowVisibilityAnimationType(window)) { 475 case WINDOW_VISIBILITY_ANIMATION_TYPE_DROP: 476 AnimateHideWindow_Drop(window); 477 return true; 478 case WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL: 479 AnimateHideWindow_Vertical(window); 480 return true; 481 case WINDOW_VISIBILITY_ANIMATION_TYPE_FADE: 482 AnimateHideWindow_Fade(window); 483 return true; 484 case WINDOW_VISIBILITY_ANIMATION_TYPE_ROTATE: 485 AnimateHideWindow_Rotate(window); 486 return true; 487 default: 488 return false; 489 } 490 } 491 492 } // namespace 493 494 //////////////////////////////////////////////////////////////////////////////// 495 // External interface 496 497 void SetWindowVisibilityAnimationType(aura::Window* window, int type) { 498 window->SetProperty(kWindowVisibilityAnimationTypeKey, type); 499 } 500 501 int GetWindowVisibilityAnimationType(aura::Window* window) { 502 return window->GetProperty(kWindowVisibilityAnimationTypeKey); 503 } 504 505 void SetWindowVisibilityAnimationTransition( 506 aura::Window* window, 507 WindowVisibilityAnimationTransition transition) { 508 window->SetProperty(kWindowVisibilityAnimationTransitionKey, transition); 509 } 510 511 bool HasWindowVisibilityAnimationTransition( 512 aura::Window* window, 513 WindowVisibilityAnimationTransition transition) { 514 WindowVisibilityAnimationTransition prop = window->GetProperty( 515 kWindowVisibilityAnimationTransitionKey); 516 return (prop & transition) != 0; 517 } 518 519 void SetWindowVisibilityAnimationDuration(aura::Window* window, 520 const TimeDelta& duration) { 521 window->SetProperty(kWindowVisibilityAnimationDurationKey, 522 static_cast<int>(duration.ToInternalValue())); 523 } 524 525 void SetWindowVisibilityAnimationVerticalPosition(aura::Window* window, 526 float position) { 527 window->SetProperty(kWindowVisibilityAnimationVerticalPositionKey, position); 528 } 529 530 ui::ImplicitAnimationObserver* CreateHidingWindowAnimationObserver( 531 aura::Window* window) { 532 return new HidingWindowAnimationObserver(window); 533 } 534 535 bool AnimateOnChildWindowVisibilityChanged(aura::Window* window, bool visible) { 536 if (WindowAnimationsDisabled(window)) 537 return false; 538 if (visible) 539 return AnimateShowWindow(window); 540 // Don't start hiding the window again if it's already being hidden. 541 return window->layer()->GetTargetOpacity() != 0.0f && 542 AnimateHideWindow(window); 543 } 544 545 bool AnimateWindow(aura::Window* window, WindowAnimationType type) { 546 switch (type) { 547 case WINDOW_ANIMATION_TYPE_BOUNCE: 548 AnimateBounce(window); 549 return true; 550 default: 551 NOTREACHED(); 552 return false; 553 } 554 } 555 556 bool WindowAnimationsDisabled(aura::Window* window) { 557 return (window && 558 window->GetProperty(aura::client::kAnimationsDisabledKey)) || 559 CommandLine::ForCurrentProcess()->HasSwitch( 560 switches::kWindowAnimationsDisabled); 561 } 562 563 } // namespace corewm 564 } // namespace views 565