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 "ash/wm/window_animations.h"
6
7#include "ash/shell_window_ids.h"
8#include "ash/test/ash_test_base.h"
9#include "ash/wm/window_state.h"
10#include "ash/wm/workspace_controller.h"
11#include "base/time/time.h"
12#include "ui/aura/test/test_windows.h"
13#include "ui/aura/window.h"
14#include "ui/compositor/layer.h"
15#include "ui/compositor/layer_animation_observer.h"
16#include "ui/compositor/layer_animator.h"
17#include "ui/compositor/scoped_animation_duration_scale_mode.h"
18#include "ui/compositor/scoped_layer_animation_settings.h"
19#include "ui/gfx/animation/animation_container_element.h"
20
21using aura::Window;
22using ui::Layer;
23
24namespace ash {
25namespace internal {
26
27class WindowAnimationsTest : public ash::test::AshTestBase {
28 public:
29  WindowAnimationsTest() {}
30
31  virtual void TearDown() OVERRIDE {
32    AshTestBase::TearDown();
33  }
34
35 private:
36  DISALLOW_COPY_AND_ASSIGN(WindowAnimationsTest);
37};
38
39// Listens to animation scheduled notifications. Remembers the transition
40// duration of the first sequence.
41class MinimizeAnimationObserver : public ui::LayerAnimationObserver {
42 public:
43  explicit MinimizeAnimationObserver(ui::LayerAnimator* animator)
44      : animator_(animator) {
45    animator_->AddObserver(this);
46    // RemoveObserver is called when the first animation is scheduled and so
47    // there should be no need for now to remove it in destructor.
48  };
49  base::TimeDelta duration() { return duration_; }
50
51 protected:
52  // ui::LayerAnimationObserver:
53  virtual void OnLayerAnimationScheduled(
54      ui::LayerAnimationSequence* sequence) OVERRIDE {
55    duration_ = animator_->GetTransitionDuration();
56    animator_->RemoveObserver(this);
57  }
58  virtual void OnLayerAnimationEnded(
59      ui::LayerAnimationSequence* sequence) OVERRIDE {}
60  virtual void OnLayerAnimationAborted(
61      ui::LayerAnimationSequence* sequence) OVERRIDE {}
62
63 private:
64  ui::LayerAnimator* animator_;
65  base::TimeDelta duration_;
66
67  DISALLOW_COPY_AND_ASSIGN(MinimizeAnimationObserver);
68};
69
70TEST_F(WindowAnimationsTest, HideShowBrightnessGrayscaleAnimation) {
71  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
72  window->Show();
73  EXPECT_TRUE(window->layer()->visible());
74
75  // Hiding.
76  views::corewm::SetWindowVisibilityAnimationType(
77      window.get(),
78      WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE);
79  AnimateOnChildWindowVisibilityChanged(window.get(), false);
80  EXPECT_EQ(0.0f, window->layer()->GetTargetOpacity());
81  EXPECT_FALSE(window->layer()->GetTargetVisibility());
82  EXPECT_FALSE(window->layer()->visible());
83
84  // Showing.
85  views::corewm::SetWindowVisibilityAnimationType(
86      window.get(),
87      WINDOW_VISIBILITY_ANIMATION_TYPE_BRIGHTNESS_GRAYSCALE);
88  AnimateOnChildWindowVisibilityChanged(window.get(), true);
89  EXPECT_EQ(0.0f, window->layer()->GetTargetBrightness());
90  EXPECT_EQ(0.0f, window->layer()->GetTargetGrayscale());
91  EXPECT_TRUE(window->layer()->visible());
92
93  // Stays shown.
94  gfx::AnimationContainerElement* element =
95      static_cast<gfx::AnimationContainerElement*>(
96      window->layer()->GetAnimator());
97  element->Step(base::TimeTicks::Now() +
98                base::TimeDelta::FromSeconds(5));
99  EXPECT_EQ(0.0f, window->layer()->GetTargetBrightness());
100  EXPECT_EQ(0.0f, window->layer()->GetTargetGrayscale());
101  EXPECT_TRUE(window->layer()->visible());
102}
103
104TEST_F(WindowAnimationsTest, LayerTargetVisibility) {
105  scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
106
107  // Layer target visibility changes according to Show/Hide.
108  window->Show();
109  EXPECT_TRUE(window->layer()->GetTargetVisibility());
110  window->Hide();
111  EXPECT_FALSE(window->layer()->GetTargetVisibility());
112  window->Show();
113  EXPECT_TRUE(window->layer()->GetTargetVisibility());
114}
115
116TEST_F(WindowAnimationsTest, CrossFadeToBounds) {
117  ui::ScopedAnimationDurationScaleMode normal_duration_mode(
118      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
119
120  scoped_ptr<Window> window(CreateTestWindowInShellWithId(0));
121  window->SetBounds(gfx::Rect(5, 10, 320, 240));
122  window->Show();
123
124  Layer* old_layer = window->layer();
125  EXPECT_EQ(1.0f, old_layer->GetTargetOpacity());
126
127  // Cross fade to a larger size, as in a maximize animation.
128  CrossFadeToBounds(window.get(), gfx::Rect(0, 0, 640, 480));
129  // Window's layer has been replaced.
130  EXPECT_NE(old_layer, window->layer());
131  // Original layer stays opaque and stretches to new size.
132  EXPECT_EQ(1.0f, old_layer->GetTargetOpacity());
133  EXPECT_EQ("5,10 320x240", old_layer->bounds().ToString());
134  gfx::Transform grow_transform;
135  grow_transform.Translate(-5.f, -10.f);
136  grow_transform.Scale(640.f / 320.f, 480.f / 240.f);
137  EXPECT_EQ(grow_transform, old_layer->GetTargetTransform());
138  // New layer animates in to the identity transform.
139  EXPECT_EQ(1.0f, window->layer()->GetTargetOpacity());
140  EXPECT_EQ(gfx::Transform(), window->layer()->GetTargetTransform());
141
142  // Run the animations to completion.
143  static_cast<gfx::AnimationContainerElement*>(old_layer->GetAnimator())->Step(
144      base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1));
145  static_cast<gfx::AnimationContainerElement*>(window->layer()->GetAnimator())->
146      Step(base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1));
147
148  // Cross fade to a smaller size, as in a restore animation.
149  old_layer = window->layer();
150  CrossFadeToBounds(window.get(), gfx::Rect(5, 10, 320, 240));
151  // Again, window layer has been replaced.
152  EXPECT_NE(old_layer, window->layer());
153  // Original layer fades out and stretches down to new size.
154  EXPECT_EQ(0.0f, old_layer->GetTargetOpacity());
155  EXPECT_EQ("0,0 640x480", old_layer->bounds().ToString());
156  gfx::Transform shrink_transform;
157  shrink_transform.Translate(5.f, 10.f);
158  shrink_transform.Scale(320.f / 640.f, 240.f / 480.f);
159  EXPECT_EQ(shrink_transform, old_layer->GetTargetTransform());
160  // New layer animates in to the identity transform.
161  EXPECT_EQ(1.0f, window->layer()->GetTargetOpacity());
162  EXPECT_EQ(gfx::Transform(), window->layer()->GetTargetTransform());
163
164  static_cast<gfx::AnimationContainerElement*>(old_layer->GetAnimator())->Step(
165      base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1));
166  static_cast<gfx::AnimationContainerElement*>(window->layer()->GetAnimator())->
167      Step(base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1));
168}
169
170TEST_F(WindowAnimationsTest, LockAnimationDuration) {
171  ui::ScopedAnimationDurationScaleMode normal_duration_mode(
172      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
173
174  scoped_ptr<Window> window(CreateTestWindowInShellWithId(0));
175  Layer* layer = window->layer();
176  window->SetBounds(gfx::Rect(5, 10, 320, 240));
177  window->Show();
178
179  // Test that it is possible to override transition duration when it is not
180  // locked.
181  {
182    ui::ScopedLayerAnimationSettings settings1(layer->GetAnimator());
183    settings1.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000));
184    {
185      ui::ScopedLayerAnimationSettings settings2(layer->GetAnimator());
186      // Duration is not locked so it gets overridden.
187      settings2.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
188      wm::GetWindowState(window.get())->Minimize();
189      EXPECT_TRUE(layer->GetAnimator()->is_animating());
190      // Expect duration from the inner scope
191      EXPECT_EQ(50,
192                layer->GetAnimator()->GetTransitionDuration().InMilliseconds());
193    }
194    window->Show();
195    layer->GetAnimator()->StopAnimating();
196  }
197
198  // Test that it is possible to lock transition duration
199  {
200    ui::ScopedLayerAnimationSettings settings1(layer->GetAnimator());
201    settings1.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000));
202    // Duration is locked in outer scope.
203    settings1.LockTransitionDuration();
204    {
205      ui::ScopedLayerAnimationSettings settings2(layer->GetAnimator());
206      // Transition duration setting is ignored.
207      settings2.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
208      wm::GetWindowState(window.get())->Minimize();
209      EXPECT_TRUE(layer->GetAnimator()->is_animating());
210      // Expect duration from the outer scope
211      EXPECT_EQ(1000,
212                layer->GetAnimator()->GetTransitionDuration().InMilliseconds());
213    }
214    window->Show();
215    layer->GetAnimator()->StopAnimating();
216  }
217
218  // Test that duration respects default.
219  {
220    // Query default duration.
221    MinimizeAnimationObserver observer(layer->GetAnimator());
222    wm::GetWindowState(window.get())->Minimize();
223    EXPECT_TRUE(layer->GetAnimator()->is_animating());
224    base::TimeDelta default_duration(observer.duration());
225    window->Show();
226    layer->GetAnimator()->StopAnimating();
227
228    ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
229    settings.LockTransitionDuration();
230    // Setting transition duration is ignored since duration is locked
231    settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000));
232    wm::GetWindowState(window.get())->Minimize();
233    EXPECT_TRUE(layer->GetAnimator()->is_animating());
234    // Expect default duration (200ms for stock ash minimizing animation).
235    EXPECT_EQ(default_duration.InMilliseconds(),
236              layer->GetAnimator()->GetTransitionDuration().InMilliseconds());
237    window->Show();
238    layer->GetAnimator()->StopAnimating();
239  }
240}
241
242}  // namespace internal
243}  // namespace ash
244