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