Fixes in the logic for dismissing the GestureNav overlay.
[chromium-blink-merge.git] / content / browser / web_contents / aura / window_slider.cc
blob14c3efbe7861ee0e589c3b95895178018541f525
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.
5 #include "content/browser/web_contents/aura/window_slider.h"
7 #include <algorithm>
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"
18 namespace content {
20 namespace {
22 void DeleteLayerAndShadow(ui::Layer* layer,
23 ShadowLayerDelegate* shadow) {
24 delete shadow;
25 delete layer;
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:
32 CallbackAnimationObserver(const base::Closure& closure)
33 : closure_(closure) {
36 virtual ~CallbackAnimationObserver() {}
38 private:
39 // Overridden from ui::ImplicitAnimationObserver:
40 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
41 if (!closure_.is_null())
42 closure_.Run();
43 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
46 const base::Closure closure_;
48 DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
51 } // namespace
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);
70 event_window_->AddObserver(this);
71 owner_->AddObserver(this);
74 WindowSlider::~WindowSlider() {
75 if (event_window_) {
76 event_window_->RemovePreTargetHandler(this);
77 event_window_->RemoveObserver(this);
79 if (owner_)
80 owner_->RemoveObserver(this);
81 delegate_->OnWindowSliderDestroyed();
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);
94 bool WindowSlider::IsSlideInProgress() const {
95 // if active_start_threshold_ is 0, it means that sliding hasn't been started
96 return active_start_threshold_ != 0 &&
97 (fabs(delta_x_) >= active_start_threshold_ || slider_.get() ||
98 weak_factory_.HasWeakPtrs());
101 void WindowSlider::SetupSliderLayer() {
102 ui::Layer* parent = owner_->layer()->parent();
103 parent->Add(slider_.get());
104 if (delta_x_ < 0)
105 parent->StackAbove(slider_.get(), owner_->layer());
106 else
107 parent->StackBelow(slider_.get(), owner_->layer());
108 slider_->SetBounds(owner_->layer()->bounds());
109 slider_->SetVisible(true);
112 void WindowSlider::UpdateForScroll(float x_offset, float y_offset) {
113 float old_delta = delta_x_;
114 delta_x_ += x_offset;
115 if (fabs(delta_x_) < active_start_threshold_ && !slider_.get())
116 return;
118 if ((old_delta < 0 && delta_x_ > 0) ||
119 (old_delta > 0 && delta_x_ < 0)) {
120 slider_.reset();
121 shadow_.reset();
124 float translate = 0.f;
125 ui::Layer* translate_layer = NULL;
127 if (!slider_.get()) {
128 slider_.reset(delta_x_ < 0 ? delegate_->CreateFrontLayer() :
129 delegate_->CreateBackLayer());
130 if (!slider_.get())
131 return;
132 SetupSliderLayer();
135 if (delta_x_ <= -active_start_threshold_) {
136 translate = owner_->bounds().width() +
137 std::max(delta_x_ + active_start_threshold_,
138 static_cast<float>(-owner_->bounds().width()));
139 translate_layer = slider_.get();
140 } else if (delta_x_ >= active_start_threshold_) {
141 translate = std::min(delta_x_ - active_start_threshold_,
142 static_cast<float>(owner_->bounds().width()));
143 translate_layer = owner_->layer();
144 } else {
145 return;
148 if (!shadow_.get())
149 shadow_.reset(new ShadowLayerDelegate(translate_layer));
151 gfx::Transform transform;
152 transform.Translate(translate, 0);
153 translate_layer->SetTransform(transform);
156 void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) {
157 if (!slider_.get())
158 return;
160 int width = owner_->bounds().width();
161 float ratio = (fabs(delta_x_) - active_start_threshold_) / width;
162 if (ratio < complete_threshold_) {
163 ResetScroll();
164 return;
167 ui::Layer* sliding = delta_x_ < 0 ? slider_.get() : owner_->layer();
168 ui::ScopedLayerAnimationSettings settings(sliding->GetAnimator());
169 settings.SetPreemptionStrategy(
170 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
171 settings.SetTweenType(gfx::Tween::EASE_OUT);
172 settings.AddObserver(new CallbackAnimationObserver(
173 base::Bind(&WindowSlider::CompleteWindowSlideAfterAnimation,
174 weak_factory_.GetWeakPtr())));
176 gfx::Transform transform;
177 transform.Translate(delta_x_ < 0 ? 0 : width, 0);
178 sliding->SetTransform(transform);
181 void WindowSlider::ResetScroll() {
182 if (!slider_.get())
183 return;
185 // Do not trigger any callbacks if this animation replaces any in-progress
186 // animation.
187 weak_factory_.InvalidateWeakPtrs();
189 // Reset the state of the sliding layer.
190 if (slider_.get()) {
191 ui::Layer* layer = slider_.release();
192 ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
193 settings.SetPreemptionStrategy(
194 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
195 settings.SetTweenType(gfx::Tween::EASE_OUT);
197 // Delete the layer and the shadow at the end of the animation.
198 settings.AddObserver(new CallbackAnimationObserver(
199 base::Bind(&DeleteLayerAndShadow,
200 base::Unretained(layer),
201 base::Unretained(shadow_.release()))));
203 gfx::Transform transform;
204 transform.Translate(delta_x_ < 0 ? layer->bounds().width() : 0, 0);
205 layer->SetTransform(transform);
208 // Reset the state of the main layer.
210 ui::ScopedLayerAnimationSettings settings(owner_->layer()->GetAnimator());
211 settings.SetPreemptionStrategy(
212 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
213 settings.SetTweenType(gfx::Tween::EASE_OUT);
214 settings.AddObserver(new CallbackAnimationObserver(
215 base::Bind(&WindowSlider::AbortWindowSlideAfterAnimation,
216 weak_factory_.GetWeakPtr())));
217 owner_->layer()->SetTransform(gfx::Transform());
218 owner_->layer()->SetLayerBrightness(0.f);
221 delta_x_ = 0.f;
224 void WindowSlider::CancelScroll() {
225 ResetScroll();
228 void WindowSlider::CompleteWindowSlideAfterAnimation() {
229 weak_factory_.InvalidateWeakPtrs();
230 shadow_.reset();
231 slider_.reset();
232 delta_x_ = 0.f;
234 delegate_->OnWindowSlideComplete();
237 void WindowSlider::AbortWindowSlideAfterAnimation() {
238 weak_factory_.InvalidateWeakPtrs();
240 delegate_->OnWindowSlideAborted();
243 void WindowSlider::OnKeyEvent(ui::KeyEvent* event) {
244 CancelScroll();
247 void WindowSlider::OnMouseEvent(ui::MouseEvent* event) {
248 if (!(event->flags() & ui::EF_IS_SYNTHESIZED))
249 CancelScroll();
252 void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) {
253 active_start_threshold_ = start_threshold_touchpad_;
254 if (event->type() == ui::ET_SCROLL)
255 UpdateForScroll(event->x_offset_ordinal(), event->y_offset_ordinal());
256 else if (event->type() == ui::ET_SCROLL_FLING_START)
257 UpdateForFling(event->x_offset_ordinal(), event->y_offset_ordinal());
258 else
259 CancelScroll();
260 event->SetHandled();
263 void WindowSlider::OnGestureEvent(ui::GestureEvent* event) {
264 active_start_threshold_ = start_threshold_touchscreen_;
265 const ui::GestureEventDetails& details = event->details();
266 switch (event->type()) {
267 case ui::ET_GESTURE_SCROLL_BEGIN:
268 ResetScroll();
269 break;
271 case ui::ET_GESTURE_SCROLL_UPDATE:
272 UpdateForScroll(details.scroll_x(), details.scroll_y());
273 break;
275 case ui::ET_GESTURE_SCROLL_END:
276 UpdateForFling(0.f, 0.f);
277 break;
279 case ui::ET_SCROLL_FLING_START:
280 UpdateForFling(details.velocity_x(), details.velocity_y());
281 break;
283 case ui::ET_GESTURE_PINCH_BEGIN:
284 case ui::ET_GESTURE_PINCH_UPDATE:
285 case ui::ET_GESTURE_PINCH_END:
286 CancelScroll();
287 break;
289 default:
290 break;
293 event->SetHandled();
296 void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window* window) {
297 if (window == event_window_) {
298 window->RemoveObserver(this);
299 window->RemovePreTargetHandler(this);
300 event_window_ = NULL;
301 } else if (window == owner_) {
302 window->RemoveObserver(this);
303 owner_ = NULL;
304 delete this;
305 } else {
306 NOTREACHED();
310 } // namespace content