OLD | NEW |
| (Empty) |
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: | |
26 CallbackAnimationObserver(const base::Closure& closure) | |
27 : closure_(closure) { | |
28 } | |
29 | |
30 ~CallbackAnimationObserver() override {} | |
31 | |
32 private: | |
33 // Overridden from ui::ImplicitAnimationObserver: | |
34 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 | |
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 | |
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 | |
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 | |
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_); | |
92 } | |
93 | |
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 | |
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 | |
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 | |
190 void WindowSlider::CompleteActiveAnimations() { | |
191 if (active_animator_) | |
192 active_animator_->StopAnimating(); | |
193 } | |
194 | |
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 | |
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 | |
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 | |
241 void WindowSlider::OnKeyEvent(ui::KeyEvent* event) { | |
242 ResetSlide(); | |
243 } | |
244 | |
245 void WindowSlider::OnMouseEvent(ui::MouseEvent* event) { | |
246 if (!(event->flags() & ui::EF_IS_SYNTHESIZED)) | |
247 ResetSlide(); | |
248 } | |
249 | |
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 | |
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 | |
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 | |
OLD | NEW |