window_slider.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved. 2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file. 4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/browser/web_contents/aura/window_slider.h" 6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <algorithm> 846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "base/bind.h" 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/callback.h" 1146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "content/browser/web_contents/aura/shadow_layer_delegate.h" 12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/public/browser/overscroll_configuration.h" 13f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "ui/aura/window.h" 1446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)#include "ui/compositor/layer_animation_observer.h" 15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "ui/compositor/scoped_layer_animation_settings.h" 16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "ui/events/event.h" 17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 1846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)namespace content { 1946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 2046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)namespace { 2146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 2246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void DeleteLayerAndShadow(ui::Layer* layer, 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) ShadowLayerDelegate* shadow) { 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) delete shadow; 25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) delete layer; 2646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)} 2746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// An animation observer that runs a callback at the end of the animation, and 2946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)// destroys itself. 3046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)class CallbackAnimationObserver : public ui::ImplicitAnimationObserver { 3146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) public: 3246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) CallbackAnimationObserver(const base::Closure& closure) 3346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) : closure_(closure) { 3446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) } 3546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 3646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) virtual ~CallbackAnimationObserver() {} 3746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 3846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) private: 3946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) // Overridden from ui::ImplicitAnimationObserver: 4046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) virtual void OnImplicitAnimationsCompleted() OVERRIDE { 4146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) if (!closure_.is_null()) 4246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) closure_.Run(); 4346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); 4446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) } 4546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 4646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) const base::Closure closure_; 47f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 48f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver); 49f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}; 50f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 51f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)} // namespace 52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 53116680a4aac90f2aa7413d9095a592090648e557Ben MurdochWindowSlider::WindowSlider(Delegate* delegate, 54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch aura::Window* event_window, 5546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) aura::Window* owner) 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) : delegate_(delegate), 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) event_window_(event_window), 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) owner_(owner), 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) delta_x_(0.f), 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) weak_factory_(this), 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 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 return fabs(delta_x_) >= active_start_threshold_ || slider_.get() || 96 weak_factory_.HasWeakPtrs(); 97} 98 99void WindowSlider::SetupSliderLayer() { 100 ui::Layer* parent = owner_->layer()->parent(); 101 parent->Add(slider_.get()); 102 if (delta_x_ < 0) 103 parent->StackAbove(slider_.get(), owner_->layer()); 104 else 105 parent->StackBelow(slider_.get(), owner_->layer()); 106 slider_->SetBounds(owner_->layer()->bounds()); 107 slider_->SetVisible(true); 108} 109 110void WindowSlider::UpdateForScroll(float x_offset, float y_offset) { 111 float old_delta = delta_x_; 112 delta_x_ += x_offset; 113 if (fabs(delta_x_) < active_start_threshold_ && !slider_.get()) 114 return; 115 116 if ((old_delta < 0 && delta_x_ > 0) || 117 (old_delta > 0 && delta_x_ < 0)) { 118 slider_.reset(); 119 shadow_.reset(); 120 } 121 122 float translate = 0.f; 123 ui::Layer* translate_layer = NULL; 124 125 if (!slider_.get()) { 126 slider_.reset(delta_x_ < 0 ? delegate_->CreateFrontLayer() : 127 delegate_->CreateBackLayer()); 128 if (!slider_.get()) 129 return; 130 SetupSliderLayer(); 131 } 132 133 if (delta_x_ <= -active_start_threshold_) { 134 translate = owner_->bounds().width() + 135 std::max(delta_x_ + active_start_threshold_, 136 static_cast<float>(-owner_->bounds().width())); 137 translate_layer = slider_.get(); 138 } else if (delta_x_ >= active_start_threshold_) { 139 translate = std::min(delta_x_ - active_start_threshold_, 140 static_cast<float>(owner_->bounds().width())); 141 translate_layer = owner_->layer(); 142 } else { 143 return; 144 } 145 146 if (!shadow_.get()) 147 shadow_.reset(new ShadowLayerDelegate(translate_layer)); 148 149 gfx::Transform transform; 150 transform.Translate(translate, 0); 151 translate_layer->SetTransform(transform); 152} 153 154void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) { 155 if (!slider_.get()) 156 return; 157 158 int width = owner_->bounds().width(); 159 float ratio = (fabs(delta_x_) - active_start_threshold_) / width; 160 if (ratio < complete_threshold_) { 161 ResetScroll(); 162 return; 163 } 164 165 ui::Layer* sliding = delta_x_ < 0 ? slider_.get() : owner_->layer(); 166 ui::ScopedLayerAnimationSettings settings(sliding->GetAnimator()); 167 settings.SetPreemptionStrategy( 168 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 169 settings.SetTweenType(gfx::Tween::EASE_OUT); 170 settings.AddObserver(new CallbackAnimationObserver( 171 base::Bind(&WindowSlider::CompleteWindowSlideAfterAnimation, 172 weak_factory_.GetWeakPtr()))); 173 174 gfx::Transform transform; 175 transform.Translate(delta_x_ < 0 ? 0 : width, 0); 176 sliding->SetTransform(transform); 177} 178 179void WindowSlider::ResetScroll() { 180 if (!slider_.get()) 181 return; 182 183 // Do not trigger any callbacks if this animation replaces any in-progress 184 // animation. 185 weak_factory_.InvalidateWeakPtrs(); 186 187 // Reset the state of the sliding layer. 188 if (slider_.get()) { 189 ui::Layer* layer = slider_.release(); 190 ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); 191 settings.SetPreemptionStrategy( 192 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 193 settings.SetTweenType(gfx::Tween::EASE_OUT); 194 195 // Delete the layer and the shadow at the end of the animation. 196 settings.AddObserver(new CallbackAnimationObserver( 197 base::Bind(&DeleteLayerAndShadow, 198 base::Unretained(layer), 199 base::Unretained(shadow_.release())))); 200 201 gfx::Transform transform; 202 transform.Translate(delta_x_ < 0 ? layer->bounds().width() : 0, 0); 203 layer->SetTransform(transform); 204 } 205 206 // Reset the state of the main layer. 207 { 208 ui::ScopedLayerAnimationSettings settings(owner_->layer()->GetAnimator()); 209 settings.SetPreemptionStrategy( 210 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); 211 settings.SetTweenType(gfx::Tween::EASE_OUT); 212 settings.AddObserver(new CallbackAnimationObserver( 213 base::Bind(&WindowSlider::AbortWindowSlideAfterAnimation, 214 weak_factory_.GetWeakPtr()))); 215 owner_->layer()->SetTransform(gfx::Transform()); 216 owner_->layer()->SetLayerBrightness(0.f); 217 } 218 219 delta_x_ = 0.f; 220} 221 222void WindowSlider::CancelScroll() { 223 ResetScroll(); 224} 225 226void WindowSlider::CompleteWindowSlideAfterAnimation() { 227 weak_factory_.InvalidateWeakPtrs(); 228 shadow_.reset(); 229 slider_.reset(); 230 delta_x_ = 0.f; 231 232 delegate_->OnWindowSlideComplete(); 233} 234 235void WindowSlider::AbortWindowSlideAfterAnimation() { 236 weak_factory_.InvalidateWeakPtrs(); 237 238 delegate_->OnWindowSlideAborted(); 239} 240 241void WindowSlider::OnKeyEvent(ui::KeyEvent* event) { 242 CancelScroll(); 243} 244 245void WindowSlider::OnMouseEvent(ui::MouseEvent* event) { 246 if (!(event->flags() & ui::EF_IS_SYNTHESIZED)) 247 CancelScroll(); 248} 249 250void 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 UpdateForFling(event->x_offset_ordinal(), event->y_offset_ordinal()); 256 else 257 CancelScroll(); 258 event->SetHandled(); 259} 260 261void 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 ResetScroll(); 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 UpdateForFling(0.f, 0.f); 275 break; 276 277 case ui::ET_SCROLL_FLING_START: 278 UpdateForFling(details.velocity_x(), details.velocity_y()); 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 CancelScroll(); 285 break; 286 287 default: 288 break; 289 } 290 291 event->SetHandled(); 292} 293 294void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window* window) { 295 if (window == event_window_) { 296 window->RemoveObserver(this); 297 window->RemovePreTargetHandler(this); 298 event_window_ = NULL; 299 } else if (window == owner_) { 300 window->RemoveObserver(this); 301 owner_ = NULL; 302 delete this; 303 } else { 304 NOTREACHED(); 305 } 306} 307 308} // namespace content 309