window_slider.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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 "base/bind.h" 8#include "base/callback.h" 9#include "content/browser/web_contents/aura/shadow_layer_delegate.h" 10#include "content/public/browser/overscroll_configuration.h" 11#include "ui/aura/window.h" 12#include "ui/base/events/event.h" 13#include "ui/compositor/layer_animation_observer.h" 14#include "ui/compositor/scoped_layer_animation_settings.h" 15 16namespace content { 17 18namespace { 19 20void DeleteLayerAndShadow(ui::Layer* layer, 21 ShadowLayerDelegate* shadow) { 22 delete shadow; 23 delete layer; 24} 25 26// An animation observer that runs a callback at the end of the animation, and 27// destroys itself. 28class CallbackAnimationObserver : public ui::ImplicitAnimationObserver { 29 public: 30 CallbackAnimationObserver(const base::Closure& closure) 31 : closure_(closure) { 32 } 33 34 virtual ~CallbackAnimationObserver() {} 35 36 private: 37 // Overridden from ui::ImplicitAnimationObserver: 38 virtual void OnImplicitAnimationsCompleted() OVERRIDE { 39 if (!closure_.is_null()) 40 closure_.Run(); 41 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 42 } 43 44 const base::Closure closure_; 45 46 DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver); 47}; 48 49} // namespace 50 51WindowSlider::WindowSlider(Delegate* delegate, 52 aura::Window* event_window, 53 aura::Window* owner) 54 : delegate_(delegate), 55 event_window_(event_window), 56 owner_(owner), 57 delta_x_(0.f), 58 weak_factory_(this), 59 min_start_threshold_(content::GetOverscrollConfig( 60 content::OVERSCROLL_CONFIG_MIN_THRESHOLD_START)), 61 complete_threshold_(content::GetOverscrollConfig( 62 content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE)) { 63 event_window_->AddPreTargetHandler(this); 64 65 event_window_->AddObserver(this); 66 owner_->AddObserver(this); 67} 68 69WindowSlider::~WindowSlider() { 70 delegate_->OnWindowSliderDestroyed(); 71 if (event_window_) { 72 event_window_->RemovePreTargetHandler(this); 73 event_window_->RemoveObserver(this); 74 } 75 if (owner_) 76 owner_->RemoveObserver(this); 77} 78 79void 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 89void WindowSlider::SetupSliderLayer() { 90 ui::Layer* parent = owner_->layer()->parent(); 91 parent->Add(slider_.get()); 92 if (delta_x_ < 0) 93 parent->StackAbove(slider_.get(), owner_->layer()); 94 else 95 parent->StackBelow(slider_.get(), owner_->layer()); 96 slider_->SetBounds(owner_->layer()->bounds()); 97 slider_->SetVisible(true); 98} 99 100void WindowSlider::UpdateForScroll(float x_offset, float y_offset) { 101 float old_delta = delta_x_; 102 delta_x_ += x_offset; 103 if (fabs(delta_x_) < min_start_threshold_) { 104 ResetScroll(); 105 return; 106 } 107 108 if ((old_delta < 0 && delta_x_ > 0) || 109 (old_delta > 0 && delta_x_ < 0)) { 110 slider_.reset(); 111 shadow_.reset(); 112 } 113 114 float translate = 0.f; 115 ui::Layer* translate_layer = NULL; 116 117 if (delta_x_ <= -min_start_threshold_) { 118 if (!slider_.get()) { 119 slider_.reset(delegate_->CreateFrontLayer()); 120 SetupSliderLayer(); 121 } 122 translate = event_window_->bounds().width() - 123 fabs(delta_x_ - min_start_threshold_); 124 translate_layer = slider_.get(); 125 } else if (delta_x_ >= min_start_threshold_) { 126 if (!slider_.get()) { 127 slider_.reset(delegate_->CreateBackLayer()); 128 SetupSliderLayer(); 129 } 130 translate = delta_x_ - min_start_threshold_; 131 translate_layer = owner_->layer(); 132 } else { 133 NOTREACHED(); 134 } 135 136 if (!shadow_.get()) 137 shadow_.reset(new ShadowLayerDelegate(translate_layer)); 138 139 gfx::Transform transform; 140 transform.Translate(translate, 0); 141 translate_layer->SetTransform(transform); 142} 143 144void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) { 145 if (fabs(delta_x_) < min_start_threshold_) 146 return; 147 148 int width = owner_->bounds().width(); 149 float ratio = (fabs(delta_x_) - min_start_threshold_) / width; 150 if (ratio < complete_threshold_) { 151 ResetScroll(); 152 return; 153 } 154 155 ui::Layer* sliding = delta_x_ < 0 ? slider_.get() : owner_->layer(); 156 ui::ScopedLayerAnimationSettings settings(sliding->GetAnimator()); 157 settings.SetPreemptionStrategy( 158 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 159 settings.SetTweenType(ui::Tween::EASE_OUT); 160 settings.AddObserver(new CallbackAnimationObserver( 161 base::Bind(&WindowSlider::CompleteWindowSlideAfterAnimation, 162 weak_factory_.GetWeakPtr()))); 163 164 gfx::Transform transform; 165 transform.Translate(delta_x_ < 0 ? 0 : width, 0); 166 sliding->SetTransform(transform); 167} 168 169void WindowSlider::ResetScroll() { 170 if (!slider_.get()) 171 return; 172 173 // Do not trigger any callbacks if this animation replaces any in-progress 174 // animation. 175 weak_factory_.InvalidateWeakPtrs(); 176 177 // Reset the state of the sliding layer. 178 if (slider_.get()) { 179 ui::Layer* layer = slider_.release(); 180 ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); 181 settings.SetPreemptionStrategy( 182 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 183 settings.SetTweenType(ui::Tween::EASE_OUT); 184 185 // Delete the layer and the shadow at the end of the animation. 186 settings.AddObserver(new CallbackAnimationObserver( 187 base::Bind(&DeleteLayerAndShadow, 188 base::Unretained(layer), 189 base::Unretained(shadow_.release())))); 190 191 gfx::Transform transform; 192 transform.Translate(delta_x_ < 0 ? layer->bounds().width() : 0, 0); 193 layer->SetTransform(transform); 194 } 195 196 // Reset the state of the main layer. 197 { 198 ui::ScopedLayerAnimationSettings settings(owner_->layer()->GetAnimator()); 199 settings.SetPreemptionStrategy( 200 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 201 settings.SetTweenType(ui::Tween::EASE_OUT); 202 settings.AddObserver(new CallbackAnimationObserver( 203 base::Bind(&WindowSlider::AbortWindowSlideAfterAnimation, 204 weak_factory_.GetWeakPtr()))); 205 owner_->layer()->SetTransform(gfx::Transform()); 206 owner_->layer()->SetLayerBrightness(0.f); 207 } 208 209 delta_x_ = 0.f; 210} 211 212void WindowSlider::CancelScroll() { 213 ResetScroll(); 214} 215 216void WindowSlider::CompleteWindowSlideAfterAnimation() { 217 delegate_->OnWindowSlideComplete(); 218 delete this; 219} 220 221void WindowSlider::AbortWindowSlideAfterAnimation() { 222 delegate_->OnWindowSlideAborted(); 223} 224 225void WindowSlider::OnKeyEvent(ui::KeyEvent* event) { 226 CancelScroll(); 227} 228 229void WindowSlider::OnMouseEvent(ui::MouseEvent* event) { 230 if (!(event->flags() & ui::EF_IS_SYNTHESIZED)) 231 CancelScroll(); 232} 233 234void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) { 235 if (event->type() == ui::ET_SCROLL) 236 UpdateForScroll(event->x_offset_ordinal(), event->y_offset_ordinal()); 237 else if (event->type() == ui::ET_SCROLL_FLING_START) 238 UpdateForFling(event->x_offset_ordinal(), event->y_offset_ordinal()); 239 else 240 CancelScroll(); 241 event->SetHandled(); 242} 243 244void WindowSlider::OnGestureEvent(ui::GestureEvent* event) { 245 const ui::GestureEventDetails& details = event->details(); 246 switch (event->type()) { 247 case ui::ET_GESTURE_SCROLL_BEGIN: 248 ResetScroll(); 249 break; 250 251 case ui::ET_GESTURE_SCROLL_UPDATE: 252 UpdateForScroll(details.scroll_x(), details.scroll_y()); 253 break; 254 255 case ui::ET_GESTURE_SCROLL_END: 256 UpdateForFling(0.f, 0.f); 257 break; 258 259 case ui::ET_SCROLL_FLING_START: 260 UpdateForFling(details.velocity_x(), details.velocity_y()); 261 break; 262 263 case ui::ET_GESTURE_PINCH_BEGIN: 264 case ui::ET_GESTURE_PINCH_UPDATE: 265 case ui::ET_GESTURE_PINCH_END: 266 CancelScroll(); 267 break; 268 269 default: 270 break; 271 } 272 273 event->SetHandled(); 274} 275 276void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window* window) { 277 if (window == event_window_) { 278 window->RemoveObserver(this); 279 window->RemovePreTargetHandler(this); 280 event_window_ = NULL; 281 } else if (window == owner_) { 282 window->RemoveObserver(this); 283 owner_ = NULL; 284 if (!slider_.get()) 285 delete this; 286 } else { 287 NOTREACHED(); 288 } 289} 290 291} // namespace content 292