• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 // An animation observer that runs a callback at the end of the animation, and
23 // destroys itself.
24 class CallbackAnimationObserver : public ui::ImplicitAnimationObserver {
25  public:
CallbackAnimationObserver(const base::Closure & closure)26   CallbackAnimationObserver(const base::Closure& closure)
27       : closure_(closure) {
28   }
29 
~CallbackAnimationObserver()30   virtual ~CallbackAnimationObserver() {}
31 
32  private:
33   // Overridden from ui::ImplicitAnimationObserver:
OnImplicitAnimationsCompleted()34   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
35     if (!closure_.is_null())
36       closure_.Run();
37     base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
38   }
39 
40   const base::Closure closure_;
41 
42   DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
43 };
44 
45 }  // namespace
46 
WindowSlider(Delegate * delegate,aura::Window * event_window,aura::Window * owner)47 WindowSlider::WindowSlider(Delegate* delegate,
48                            aura::Window* event_window,
49                            aura::Window* owner)
50     : delegate_(delegate),
51       event_window_(event_window),
52       owner_(owner),
53       active_animator_(NULL),
54       delta_x_(0.f),
55       active_start_threshold_(0.f),
56       start_threshold_touchscreen_(content::GetOverscrollConfig(
57           content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN)),
58       start_threshold_touchpad_(content::GetOverscrollConfig(
59           content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD)),
60       complete_threshold_(content::GetOverscrollConfig(
61           content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE)),
62       weak_factory_(this) {
63   event_window_->AddPreTargetHandler(this);
64 
65   event_window_->AddObserver(this);
66   owner_->AddObserver(this);
67 }
68 
~WindowSlider()69 WindowSlider::~WindowSlider() {
70   if (event_window_) {
71     event_window_->RemovePreTargetHandler(this);
72     event_window_->RemoveObserver(this);
73   }
74   if (owner_)
75     owner_->RemoveObserver(this);
76   delegate_->OnWindowSliderDestroyed();
77 }
78 
ChangeOwner(aura::Window * new_owner)79 void 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 
IsSlideInProgress() const89 bool WindowSlider::IsSlideInProgress() const {
90   // if active_start_threshold_ is 0, it means that sliding hasn't been started
91   return active_start_threshold_ != 0 && (slider_.get() || active_animator_);
92 }
93 
SetupSliderLayer()94 void WindowSlider::SetupSliderLayer() {
95   ui::Layer* parent = owner_->layer()->parent();
96   parent->Add(slider_.get());
97   if (delta_x_ < 0)
98     parent->StackAbove(slider_.get(), owner_->layer());
99   else
100     parent->StackBelow(slider_.get(), owner_->layer());
101   slider_->SetBounds(owner_->layer()->bounds());
102   slider_->SetVisible(true);
103 }
104 
UpdateForScroll(float x_offset,float y_offset)105 void WindowSlider::UpdateForScroll(float x_offset, float y_offset) {
106   if (active_animator_) {
107     // If there is an active animation, complete it before processing the scroll
108     // so that the callbacks that are invoked on the Delegate are consistent.
109     // Completing the animation may destroy WindowSlider through the animation
110     // callback, so we can't continue processing the scroll event here.
111     delta_x_ += x_offset;
112     CompleteActiveAnimations();
113     return;
114   }
115 
116   float old_delta = delta_x_;
117   delta_x_ += x_offset;
118   if (fabs(delta_x_) < active_start_threshold_ && !slider_.get())
119     return;
120 
121   if ((old_delta < 0 && delta_x_ > 0) ||
122       (old_delta > 0 && delta_x_ < 0)) {
123     slider_.reset();
124     shadow_.reset();
125   }
126 
127   float translate = 0.f;
128   ui::Layer* translate_layer = NULL;
129 
130   if (!slider_.get()) {
131     slider_.reset(delta_x_ < 0 ? delegate_->CreateFrontLayer() :
132                                  delegate_->CreateBackLayer());
133     if (!slider_.get())
134       return;
135     SetupSliderLayer();
136   }
137 
138   if (delta_x_ <= -active_start_threshold_) {
139     translate = owner_->bounds().width() +
140         std::max(delta_x_ + active_start_threshold_,
141                  static_cast<float>(-owner_->bounds().width()));
142     translate_layer = slider_.get();
143   } else if (delta_x_ >= active_start_threshold_) {
144     translate = std::min(delta_x_ - active_start_threshold_,
145                          static_cast<float>(owner_->bounds().width()));
146     translate_layer = owner_->layer();
147   } else {
148     return;
149   }
150 
151   if (!shadow_.get())
152     shadow_.reset(new ShadowLayerDelegate(translate_layer));
153 
154   gfx::Transform transform;
155   transform.Translate(translate, 0);
156   translate_layer->SetTransform(transform);
157 }
158 
CompleteOrResetSlide()159 void WindowSlider::CompleteOrResetSlide() {
160   if (!slider_.get())
161     return;
162 
163   int width = owner_->bounds().width();
164   float ratio = (fabs(delta_x_) - active_start_threshold_) / width;
165   if (ratio < complete_threshold_) {
166     ResetSlide();
167     return;
168   }
169 
170   ui::Layer* sliding = delta_x_ < 0 ? slider_.get() : owner_->layer();
171   active_animator_ = sliding->GetAnimator();
172 
173   ui::ScopedLayerAnimationSettings settings(active_animator_);
174   settings.SetPreemptionStrategy(
175       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
176   settings.SetTweenType(gfx::Tween::EASE_OUT);
177   settings.AddObserver(new CallbackAnimationObserver(
178       base::Bind(&WindowSlider::SlideAnimationCompleted,
179                  weak_factory_.GetWeakPtr(),
180                  base::Passed(&slider_),
181                  base::Passed(&shadow_))));
182 
183   gfx::Transform transform;
184   transform.Translate(delta_x_ < 0 ? 0 : width, 0);
185   delta_x_ = 0;
186   delegate_->OnWindowSlideCompleting();
187   sliding->SetTransform(transform);
188 }
189 
CompleteActiveAnimations()190 void WindowSlider::CompleteActiveAnimations() {
191   if (active_animator_)
192     active_animator_->StopAnimating();
193 }
194 
ResetSlide()195 void WindowSlider::ResetSlide() {
196   if (!slider_.get())
197     return;
198 
199   // Reset the state of the sliding layer.
200   if (slider_.get()) {
201     ui::Layer* translate_layer;
202     gfx::Transform transform;
203     if (delta_x_ < 0) {
204       translate_layer = slider_.get();
205       transform.Translate(translate_layer->bounds().width(), 0);
206     } else {
207       translate_layer = owner_->layer();
208     }
209 
210     active_animator_ = translate_layer->GetAnimator();
211     ui::ScopedLayerAnimationSettings settings(active_animator_);
212     settings.SetPreemptionStrategy(
213         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
214     settings.SetTweenType(gfx::Tween::EASE_OUT);
215     settings.AddObserver(new CallbackAnimationObserver(
216         base::Bind(&WindowSlider::ResetSlideAnimationCompleted,
217                    weak_factory_.GetWeakPtr(),
218                    base::Passed(&slider_),
219                    base::Passed(&shadow_))));
220     translate_layer->SetTransform(transform);
221   }
222 
223   delta_x_ = 0.f;
224 }
225 
SlideAnimationCompleted(scoped_ptr<ui::Layer> layer,scoped_ptr<ShadowLayerDelegate> shadow)226 void WindowSlider::SlideAnimationCompleted(
227     scoped_ptr<ui::Layer> layer, scoped_ptr<ShadowLayerDelegate> shadow) {
228   active_animator_ = NULL;
229   shadow.reset();
230   delegate_->OnWindowSlideCompleted(layer.Pass());
231 }
232 
ResetSlideAnimationCompleted(scoped_ptr<ui::Layer> layer,scoped_ptr<ShadowLayerDelegate> shadow)233 void WindowSlider::ResetSlideAnimationCompleted(
234     scoped_ptr<ui::Layer> layer, scoped_ptr<ShadowLayerDelegate> shadow) {
235   active_animator_ = NULL;
236   shadow.reset();
237   layer.reset();
238   delegate_->OnWindowSlideAborted();
239 }
240 
OnKeyEvent(ui::KeyEvent * event)241 void WindowSlider::OnKeyEvent(ui::KeyEvent* event) {
242   ResetSlide();
243 }
244 
OnMouseEvent(ui::MouseEvent * event)245 void WindowSlider::OnMouseEvent(ui::MouseEvent* event) {
246   if (!(event->flags() & ui::EF_IS_SYNTHESIZED))
247     ResetSlide();
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     CompleteOrResetSlide();
256   else
257     ResetSlide();
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       CompleteActiveAnimations();
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       CompleteOrResetSlide();
275       break;
276 
277     case ui::ET_SCROLL_FLING_START:
278       CompleteOrResetSlide();
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       ResetSlide();
285       break;
286 
287     default:
288       break;
289   }
290 
291   event->SetHandled();
292 }
293 
OnWindowRemovingFromRootWindow(aura::Window * window,aura::Window * new_root)294 void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window* window,
295                                                   aura::Window* new_root) {
296   if (window == event_window_) {
297     window->RemoveObserver(this);
298     window->RemovePreTargetHandler(this);
299     event_window_ = NULL;
300   } else if (window == owner_) {
301     window->RemoveObserver(this);
302     owner_ = NULL;
303     delete this;
304   } else {
305     NOTREACHED();
306   }
307 }
308 
309 }  // namespace content
310