1 // Copyright (c) 2013 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 "content/browser/web_contents/aura/window_slider.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "content/browser/web_contents/aura/shadow_layer_delegate.h"
12 #include "content/public/browser/overscroll_configuration.h"
13 #include "ui/aura/window.h"
14 #include "ui/compositor/layer_animation_observer.h"
15 #include "ui/compositor/scoped_layer_animation_settings.h"
16 #include "ui/events/event.h"
17
18 namespace content {
19
20 namespace {
21
DeleteLayerAndShadow(ui::Layer * layer,ShadowLayerDelegate * shadow)22 void DeleteLayerAndShadow(ui::Layer* layer,
23 ShadowLayerDelegate* shadow) {
24 delete shadow;
25 delete layer;
26 }
27
28 // An animation observer that runs a callback at the end of the animation, and
29 // destroys itself.
30 class CallbackAnimationObserver : public ui::ImplicitAnimationObserver {
31 public:
CallbackAnimationObserver(const base::Closure & closure)32 CallbackAnimationObserver(const base::Closure& closure)
33 : closure_(closure) {
34 }
35
~CallbackAnimationObserver()36 virtual ~CallbackAnimationObserver() {}
37
38 private:
39 // Overridden from ui::ImplicitAnimationObserver:
OnImplicitAnimationsCompleted()40 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
41 if (!closure_.is_null())
42 closure_.Run();
43 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
44 }
45
46 const base::Closure closure_;
47
48 DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
49 };
50
51 } // namespace
52
WindowSlider(Delegate * delegate,aura::Window * event_window,aura::Window * owner)53 WindowSlider::WindowSlider(Delegate* delegate,
54 aura::Window* event_window,
55 aura::Window* owner)
56 : delegate_(delegate),
57 event_window_(event_window),
58 owner_(owner),
59 delta_x_(0.f),
60 weak_factory_(this),
61 active_start_threshold_(0.f),
62 start_threshold_touchscreen_(content::GetOverscrollConfig(
63 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN)),
64 start_threshold_touchpad_(content::GetOverscrollConfig(
65 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD)),
66 complete_threshold_(content::GetOverscrollConfig(
67 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE)) {
68 event_window_->AddPreTargetHandler(this);
69
70 event_window_->AddObserver(this);
71 owner_->AddObserver(this);
72 }
73
~WindowSlider()74 WindowSlider::~WindowSlider() {
75 if (event_window_) {
76 event_window_->RemovePreTargetHandler(this);
77 event_window_->RemoveObserver(this);
78 }
79 if (owner_)
80 owner_->RemoveObserver(this);
81 delegate_->OnWindowSliderDestroyed();
82 }
83
ChangeOwner(aura::Window * new_owner)84 void WindowSlider::ChangeOwner(aura::Window* new_owner) {
85 if (owner_)
86 owner_->RemoveObserver(this);
87 owner_ = new_owner;
88 if (owner_) {
89 owner_->AddObserver(this);
90 UpdateForScroll(0.f, 0.f);
91 }
92 }
93
IsSlideInProgress() const94 bool WindowSlider::IsSlideInProgress() const {
95 return fabs(delta_x_) >= active_start_threshold_ || slider_.get() ||
96 weak_factory_.HasWeakPtrs();
97 }
98
SetupSliderLayer()99 void WindowSlider::SetupSliderLayer() {
100 ui::Layer* parent = owner_->layer()->parent();
101 parent->Add(slider_.get());
102 if (delta_x_ < 0)
103 parent->StackAbove(slider_.get(), owner_->layer());
104 else
105 parent->StackBelow(slider_.get(), owner_->layer());
106 slider_->SetBounds(owner_->layer()->bounds());
107 slider_->SetVisible(true);
108 }
109
UpdateForScroll(float x_offset,float y_offset)110 void WindowSlider::UpdateForScroll(float x_offset, float y_offset) {
111 float old_delta = delta_x_;
112 delta_x_ += x_offset;
113 if (fabs(delta_x_) < active_start_threshold_ && !slider_.get())
114 return;
115
116 if ((old_delta < 0 && delta_x_ > 0) ||
117 (old_delta > 0 && delta_x_ < 0)) {
118 slider_.reset();
119 shadow_.reset();
120 }
121
122 float translate = 0.f;
123 ui::Layer* translate_layer = NULL;
124
125 if (!slider_.get()) {
126 slider_.reset(delta_x_ < 0 ? delegate_->CreateFrontLayer() :
127 delegate_->CreateBackLayer());
128 if (!slider_.get())
129 return;
130 SetupSliderLayer();
131 }
132
133 if (delta_x_ <= -active_start_threshold_) {
134 translate = owner_->bounds().width() +
135 std::max(delta_x_ + active_start_threshold_,
136 static_cast<float>(-owner_->bounds().width()));
137 translate_layer = slider_.get();
138 } else if (delta_x_ >= active_start_threshold_) {
139 translate = std::min(delta_x_ - active_start_threshold_,
140 static_cast<float>(owner_->bounds().width()));
141 translate_layer = owner_->layer();
142 } else {
143 return;
144 }
145
146 if (!shadow_.get())
147 shadow_.reset(new ShadowLayerDelegate(translate_layer));
148
149 gfx::Transform transform;
150 transform.Translate(translate, 0);
151 translate_layer->SetTransform(transform);
152 }
153
UpdateForFling(float x_velocity,float y_velocity)154 void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) {
155 if (!slider_.get())
156 return;
157
158 int width = owner_->bounds().width();
159 float ratio = (fabs(delta_x_) - active_start_threshold_) / width;
160 if (ratio < complete_threshold_) {
161 ResetScroll();
162 return;
163 }
164
165 ui::Layer* sliding = delta_x_ < 0 ? slider_.get() : owner_->layer();
166 ui::ScopedLayerAnimationSettings settings(sliding->GetAnimator());
167 settings.SetPreemptionStrategy(
168 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
169 settings.SetTweenType(gfx::Tween::EASE_OUT);
170 settings.AddObserver(new CallbackAnimationObserver(
171 base::Bind(&WindowSlider::CompleteWindowSlideAfterAnimation,
172 weak_factory_.GetWeakPtr())));
173
174 gfx::Transform transform;
175 transform.Translate(delta_x_ < 0 ? 0 : width, 0);
176 sliding->SetTransform(transform);
177 }
178
ResetScroll()179 void WindowSlider::ResetScroll() {
180 if (!slider_.get())
181 return;
182
183 // Do not trigger any callbacks if this animation replaces any in-progress
184 // animation.
185 weak_factory_.InvalidateWeakPtrs();
186
187 // Reset the state of the sliding layer.
188 if (slider_.get()) {
189 ui::Layer* layer = slider_.release();
190 ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
191 settings.SetPreemptionStrategy(
192 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
193 settings.SetTweenType(gfx::Tween::EASE_OUT);
194
195 // Delete the layer and the shadow at the end of the animation.
196 settings.AddObserver(new CallbackAnimationObserver(
197 base::Bind(&DeleteLayerAndShadow,
198 base::Unretained(layer),
199 base::Unretained(shadow_.release()))));
200
201 gfx::Transform transform;
202 transform.Translate(delta_x_ < 0 ? layer->bounds().width() : 0, 0);
203 layer->SetTransform(transform);
204 }
205
206 // Reset the state of the main layer.
207 {
208 ui::ScopedLayerAnimationSettings settings(owner_->layer()->GetAnimator());
209 settings.SetPreemptionStrategy(
210 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
211 settings.SetTweenType(gfx::Tween::EASE_OUT);
212 settings.AddObserver(new CallbackAnimationObserver(
213 base::Bind(&WindowSlider::AbortWindowSlideAfterAnimation,
214 weak_factory_.GetWeakPtr())));
215 owner_->layer()->SetTransform(gfx::Transform());
216 owner_->layer()->SetLayerBrightness(0.f);
217 }
218
219 delta_x_ = 0.f;
220 }
221
CancelScroll()222 void WindowSlider::CancelScroll() {
223 ResetScroll();
224 }
225
CompleteWindowSlideAfterAnimation()226 void WindowSlider::CompleteWindowSlideAfterAnimation() {
227 weak_factory_.InvalidateWeakPtrs();
228 shadow_.reset();
229 slider_.reset();
230 delta_x_ = 0.f;
231
232 delegate_->OnWindowSlideComplete();
233 }
234
AbortWindowSlideAfterAnimation()235 void WindowSlider::AbortWindowSlideAfterAnimation() {
236 weak_factory_.InvalidateWeakPtrs();
237
238 delegate_->OnWindowSlideAborted();
239 }
240
OnKeyEvent(ui::KeyEvent * event)241 void WindowSlider::OnKeyEvent(ui::KeyEvent* event) {
242 CancelScroll();
243 }
244
OnMouseEvent(ui::MouseEvent * event)245 void WindowSlider::OnMouseEvent(ui::MouseEvent* event) {
246 if (!(event->flags() & ui::EF_IS_SYNTHESIZED))
247 CancelScroll();
248 }
249
OnScrollEvent(ui::ScrollEvent * event)250 void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) {
251 active_start_threshold_ = start_threshold_touchpad_;
252 if (event->type() == ui::ET_SCROLL)
253 UpdateForScroll(event->x_offset_ordinal(), event->y_offset_ordinal());
254 else if (event->type() == ui::ET_SCROLL_FLING_START)
255 UpdateForFling(event->x_offset_ordinal(), event->y_offset_ordinal());
256 else
257 CancelScroll();
258 event->SetHandled();
259 }
260
OnGestureEvent(ui::GestureEvent * event)261 void WindowSlider::OnGestureEvent(ui::GestureEvent* event) {
262 active_start_threshold_ = start_threshold_touchscreen_;
263 const ui::GestureEventDetails& details = event->details();
264 switch (event->type()) {
265 case ui::ET_GESTURE_SCROLL_BEGIN:
266 ResetScroll();
267 break;
268
269 case ui::ET_GESTURE_SCROLL_UPDATE:
270 UpdateForScroll(details.scroll_x(), details.scroll_y());
271 break;
272
273 case ui::ET_GESTURE_SCROLL_END:
274 UpdateForFling(0.f, 0.f);
275 break;
276
277 case ui::ET_SCROLL_FLING_START:
278 UpdateForFling(details.velocity_x(), details.velocity_y());
279 break;
280
281 case ui::ET_GESTURE_PINCH_BEGIN:
282 case ui::ET_GESTURE_PINCH_UPDATE:
283 case ui::ET_GESTURE_PINCH_END:
284 CancelScroll();
285 break;
286
287 default:
288 break;
289 }
290
291 event->SetHandled();
292 }
293
OnWindowRemovingFromRootWindow(aura::Window * window)294 void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window* window) {
295 if (window == event_window_) {
296 window->RemoveObserver(this);
297 window->RemovePreTargetHandler(this);
298 event_window_ = NULL;
299 } else if (window == owner_) {
300 window->RemoveObserver(this);
301 owner_ = NULL;
302 delete this;
303 } else {
304 NOTREACHED();
305 }
306 }
307
308 } // namespace content
309