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 virtual ~CallbackAnimationObserver() {} 31 32 private: 33 // Overridden from ui::ImplicitAnimationObserver: 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 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 310