window_slider.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 18namespace content { 19 20namespace { 21 22void DeleteLayerAndShadow(ui::Layer* layer, 23 ShadowLayerDelegate* shadow) { 24 delete shadow; 25 delete layer; 26} 27 28// An animation observer that runs a callback at the end of the animation, and 29// destroys itself. 30class CallbackAnimationObserver : public ui::ImplicitAnimationObserver { 31 public: 32 CallbackAnimationObserver(const base::Closure& closure) 33 : closure_(closure) { 34 } 35 36 virtual ~CallbackAnimationObserver() {} 37 38 private: 39 // Overridden from ui::ImplicitAnimationObserver: 40 virtual void OnImplicitAnimationsCompleted() OVERRIDE { 41 if (!closure_.is_null()) 42 closure_.Run(); 43 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 44 } 45 46 const base::Closure closure_; 47 48 DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver); 49}; 50 51} // namespace 52 53WindowSlider::WindowSlider(Delegate* delegate, 54 aura::Window* event_window, 55 aura::Window* owner) 56 : delegate_(delegate), 57 event_window_(event_window), 58 owner_(owner), 59 delta_x_(0.f), 60 weak_factory_(this), 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); 69 70 event_window_->AddObserver(this); 71 owner_->AddObserver(this); 72} 73 74WindowSlider::~WindowSlider() { 75 if (event_window_) { 76 event_window_->RemovePreTargetHandler(this); 77 event_window_->RemoveObserver(this); 78 } 79 if (owner_) 80 owner_->RemoveObserver(this); 81 delegate_->OnWindowSliderDestroyed(); 82} 83 84void WindowSlider::ChangeOwner(aura::Window* new_owner) { 85 if (owner_) 86 owner_->RemoveObserver(this); 87 owner_ = new_owner; 88 if (owner_) { 89 owner_->AddObserver(this); 90 UpdateForScroll(0.f, 0.f); 91 } 92} 93 94bool 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()); 99} 100 101void WindowSlider::SetupSliderLayer() { 102 ui::Layer* parent = owner_->layer()->parent(); 103 parent->Add(slider_.get()); 104 if (delta_x_ < 0) 105 parent->StackAbove(slider_.get(), owner_->layer()); 106 else 107 parent->StackBelow(slider_.get(), owner_->layer()); 108 slider_->SetBounds(owner_->layer()->bounds()); 109 slider_->SetVisible(true); 110} 111 112void 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()) 116 return; 117 118 if ((old_delta < 0 && delta_x_ > 0) || 119 (old_delta > 0 && delta_x_ < 0)) { 120 slider_.reset(); 121 shadow_.reset(); 122 } 123 124 float translate = 0.f; 125 ui::Layer* translate_layer = NULL; 126 127 if (!slider_.get()) { 128 slider_.reset(delta_x_ < 0 ? delegate_->CreateFrontLayer() : 129 delegate_->CreateBackLayer()); 130 if (!slider_.get()) 131 return; 132 SetupSliderLayer(); 133 } 134 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(); 144 } else { 145 return; 146 } 147 148 if (!shadow_.get()) 149 shadow_.reset(new ShadowLayerDelegate(translate_layer)); 150 151 gfx::Transform transform; 152 transform.Translate(translate, 0); 153 translate_layer->SetTransform(transform); 154} 155 156void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) { 157 if (!slider_.get()) 158 return; 159 160 int width = owner_->bounds().width(); 161 float ratio = (fabs(delta_x_) - active_start_threshold_) / width; 162 if (ratio < complete_threshold_) { 163 ResetScroll(); 164 return; 165 } 166 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()))); 175 176 gfx::Transform transform; 177 transform.Translate(delta_x_ < 0 ? 0 : width, 0); 178 sliding->SetTransform(transform); 179} 180 181void WindowSlider::ResetScroll() { 182 if (!slider_.get()) 183 return; 184 185 // Do not trigger any callbacks if this animation replaces any in-progress 186 // animation. 187 weak_factory_.InvalidateWeakPtrs(); 188 189 // Reset the state of the sliding layer. 190 if (slider_.get()) { 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); 196 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())))); 202 203 gfx::Transform transform; 204 transform.Translate(delta_x_ < 0 ? layer->bounds().width() : 0, 0); 205 layer->SetTransform(transform); 206 } 207 208 // Reset the state of the main layer. 209 { 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); 219 } 220 221 delta_x_ = 0.f; 222} 223 224void WindowSlider::CancelScroll() { 225 ResetScroll(); 226} 227 228void WindowSlider::CompleteWindowSlideAfterAnimation() { 229 weak_factory_.InvalidateWeakPtrs(); 230 shadow_.reset(); 231 slider_.reset(); 232 delta_x_ = 0.f; 233 234 delegate_->OnWindowSlideComplete(); 235} 236 237void WindowSlider::AbortWindowSlideAfterAnimation() { 238 weak_factory_.InvalidateWeakPtrs(); 239 240 delegate_->OnWindowSlideAborted(); 241} 242 243void WindowSlider::OnKeyEvent(ui::KeyEvent* event) { 244 CancelScroll(); 245} 246 247void WindowSlider::OnMouseEvent(ui::MouseEvent* event) { 248 if (!(event->flags() & ui::EF_IS_SYNTHESIZED)) 249 CancelScroll(); 250} 251 252void 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()); 258 else 259 CancelScroll(); 260 event->SetHandled(); 261} 262 263void 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: 268 ResetScroll(); 269 break; 270 271 case ui::ET_GESTURE_SCROLL_UPDATE: 272 UpdateForScroll(details.scroll_x(), details.scroll_y()); 273 break; 274 275 case ui::ET_GESTURE_SCROLL_END: 276 UpdateForFling(0.f, 0.f); 277 break; 278 279 case ui::ET_SCROLL_FLING_START: 280 UpdateForFling(details.velocity_x(), details.velocity_y()); 281 break; 282 283 case ui::ET_GESTURE_PINCH_BEGIN: 284 case ui::ET_GESTURE_PINCH_UPDATE: 285 case ui::ET_GESTURE_PINCH_END: 286 CancelScroll(); 287 break; 288 289 default: 290 break; 291 } 292 293 event->SetHandled(); 294} 295 296void 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); 303 owner_ = NULL; 304 delete this; 305 } else { 306 NOTREACHED(); 307 } 308} 309 310} // namespace content 311