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 // An animation observer that runs a callback at the end of the animation, and
24 class CallbackAnimationObserver
: public ui::ImplicitAnimationObserver
{
26 CallbackAnimationObserver(const base::Closure
& closure
)
30 virtual ~CallbackAnimationObserver() {}
33 // Overridden from ui::ImplicitAnimationObserver:
34 virtual void OnImplicitAnimationsCompleted() OVERRIDE
{
35 if (!closure_
.is_null())
37 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
40 const base::Closure closure_
;
42 DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver
);
47 WindowSlider::WindowSlider(Delegate
* delegate
,
48 aura::Window
* event_window
,
50 : delegate_(delegate
),
51 event_window_(event_window
),
53 active_animator_(NULL
),
56 active_start_threshold_(0.f
),
57 start_threshold_touchscreen_(content::GetOverscrollConfig(
58 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN
)),
59 start_threshold_touchpad_(content::GetOverscrollConfig(
60 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHPAD
)),
61 complete_threshold_(content::GetOverscrollConfig(
62 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE
)) {
63 event_window_
->AddPreTargetHandler(this);
65 event_window_
->AddObserver(this);
66 owner_
->AddObserver(this);
69 WindowSlider::~WindowSlider() {
71 event_window_
->RemovePreTargetHandler(this);
72 event_window_
->RemoveObserver(this);
75 owner_
->RemoveObserver(this);
76 delegate_
->OnWindowSliderDestroyed();
79 void WindowSlider::ChangeOwner(aura::Window
* new_owner
) {
81 owner_
->RemoveObserver(this);
84 owner_
->AddObserver(this);
85 UpdateForScroll(0.f
, 0.f
);
89 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_
);
94 void WindowSlider::SetupSliderLayer() {
95 ui::Layer
* parent
= owner_
->layer()->parent();
96 parent
->Add(slider_
.get());
98 parent
->StackAbove(slider_
.get(), owner_
->layer());
100 parent
->StackBelow(slider_
.get(), owner_
->layer());
101 slider_
->SetBounds(owner_
->layer()->bounds());
102 slider_
->SetVisible(true);
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();
116 float old_delta
= delta_x_
;
117 delta_x_
+= x_offset
;
118 if (fabs(delta_x_
) < active_start_threshold_
&& !slider_
.get())
121 if ((old_delta
< 0 && delta_x_
> 0) ||
122 (old_delta
> 0 && delta_x_
< 0)) {
127 float translate
= 0.f
;
128 ui::Layer
* translate_layer
= NULL
;
130 if (!slider_
.get()) {
131 slider_
.reset(delta_x_
< 0 ? delegate_
->CreateFrontLayer() :
132 delegate_
->CreateBackLayer());
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();
152 shadow_
.reset(new ShadowLayerDelegate(translate_layer
));
154 gfx::Transform transform
;
155 transform
.Translate(translate
, 0);
156 translate_layer
->SetTransform(transform
);
159 void WindowSlider::CompleteOrResetSlide() {
163 int width
= owner_
->bounds().width();
164 float ratio
= (fabs(delta_x_
) - active_start_threshold_
) / width
;
165 if (ratio
< complete_threshold_
) {
170 ui::Layer
* sliding
= delta_x_
< 0 ? slider_
.get() : owner_
->layer();
171 active_animator_
= sliding
->GetAnimator();
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_
))));
183 gfx::Transform transform
;
184 transform
.Translate(delta_x_
< 0 ? 0 : width
, 0);
186 delegate_
->OnWindowSlideCompleting();
187 sliding
->SetTransform(transform
);
190 void WindowSlider::CompleteActiveAnimations() {
191 if (active_animator_
)
192 active_animator_
->StopAnimating();
195 void WindowSlider::ResetSlide() {
199 // Reset the state of the sliding layer.
201 ui::Layer
* translate_layer
;
202 gfx::Transform transform
;
204 translate_layer
= slider_
.get();
205 transform
.Translate(translate_layer
->bounds().width(), 0);
207 translate_layer
= owner_
->layer();
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
);
226 void WindowSlider::SlideAnimationCompleted(
227 scoped_ptr
<ui::Layer
> layer
, scoped_ptr
<ShadowLayerDelegate
> shadow
) {
228 active_animator_
= NULL
;
231 delegate_
->OnWindowSlideCompleted();
234 void WindowSlider::ResetSlideAnimationCompleted(
235 scoped_ptr
<ui::Layer
> layer
, scoped_ptr
<ShadowLayerDelegate
> shadow
) {
236 active_animator_
= NULL
;
239 delegate_
->OnWindowSlideAborted();
242 void WindowSlider::OnKeyEvent(ui::KeyEvent
* event
) {
246 void WindowSlider::OnMouseEvent(ui::MouseEvent
* event
) {
247 if (!(event
->flags() & ui::EF_IS_SYNTHESIZED
))
251 void WindowSlider::OnScrollEvent(ui::ScrollEvent
* event
) {
252 active_start_threshold_
= start_threshold_touchpad_
;
253 if (event
->type() == ui::ET_SCROLL
)
254 UpdateForScroll(event
->x_offset_ordinal(), event
->y_offset_ordinal());
255 else if (event
->type() == ui::ET_SCROLL_FLING_START
)
256 CompleteOrResetSlide();
262 void WindowSlider::OnGestureEvent(ui::GestureEvent
* event
) {
263 active_start_threshold_
= start_threshold_touchscreen_
;
264 const ui::GestureEventDetails
& details
= event
->details();
265 switch (event
->type()) {
266 case ui::ET_GESTURE_SCROLL_BEGIN
:
267 CompleteActiveAnimations();
270 case ui::ET_GESTURE_SCROLL_UPDATE
:
271 UpdateForScroll(details
.scroll_x(), details
.scroll_y());
274 case ui::ET_GESTURE_SCROLL_END
:
275 CompleteOrResetSlide();
278 case ui::ET_SCROLL_FLING_START
:
279 CompleteOrResetSlide();
282 case ui::ET_GESTURE_PINCH_BEGIN
:
283 case ui::ET_GESTURE_PINCH_UPDATE
:
284 case ui::ET_GESTURE_PINCH_END
:
295 void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window
* window
,
296 aura::Window
* new_root
) {
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