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 "ash/rotator/screen_rotation_animator.h"
10 #include "ash/display/display_info.h"
11 #include "ash/display/display_manager.h"
12 #include "ash/display/window_tree_host_manager.h"
13 #include "ash/rotator/screen_rotation_animation.h"
14 #include "ash/shell.h"
15 #include "base/command_line.h"
16 #include "base/time/time.h"
17 #include "ui/aura/window.h"
18 #include "ui/compositor/layer.h"
19 #include "ui/compositor/layer_animation_observer.h"
20 #include "ui/compositor/layer_animation_sequence.h"
21 #include "ui/compositor/layer_animator.h"
22 #include "ui/compositor/layer_owner.h"
23 #include "ui/compositor/layer_tree_owner.h"
24 #include "ui/gfx/animation/tween.h"
25 #include "ui/gfx/display.h"
26 #include "ui/gfx/geometry/point.h"
27 #include "ui/gfx/geometry/rect.h"
28 #include "ui/gfx/geometry/rect_f.h"
29 #include "ui/gfx/transform.h"
30 #include "ui/gfx/transform_util.h"
31 #include "ui/wm/core/window_util.h"
37 // The number of degrees that the rotation animations animate through.
38 const int kRotationDegrees
= 20;
40 // The time it takes for the rotation animations to run.
41 const int kRotationDurationInMs
= 250;
43 // Gets the current display rotation for the display with the specified
45 gfx::Display::Rotation
GetCurrentRotation(int64 display_id
) {
46 return Shell::GetInstance()
48 ->GetDisplayInfo(display_id
)
52 // Returns true if the rotation between |initial_rotation| and |new_rotation| is
54 bool Is180DegreeFlip(gfx::Display::Rotation initial_rotation
,
55 gfx::Display::Rotation new_rotation
) {
56 return (initial_rotation
+ 2) % 4 == new_rotation
;
59 // A LayerAnimationObserver that will destroy the contained LayerTreeOwner when
60 // notified that a layer animation has ended or was aborted.
61 class LayerCleanupObserver
: public ui::LayerAnimationObserver
{
63 explicit LayerCleanupObserver(
64 scoped_ptr
<ui::LayerTreeOwner
> layer_tree_owner
);
65 ~LayerCleanupObserver() override
;
67 // Get the root layer of the owned layer tree.
68 ui::Layer
* GetRootLayer();
70 // ui::LayerAnimationObserver:
71 void OnLayerAnimationEnded(ui::LayerAnimationSequence
* sequence
) override
;
72 void OnLayerAnimationAborted(ui::LayerAnimationSequence
* sequence
) override
;
73 void OnLayerAnimationScheduled(
74 ui::LayerAnimationSequence
* sequence
) override
{}
77 // ui::LayerAnimationObserver:
78 bool RequiresNotificationWhenAnimatorDestroyed() const override
{
81 void OnAttachedToSequence(ui::LayerAnimationSequence
* sequence
) override
;
82 void OnDetachedFromSequence(ui::LayerAnimationSequence
* sequence
) override
;
85 // Aborts the active animations of the layer, and recurses upon its child
87 void AbortAnimations(ui::Layer
* layer
);
89 // The owned layer tree.
90 scoped_ptr
<ui::LayerTreeOwner
> layer_tree_owner_
;
92 // The LayerAnimationSequence that |this| has been attached to. Defaults to
94 ui::LayerAnimationSequence
* sequence_
;
96 DISALLOW_COPY_AND_ASSIGN(LayerCleanupObserver
);
99 LayerCleanupObserver::LayerCleanupObserver(
100 scoped_ptr
<ui::LayerTreeOwner
> layer_tree_owner
)
101 : layer_tree_owner_(layer_tree_owner
.Pass()), sequence_(nullptr) {
104 LayerCleanupObserver::~LayerCleanupObserver() {
105 // We must eplicitly detach from |sequence_| because we return true from
106 // RequiresNotificationWhenAnimatorDestroyed.
108 sequence_
->RemoveObserver(this);
109 AbortAnimations(layer_tree_owner_
->root());
112 ui::Layer
* LayerCleanupObserver::GetRootLayer() {
113 return layer_tree_owner_
->root();
116 void LayerCleanupObserver::OnLayerAnimationEnded(
117 ui::LayerAnimationSequence
* sequence
) {
121 void LayerCleanupObserver::OnLayerAnimationAborted(
122 ui::LayerAnimationSequence
* sequence
) {
126 void LayerCleanupObserver::OnAttachedToSequence(
127 ui::LayerAnimationSequence
* sequence
) {
128 sequence_
= sequence
;
131 void LayerCleanupObserver::OnDetachedFromSequence(
132 ui::LayerAnimationSequence
* sequence
) {
133 DCHECK_EQ(sequence
, sequence_
);
137 void LayerCleanupObserver::AbortAnimations(ui::Layer
* layer
) {
138 for (ui::Layer
* child_layer
: layer
->children())
139 AbortAnimations(child_layer
);
140 layer
->GetAnimator()->AbortAllAnimations();
143 // Set the screen orientation for the given |display_id| to |new_rotation| and
144 // animate the change. The animation will rotate the initial orientation's
145 // layer towards the new orientation through |rotation_degrees| while fading
146 // out, and the new orientation's layer will be rotated in to the
147 // |new_orientation| through |rotation_degrees| arc.
148 void RotateScreen(int64 display_id
,
149 gfx::Display::Rotation new_rotation
,
150 gfx::Display::RotationSource source
) {
151 aura::Window
* root_window
= Shell::GetInstance()
152 ->window_tree_host_manager()
153 ->GetRootWindowForDisplayId(display_id
);
155 const gfx::Display::Rotation initial_orientation
=
156 GetCurrentRotation(display_id
);
158 const gfx::Rect original_screen_bounds
= root_window
->GetTargetBounds();
159 // 180 degree rotations should animate clock-wise.
160 const int rotation_factor
=
161 (initial_orientation
+ 3) % 4 == new_rotation
? 1 : -1;
163 const int old_layer_initial_rotation_degrees
=
164 (Is180DegreeFlip(initial_orientation
, new_rotation
) ? 180 : 90);
166 const base::TimeDelta duration
=
167 base::TimeDelta::FromMilliseconds(kRotationDurationInMs
);
169 const gfx::Tween::Type tween_type
= gfx::Tween::FAST_OUT_LINEAR_IN
;
171 scoped_ptr
<ui::LayerTreeOwner
> old_layer_tree
=
172 wm::RecreateLayers(root_window
);
174 // Add the cloned layer tree in to the root, so it will be rendered.
175 root_window
->layer()->Add(old_layer_tree
->root());
176 root_window
->layer()->StackAtTop(old_layer_tree
->root());
178 scoped_ptr
<LayerCleanupObserver
> layer_cleanup_observer(
179 new LayerCleanupObserver(old_layer_tree
.Pass()));
181 Shell::GetInstance()->display_manager()->SetDisplayRotation(
182 display_id
, new_rotation
, source
);
184 const gfx::Rect rotated_screen_bounds
= root_window
->GetTargetBounds();
185 const gfx::Point pivot
= gfx::Point(rotated_screen_bounds
.width() / 2,
186 rotated_screen_bounds
.height() / 2);
188 // We must animate each non-cloned child layer individually because the cloned
189 // layer was added as a child to |root_window|'s layer so that it will be
191 // TODO(bruthig): Add a NOT_DRAWN layer in between the root_window's layer and
192 // its current children so that we only need to initiate two
193 // LayerAnimationSequences. One for the new layers and one for the old layer.
194 for (ui::Layer
* child_layer
: root_window
->layer()->children()) {
195 // Skip the cloned layer because it has a different animation.
196 if (child_layer
== layer_cleanup_observer
->GetRootLayer())
199 scoped_ptr
<ScreenRotationAnimation
> screen_rotation(
200 new ScreenRotationAnimation(
201 child_layer
, kRotationDegrees
* rotation_factor
,
202 0 /* end_degrees */, child_layer
->opacity(),
203 1.0f
/* target_opacity */, pivot
, duration
, tween_type
));
205 ui::LayerAnimator
* animator
= child_layer
->GetAnimator();
206 animator
->set_preemption_strategy(
207 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS
);
208 scoped_ptr
<ui::LayerAnimationSequence
> animation_sequence(
209 new ui::LayerAnimationSequence(screen_rotation
.release()));
210 animator
->StartAnimation(animation_sequence
.release());
213 // The old layer will also be transformed into the new orientation. We will
214 // translate it so that the old layer's center point aligns with the new
215 // orientation's center point and use that center point as the pivot for the
216 // rotation animation.
217 gfx::Transform translate_transform
;
218 translate_transform
.Translate(
219 (rotated_screen_bounds
.width() - original_screen_bounds
.width()) / 2,
220 (rotated_screen_bounds
.height() - original_screen_bounds
.height()) / 2);
221 layer_cleanup_observer
->GetRootLayer()->SetTransform(translate_transform
);
223 scoped_ptr
<ScreenRotationAnimation
> screen_rotation(
224 new ScreenRotationAnimation(
225 layer_cleanup_observer
->GetRootLayer(),
226 old_layer_initial_rotation_degrees
* rotation_factor
,
227 (old_layer_initial_rotation_degrees
- kRotationDegrees
) *
229 layer_cleanup_observer
->GetRootLayer()->opacity(),
230 0.0f
/* target_opacity */, pivot
, duration
, tween_type
));
232 ui::LayerAnimator
* animator
=
233 layer_cleanup_observer
->GetRootLayer()->GetAnimator();
234 animator
->set_preemption_strategy(
235 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS
);
236 scoped_ptr
<ui::LayerAnimationSequence
> animation_sequence(
237 new ui::LayerAnimationSequence(screen_rotation
.release()));
238 // Add an observer so that the cloned layers can be cleaned up with the
239 // animation completes/aborts.
240 animation_sequence
->AddObserver(layer_cleanup_observer
.release());
241 animator
->StartAnimation(animation_sequence
.release());
246 ScreenRotationAnimator::ScreenRotationAnimator(int64 display_id
)
247 : display_id_(display_id
) {
250 ScreenRotationAnimator::~ScreenRotationAnimator() {
253 bool ScreenRotationAnimator::CanAnimate() const {
254 return Shell::GetInstance()
256 ->GetDisplayForId(display_id_
)
260 void ScreenRotationAnimator::Rotate(gfx::Display::Rotation new_rotation
,
261 gfx::Display::RotationSource source
) {
262 const gfx::Display::Rotation current_rotation
=
263 GetCurrentRotation(display_id_
);
265 if (current_rotation
== new_rotation
)
268 RotateScreen(display_id_
, new_rotation
, source
);