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 // if active_start_threshold_ is 0, it means that sliding hasn't been started
96 return active_start_threshold_
!= 0 &&
97 (fabs(delta_x_
) >= active_start_threshold_
|| slider_
.get() ||
98 weak_factory_
.HasWeakPtrs());
101 void WindowSlider::SetupSliderLayer() {
102 ui::Layer
* parent
= owner_
->layer()->parent();
103 parent
->Add(slider_
.get());
105 parent
->StackAbove(slider_
.get(), owner_
->layer());
107 parent
->StackBelow(slider_
.get(), owner_
->layer());
108 slider_
->SetBounds(owner_
->layer()->bounds());
109 slider_
->SetVisible(true);
112 void WindowSlider::UpdateForScroll(float x_offset
, float y_offset
) {
113 float old_delta
= delta_x_
;
114 delta_x_
+= x_offset
;
115 if (fabs(delta_x_
) < active_start_threshold_
&& !slider_
.get())
118 if ((old_delta
< 0 && delta_x_
> 0) ||
119 (old_delta
> 0 && delta_x_
< 0)) {
124 float translate
= 0.f
;
125 ui::Layer
* translate_layer
= NULL
;
127 if (!slider_
.get()) {
128 slider_
.reset(delta_x_
< 0 ? delegate_
->CreateFrontLayer() :
129 delegate_
->CreateBackLayer());
135 if (delta_x_
<= -active_start_threshold_
) {
136 translate
= owner_
->bounds().width() +
137 std::max(delta_x_
+ active_start_threshold_
,
138 static_cast<float>(-owner_
->bounds().width()));
139 translate_layer
= slider_
.get();
140 } else if (delta_x_
>= active_start_threshold_
) {
141 translate
= std::min(delta_x_
- active_start_threshold_
,
142 static_cast<float>(owner_
->bounds().width()));
143 translate_layer
= owner_
->layer();
149 shadow_
.reset(new ShadowLayerDelegate(translate_layer
));
151 gfx::Transform transform
;
152 transform
.Translate(translate
, 0);
153 translate_layer
->SetTransform(transform
);
156 void WindowSlider::UpdateForFling(float x_velocity
, float y_velocity
) {
160 int width
= owner_
->bounds().width();
161 float ratio
= (fabs(delta_x_
) - active_start_threshold_
) / width
;
162 if (ratio
< complete_threshold_
) {
167 ui::Layer
* sliding
= delta_x_
< 0 ? slider_
.get() : owner_
->layer();
168 ui::ScopedLayerAnimationSettings
settings(sliding
->GetAnimator());
169 settings
.SetPreemptionStrategy(
170 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET
);
171 settings
.SetTweenType(gfx::Tween::EASE_OUT
);
172 settings
.AddObserver(new CallbackAnimationObserver(
173 base::Bind(&WindowSlider::CompleteWindowSlideAfterAnimation
,
174 weak_factory_
.GetWeakPtr())));
176 gfx::Transform transform
;
177 transform
.Translate(delta_x_
< 0 ? 0 : width
, 0);
178 sliding
->SetTransform(transform
);
181 void WindowSlider::ResetScroll() {
185 // Do not trigger any callbacks if this animation replaces any in-progress
187 weak_factory_
.InvalidateWeakPtrs();
189 // Reset the state of the sliding layer.
191 ui::Layer
* layer
= slider_
.release();
192 ui::ScopedLayerAnimationSettings
settings(layer
->GetAnimator());
193 settings
.SetPreemptionStrategy(
194 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET
);
195 settings
.SetTweenType(gfx::Tween::EASE_OUT
);
197 // Delete the layer and the shadow at the end of the animation.
198 settings
.AddObserver(new CallbackAnimationObserver(
199 base::Bind(&DeleteLayerAndShadow
,
200 base::Unretained(layer
),
201 base::Unretained(shadow_
.release()))));
203 gfx::Transform transform
;
204 transform
.Translate(delta_x_
< 0 ? layer
->bounds().width() : 0, 0);
205 layer
->SetTransform(transform
);
208 // Reset the state of the main layer.
210 ui::ScopedLayerAnimationSettings
settings(owner_
->layer()->GetAnimator());
211 settings
.SetPreemptionStrategy(
212 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET
);
213 settings
.SetTweenType(gfx::Tween::EASE_OUT
);
214 settings
.AddObserver(new CallbackAnimationObserver(
215 base::Bind(&WindowSlider::AbortWindowSlideAfterAnimation
,
216 weak_factory_
.GetWeakPtr())));
217 owner_
->layer()->SetTransform(gfx::Transform());
218 owner_
->layer()->SetLayerBrightness(0.f
);
224 void WindowSlider::CancelScroll() {
228 void WindowSlider::CompleteWindowSlideAfterAnimation() {
229 weak_factory_
.InvalidateWeakPtrs();
234 delegate_
->OnWindowSlideComplete();
237 void WindowSlider::AbortWindowSlideAfterAnimation() {
238 weak_factory_
.InvalidateWeakPtrs();
240 delegate_
->OnWindowSlideAborted();
243 void WindowSlider::OnKeyEvent(ui::KeyEvent
* event
) {
247 void WindowSlider::OnMouseEvent(ui::MouseEvent
* event
) {
248 if (!(event
->flags() & ui::EF_IS_SYNTHESIZED
))
252 void WindowSlider::OnScrollEvent(ui::ScrollEvent
* event
) {
253 active_start_threshold_
= start_threshold_touchpad_
;
254 if (event
->type() == ui::ET_SCROLL
)
255 UpdateForScroll(event
->x_offset_ordinal(), event
->y_offset_ordinal());
256 else if (event
->type() == ui::ET_SCROLL_FLING_START
)
257 UpdateForFling(event
->x_offset_ordinal(), event
->y_offset_ordinal());
263 void WindowSlider::OnGestureEvent(ui::GestureEvent
* event
) {
264 active_start_threshold_
= start_threshold_touchscreen_
;
265 const ui::GestureEventDetails
& details
= event
->details();
266 switch (event
->type()) {
267 case ui::ET_GESTURE_SCROLL_BEGIN
:
271 case ui::ET_GESTURE_SCROLL_UPDATE
:
272 UpdateForScroll(details
.scroll_x(), details
.scroll_y());
275 case ui::ET_GESTURE_SCROLL_END
:
276 UpdateForFling(0.f
, 0.f
);
279 case ui::ET_SCROLL_FLING_START
:
280 UpdateForFling(details
.velocity_x(), details
.velocity_y());
283 case ui::ET_GESTURE_PINCH_BEGIN
:
284 case ui::ET_GESTURE_PINCH_UPDATE
:
285 case ui::ET_GESTURE_PINCH_END
:
296 void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window
* window
) {
297 if (window
== event_window_
) {
298 window
->RemoveObserver(this);
299 window
->RemovePreTargetHandler(this);
300 event_window_
= NULL
;
301 } else if (window
== owner_
) {
302 window
->RemoveObserver(this);
310 } // namespace content