window_slider.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file.
4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/browser/web_contents/aura/window_slider.h"
6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <algorithm>
846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "base/bind.h"
10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/callback.h"
1146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "content/browser/web_contents/aura/shadow_layer_delegate.h"
12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/public/browser/overscroll_configuration.h"
13f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "ui/aura/window.h"
1446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/compositor/layer_animation_observer.h"
15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "ui/compositor/scoped_layer_animation_settings.h"
16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "ui/events/event.h"
17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)namespace content {
1946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
2046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)namespace {
2146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
2246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void DeleteLayerAndShadow(ui::Layer* layer,
23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                          ShadowLayerDelegate* shadow) {
24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  delete shadow;
25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  delete layer;
2646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
2746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// An animation observer that runs a callback at the end of the animation, and
2946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// destroys itself.
3046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)class CallbackAnimationObserver : public ui::ImplicitAnimationObserver {
3146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) public:
3246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  CallbackAnimationObserver(const base::Closure& closure)
3346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      : closure_(closure) {
3446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
3546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
3646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  virtual ~CallbackAnimationObserver() {}
3746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
3846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) private:
3946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  // Overridden from ui::ImplicitAnimationObserver:
4046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  virtual void OnImplicitAnimationsCompleted() OVERRIDE {
4146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    if (!closure_.is_null())
4246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      closure_.Run();
4346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
4446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
4546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
4646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  const base::Closure closure_;
47f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
48f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
49f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
50f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}  // namespace
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
53116680a4aac90f2aa7413d9095a592090648e557Ben MurdochWindowSlider::WindowSlider(Delegate* delegate,
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                           aura::Window* event_window,
5546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                           aura::Window* owner)
56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    : delegate_(delegate),
57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      event_window_(event_window),
58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      owner_(owner),
59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      delta_x_(0.f),
60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      weak_factory_(this),
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      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
74WindowSlider::~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
84void 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
94bool WindowSlider::IsSlideInProgress() const {
95  return fabs(delta_x_) >= active_start_threshold_ || slider_.get() ||
96      weak_factory_.HasWeakPtrs();
97}
98
99void 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
110void 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
154void 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
179void 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
222void WindowSlider::CancelScroll() {
223  ResetScroll();
224}
225
226void WindowSlider::CompleteWindowSlideAfterAnimation() {
227  weak_factory_.InvalidateWeakPtrs();
228  shadow_.reset();
229  slider_.reset();
230  delta_x_ = 0.f;
231
232  delegate_->OnWindowSlideComplete();
233}
234
235void WindowSlider::AbortWindowSlideAfterAnimation() {
236  weak_factory_.InvalidateWeakPtrs();
237
238  delegate_->OnWindowSlideAborted();
239}
240
241void WindowSlider::OnKeyEvent(ui::KeyEvent* event) {
242  CancelScroll();
243}
244
245void WindowSlider::OnMouseEvent(ui::MouseEvent* event) {
246  if (!(event->flags() & ui::EF_IS_SYNTHESIZED))
247    CancelScroll();
248}
249
250void 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
261void 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
294void 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