blob: ed37b7f1da31bb7d519193ea8c29d999fb91e8a8 [file] [log] [blame]
bruthig37f9cad02015-03-12 22:28:501// 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.
4
5#include "ash/rotator/screen_rotation_animator.h"
6
7#include <string>
8#include <vector>
9
10#include "ash/ash_switches.h"
11#include "ash/display/display_controller.h"
12#include "ash/display/display_info.h"
13#include "ash/display/display_manager.h"
14#include "ash/rotator/screen_rotation_animation.h"
15#include "ash/shell.h"
16#include "base/command_line.h"
17#include "base/time/time.h"
18#include "ui/aura/window.h"
19#include "ui/compositor/layer.h"
20#include "ui/compositor/layer_animation_observer.h"
21#include "ui/compositor/layer_animation_sequence.h"
22#include "ui/compositor/layer_animator.h"
23#include "ui/compositor/layer_owner.h"
24#include "ui/compositor/layer_tree_owner.h"
25#include "ui/gfx/animation/tween.h"
26#include "ui/gfx/display.h"
27#include "ui/gfx/geometry/point.h"
28#include "ui/gfx/geometry/rect.h"
29#include "ui/gfx/geometry/rect_f.h"
30#include "ui/gfx/transform.h"
31#include "ui/gfx/transform_util.h"
32#include "ui/wm/core/window_util.h"
33
34namespace ash {
35
36namespace {
37
bruthig2ce3f232015-04-02 15:21:3538// Switch value for the default animation.
39const char kRotationAnimation_Default[] = "";
40
41// Switch value for no animation.
42const char kRotationAnimation_None[] = "none";
43
bruthig37f9cad02015-03-12 22:28:5044// Switch value for an animation that will rotate the initial orientation's
45// layer towards the new orientation and the new orientation's layer in to
46// position from the initial orientation through an arc of
47// |kPartialRotationDegrees| degrees. The initial orientation's layer will be
48// faded out as well.
bruthig2ce3f232015-04-02 15:21:3549const char kRotationAnimation_Partial[] = "partial-rotation";
bruthig37f9cad02015-03-12 22:28:5050
51// Switch value for an animation that will rotate both the initial and target
52// orientation's layers from the initial orientation into the target
53// orientation. The initial orientation's layer will be faded out as well and an
54// interpolated scaling factor is applied to the animation so that the initial
55// and target layers are both the same size throughout the animation.
bruthig2ce3f232015-04-02 15:21:3556const char kRotationAnimation_Full[] = "full-rotation";
bruthig37f9cad02015-03-12 22:28:5057
58// The number of degrees the partial rotation animations animate through.
59const int kPartialRotationDegrees = 20;
60
bruthig2ce3f232015-04-02 15:21:3561// The time it takes for the rotation animations to run.
bruthig37f9cad02015-03-12 22:28:5062const int kRotationDurationInMs = 250;
63
bruthig37f9cad02015-03-12 22:28:5064// Gets the current display rotation for the display with the specified
65// |display_id|.
66gfx::Display::Rotation GetCurrentRotation(int64 display_id) {
67 return Shell::GetInstance()
68 ->display_manager()
69 ->GetDisplayInfo(display_id)
70 .rotation();
71}
72
73// Returns true if the rotation between |initial_rotation| and |new_rotation| is
74// 180 degrees.
75bool Is180DegreeFlip(gfx::Display::Rotation initial_rotation,
76 gfx::Display::Rotation new_rotation) {
77 return (initial_rotation + 2) % 4 == new_rotation;
78}
79
80// A LayerAnimationObserver that will destroy the contained LayerTreeOwner when
81// notified that a layer animation has ended or was aborted.
82class LayerCleanupObserver : public ui::LayerAnimationObserver {
83 public:
84 explicit LayerCleanupObserver(
85 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner);
86 ~LayerCleanupObserver() override;
87
88 // Get the root layer of the owned layer tree.
89 ui::Layer* GetRootLayer();
90
91 // ui::LayerAnimationObserver:
92 void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override;
93 void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override;
94 void OnLayerAnimationScheduled(
95 ui::LayerAnimationSequence* sequence) override {}
96
97 protected:
98 // ui::LayerAnimationObserver:
99 bool RequiresNotificationWhenAnimatorDestroyed() const override {
100 return true;
101 }
102 void OnAttachedToSequence(ui::LayerAnimationSequence* sequence) override;
103 void OnDetachedFromSequence(ui::LayerAnimationSequence* sequence) override;
104
105 private:
106 // The owned layer tree.
107 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner_;
108
109 // The LayerAnimationSequence that |this| has been attached to. Defaults to
110 // nullptr.
111 ui::LayerAnimationSequence* sequence_;
112
113 DISALLOW_COPY_AND_ASSIGN(LayerCleanupObserver);
114};
115
116LayerCleanupObserver::LayerCleanupObserver(
117 scoped_ptr<ui::LayerTreeOwner> layer_tree_owner)
118 : layer_tree_owner_(layer_tree_owner.Pass()), sequence_(nullptr) {
119}
120
121LayerCleanupObserver::~LayerCleanupObserver() {
122 // We must eplicitly detach from |sequence_| because we return true from
123 // RequiresNotificationWhenAnimatorDestroyed.
124 if (sequence_)
125 sequence_->RemoveObserver(this);
126}
127
128ui::Layer* LayerCleanupObserver::GetRootLayer() {
129 return layer_tree_owner_->root();
130}
131
132void LayerCleanupObserver::OnLayerAnimationEnded(
133 ui::LayerAnimationSequence* sequence) {
134 delete this;
135}
136
137void LayerCleanupObserver::OnLayerAnimationAborted(
138 ui::LayerAnimationSequence* sequence) {
139 delete this;
140}
141
142void LayerCleanupObserver::OnAttachedToSequence(
143 ui::LayerAnimationSequence* sequence) {
144 sequence_ = sequence;
145}
146
147void LayerCleanupObserver::OnDetachedFromSequence(
148 ui::LayerAnimationSequence* sequence) {
149 DCHECK_EQ(sequence, sequence_);
150 sequence_ = nullptr;
151}
152
153// Set the screen orientation for the given |display| to |new_rotation| and
154// animate the change.
155void RotateScreen(int64 display_id,
156 gfx::Display::Rotation new_rotation,
157 base::TimeDelta duration,
158 int rotation_degrees,
159 int rotation_degree_offset,
160 gfx::Tween::Type tween_type,
161 bool should_scale) {
162 aura::Window* root_window =
163 Shell::GetInstance()->display_controller()->GetRootWindowForDisplayId(
164 display_id);
165
166 const gfx::Display::Rotation initial_orientation =
167 GetCurrentRotation(display_id);
168
169 const gfx::RectF original_screen_bounds = root_window->GetTargetBounds();
170 // 180 degree rotations should animate clock-wise.
171 const int rotation_factor =
172 (initial_orientation + 3) % 4 == new_rotation ? 1 : -1;
173
174 scoped_ptr<ui::LayerTreeOwner> old_layer_tree =
175 wm::RecreateLayers(root_window);
176
177 // Add the cloned layer tree in to the root, so it will be rendered.
178 root_window->layer()->Add(old_layer_tree->root());
179 root_window->layer()->StackAtTop(old_layer_tree->root());
180
181 scoped_ptr<LayerCleanupObserver> layer_cleanup_observer(
182 new LayerCleanupObserver(old_layer_tree.Pass()));
183
184 Shell::GetInstance()->display_manager()->SetDisplayRotation(display_id,
185 new_rotation);
186
187 const gfx::RectF rotated_screen_bounds = root_window->GetTargetBounds();
188 const gfx::Point pivot = gfx::Point(rotated_screen_bounds.width() / 2,
189 rotated_screen_bounds.height() / 2);
190
191 gfx::Point3F new_layer_initial_scale = gfx::Point3F(1.0f, 1.0f, 1.0f);
192 gfx::Point3F old_layer_target_scale = gfx::Point3F(1.0f, 1.0f, 1.0f);
193
194 if (should_scale) {
195 new_layer_initial_scale = gfx::Point3F(
196 original_screen_bounds.width() / rotated_screen_bounds.width(),
197 original_screen_bounds.height() / rotated_screen_bounds.height(), 1.0f);
198
199 old_layer_target_scale = gfx::Point3F(
200 rotated_screen_bounds.width() / original_screen_bounds.width(),
201 rotated_screen_bounds.height() / original_screen_bounds.height(), 1.0f);
202 }
203
204 // We must animate each non-cloned child layer individually because the cloned
205 // layer was added as a child to |root_window|'s layer so that it will be
206 // rendered.
207 // TODO(bruthig): Add a NOT_DRAWN layer in between the root_window's layer and
208 // its current children so that we only need to initiate two
209 // LayerAnimationSequences. One for the new layers and one for the old layer.
210 for (ui::Layer* child_layer : root_window->layer()->children()) {
211 // Skip the cloned layer because it has a different animation.
212 if (child_layer == layer_cleanup_observer->GetRootLayer())
213 continue;
214
215 scoped_ptr<ScreenRotationAnimation> screen_rotation(
216 new ScreenRotationAnimation(
217 child_layer, rotation_degrees * rotation_factor,
218 0 /* end_degrees */, child_layer->opacity(),
219 1.0f /* target_opacity */, new_layer_initial_scale,
220 gfx::Point3F(1.0f, 1.0f, 1.0f), pivot, duration, tween_type));
221
222 ui::LayerAnimator* animator = child_layer->GetAnimator();
223 animator->set_preemption_strategy(
224 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
225 scoped_ptr<ui::LayerAnimationSequence> animation_sequence(
226 new ui::LayerAnimationSequence(screen_rotation.release()));
227 animator->StartAnimation(animation_sequence.release());
228 }
229
230 // The old layer will also be transformed into the new orientation. We will
231 // translate it so that the old layer's center point aligns with the new
232 // orientation's center point and use that center point as the pivot for the
233 // rotation animation.
234 gfx::Transform translate_transform;
235 translate_transform.Translate(
236 (rotated_screen_bounds.width() - original_screen_bounds.width()) / 2,
237 (rotated_screen_bounds.height() - original_screen_bounds.height()) / 2);
238 layer_cleanup_observer->GetRootLayer()->SetTransform(translate_transform);
239
240 scoped_ptr<ScreenRotationAnimation> screen_rotation(
241 new ScreenRotationAnimation(
242 layer_cleanup_observer->GetRootLayer(),
243 (rotation_degrees + rotation_degree_offset) * rotation_factor,
244 rotation_degree_offset * rotation_factor,
245 layer_cleanup_observer->GetRootLayer()->opacity(),
246 0.0f /* target_opacity */, gfx::Point3F(1.0f, 1.0f, 1.0f),
247 old_layer_target_scale, pivot, duration, tween_type));
248
249 ui::LayerAnimator* animator =
250 layer_cleanup_observer->GetRootLayer()->GetAnimator();
251 animator->set_preemption_strategy(
252 ui::LayerAnimator::REPLACE_QUEUED_ANIMATIONS);
253 scoped_ptr<ui::LayerAnimationSequence> animation_sequence(
254 new ui::LayerAnimationSequence(screen_rotation.release()));
255 // Add an observer so that the cloned layers can be cleaned up with the
256 // animation completes/aborts.
257 animation_sequence->AddObserver(layer_cleanup_observer.release());
258 animator->StartAnimation(animation_sequence.release());
259}
260
261} // namespace
262
263ScreenRotationAnimator::ScreenRotationAnimator(int64 display_id)
264 : display_id_(display_id) {
265}
266
267ScreenRotationAnimator::~ScreenRotationAnimator() {
268}
269
270void ScreenRotationAnimator::Rotate(gfx::Display::Rotation new_rotation) {
271 const gfx::Display::Rotation current_rotation =
272 GetCurrentRotation(display_id_);
273
274 if (current_rotation == new_rotation)
275 return;
276
277 const std::string switch_value =
278 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
279 switches::kAshEnableScreenRotationAnimation);
280
bruthig2ce3f232015-04-02 15:21:35281 if (switch_value == kRotationAnimation_None) {
bruthig37f9cad02015-03-12 22:28:50282 Shell::GetInstance()->display_manager()->SetDisplayRotation(display_id_,
283 new_rotation);
bruthig2ce3f232015-04-02 15:21:35284 } else if (kRotationAnimation_Default == switch_value ||
285 kRotationAnimation_Partial == switch_value) {
bruthig37f9cad02015-03-12 22:28:50286 const int rotation_degree_offset =
287 Is180DegreeFlip(current_rotation, new_rotation)
288 ? 180 - kPartialRotationDegrees
289 : 90 - kPartialRotationDegrees;
290
291 RotateScreen(display_id_, new_rotation,
bruthig2ce3f232015-04-02 15:21:35292 base::TimeDelta::FromMilliseconds(kRotationDurationInMs),
bruthig37f9cad02015-03-12 22:28:50293 kPartialRotationDegrees, rotation_degree_offset,
294 gfx::Tween::FAST_OUT_LINEAR_IN, false /* should_scale */);
bruthig2ce3f232015-04-02 15:21:35295 } else if (kRotationAnimation_Full == switch_value) {
bruthig37f9cad02015-03-12 22:28:50296 const int rotation_degrees =
297 Is180DegreeFlip(current_rotation, new_rotation) ? 180 : 90;
298
299 RotateScreen(display_id_, new_rotation,
bruthig2ce3f232015-04-02 15:21:35300 base::TimeDelta::FromMilliseconds(kRotationDurationInMs),
bruthig37f9cad02015-03-12 22:28:50301 rotation_degrees, 0 /* rotation_degree_offset */,
302 gfx::Tween::FAST_OUT_LINEAR_IN, true /* should_scale */);
303 } else {
304 NOTREACHED();
305 }
306}
307
308} // namespace ash