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