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"
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"
22 void DeleteLayerAndShadow(ui::Layer* layer,
23 ShadowLayerDelegate* shadow) {
28 // An animation observer that runs a callback at the end of the animation, and
30 class CallbackAnimationObserver : public ui::ImplicitAnimationObserver {
32 CallbackAnimationObserver(const base::Closure& closure)
36 virtual ~CallbackAnimationObserver() {}
39 // Overridden from ui::ImplicitAnimationObserver:
40 virtual void OnImplicitAnimationsCompleted() OVERRIDE {
41 if (!closure_.is_null())
43 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
46 const base::Closure closure_;
48 DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
53 WindowSlider::WindowSlider(Delegate* delegate,
54 aura::Window* event_window,
56 : delegate_(delegate),
57 event_window_(event_window),
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() {
76 event_window_->RemovePreTargetHandler(this);
77 event_window_->RemoveObserver(this);
80 owner_->RemoveObserver(this);
81 delegate_->OnWindowSliderDestroyed();
84 void WindowSlider::ChangeOwner(aura::Window* new_owner) {
86 owner_->RemoveObserver(this);
89 owner_->AddObserver(this);
90 UpdateForScroll(0.f, 0.f);
94 bool WindowSlider::IsSlideInProgress() const {
95 return fabs(delta_x_) >= active_start_threshold_ || slider_.get() ||
96 weak_factory_.HasWeakPtrs();
99 void WindowSlider::SetupSliderLayer() {
100 ui::Layer* parent = owner_->layer()->parent();
101 parent->Add(slider_.get());
103 parent->StackAbove(slider_.get(), owner_->layer());
105 parent->StackBelow(slider_.get(), owner_->layer());
106 slider_->SetBounds(owner_->layer()->bounds());
107 slider_->SetVisible(true);
110 void 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())
116 if ((old_delta < 0 && delta_x_ > 0) ||
117 (old_delta > 0 && delta_x_ < 0)) {
122 float translate = 0.f;
123 ui::Layer* translate_layer = NULL;
125 if (!slider_.get()) {
126 slider_.reset(delta_x_ < 0 ? delegate_->CreateFrontLayer() :
127 delegate_->CreateBackLayer());
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();
147 shadow_.reset(new ShadowLayerDelegate(translate_layer));
149 gfx::Transform transform;
150 transform.Translate(translate, 0);
151 translate_layer->SetTransform(transform);
154 void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) {
158 int width = owner_->bounds().width();
159 float ratio = (fabs(delta_x_) - active_start_threshold_) / width;
160 if (ratio < complete_threshold_) {
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())));
174 gfx::Transform transform;
175 transform.Translate(delta_x_ < 0 ? 0 : width, 0);
176 sliding->SetTransform(transform);
179 void WindowSlider::ResetScroll() {
183 // Do not trigger any callbacks if this animation replaces any in-progress
185 weak_factory_.InvalidateWeakPtrs();
187 // Reset the state of the sliding layer.
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);
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()))));
201 gfx::Transform transform;
202 transform.Translate(delta_x_ < 0 ? layer->bounds().width() : 0, 0);
203 layer->SetTransform(transform);
206 // Reset the state of the main layer.
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);
222 void WindowSlider::CancelScroll() {
226 void WindowSlider::CompleteWindowSlideAfterAnimation() {
227 weak_factory_.InvalidateWeakPtrs();
232 delegate_->OnWindowSlideComplete();
235 void WindowSlider::AbortWindowSlideAfterAnimation() {
236 weak_factory_.InvalidateWeakPtrs();
238 delegate_->OnWindowSlideAborted();
241 void WindowSlider::OnKeyEvent(ui::KeyEvent* event) {
245 void WindowSlider::OnMouseEvent(ui::MouseEvent* event) {
246 if (!(event->flags() & ui::EF_IS_SYNTHESIZED))
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 UpdateForFling(event->x_offset_ordinal(), event->y_offset_ordinal());
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:
269 case ui::ET_GESTURE_SCROLL_UPDATE:
270 UpdateForScroll(details.scroll_x(), details.scroll_y());
273 case ui::ET_GESTURE_SCROLL_END:
274 UpdateForFling(0.f, 0.f);
277 case ui::ET_SCROLL_FLING_START:
278 UpdateForFling(details.velocity_x(), details.velocity_y());
281 case ui::ET_GESTURE_PINCH_BEGIN:
282 case ui::ET_GESTURE_PINCH_UPDATE:
283 case ui::ET_GESTURE_PINCH_END:
294 void 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);
308 } // namespace content