Home | History | Annotate | Download | only in aura
      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/base/events/event.h"
     15 #include "ui/compositor/layer_animation_observer.h"
     16 #include "ui/compositor/scoped_layer_animation_settings.h"
     17 
     18 namespace content {
     19 
     20 namespace {
     21 
     22 void 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.
     30 class 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 
     53 WindowSlider::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       horiz_start_threshold_(content::GetOverscrollConfig(
     62           content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START)),
     63       complete_threshold_(content::GetOverscrollConfig(
     64           content::OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE)) {
     65   event_window_->AddPreTargetHandler(this);
     66 
     67   event_window_->AddObserver(this);
     68   owner_->AddObserver(this);
     69 }
     70 
     71 WindowSlider::~WindowSlider() {
     72   if (event_window_) {
     73     event_window_->RemovePreTargetHandler(this);
     74     event_window_->RemoveObserver(this);
     75   }
     76   if (owner_)
     77     owner_->RemoveObserver(this);
     78   delegate_->OnWindowSliderDestroyed();
     79 }
     80 
     81 void WindowSlider::ChangeOwner(aura::Window* new_owner) {
     82   if (owner_)
     83     owner_->RemoveObserver(this);
     84   owner_ = new_owner;
     85   if (owner_) {
     86     owner_->AddObserver(this);
     87     UpdateForScroll(0.f, 0.f);
     88   }
     89 }
     90 
     91 bool WindowSlider::IsSlideInProgress() const {
     92   return fabs(delta_x_) >= horiz_start_threshold_ || slider_.get() ||
     93       weak_factory_.HasWeakPtrs();
     94 }
     95 
     96 void WindowSlider::SetupSliderLayer() {
     97   ui::Layer* parent = owner_->layer()->parent();
     98   parent->Add(slider_.get());
     99   if (delta_x_ < 0)
    100     parent->StackAbove(slider_.get(), owner_->layer());
    101   else
    102     parent->StackBelow(slider_.get(), owner_->layer());
    103   slider_->SetBounds(owner_->layer()->bounds());
    104   slider_->SetVisible(true);
    105 }
    106 
    107 void WindowSlider::UpdateForScroll(float x_offset, float y_offset) {
    108   float old_delta = delta_x_;
    109   delta_x_ += x_offset;
    110   if (fabs(delta_x_) < horiz_start_threshold_ && !slider_.get())
    111     return;
    112 
    113   if ((old_delta < 0 && delta_x_ > 0) ||
    114       (old_delta > 0 && delta_x_ < 0)) {
    115     slider_.reset();
    116     shadow_.reset();
    117   }
    118 
    119   float translate = 0.f;
    120   ui::Layer* translate_layer = NULL;
    121 
    122   if (!slider_.get()) {
    123     slider_.reset(delta_x_ < 0 ? delegate_->CreateFrontLayer() :
    124                                  delegate_->CreateBackLayer());
    125     if (!slider_.get())
    126       return;
    127     SetupSliderLayer();
    128   }
    129 
    130   if (delta_x_ <= -horiz_start_threshold_) {
    131     translate = owner_->bounds().width() +
    132         std::max(delta_x_ + horiz_start_threshold_,
    133                  static_cast<float>(-owner_->bounds().width()));
    134     translate_layer = slider_.get();
    135   } else if (delta_x_ >= horiz_start_threshold_) {
    136     translate = std::min(delta_x_ - horiz_start_threshold_,
    137                          static_cast<float>(owner_->bounds().width()));
    138     translate_layer = owner_->layer();
    139   } else {
    140     return;
    141   }
    142 
    143   if (!shadow_.get())
    144     shadow_.reset(new ShadowLayerDelegate(translate_layer));
    145 
    146   gfx::Transform transform;
    147   transform.Translate(translate, 0);
    148   translate_layer->SetTransform(transform);
    149 }
    150 
    151 void WindowSlider::UpdateForFling(float x_velocity, float y_velocity) {
    152   if (!slider_.get())
    153     return;
    154 
    155   int width = owner_->bounds().width();
    156   float ratio = (fabs(delta_x_) - horiz_start_threshold_) / width;
    157   if (ratio < complete_threshold_) {
    158     ResetScroll();
    159     return;
    160   }
    161 
    162   ui::Layer* sliding = delta_x_ < 0 ? slider_.get() : owner_->layer();
    163   ui::ScopedLayerAnimationSettings settings(sliding->GetAnimator());
    164   settings.SetPreemptionStrategy(
    165       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    166   settings.SetTweenType(ui::Tween::EASE_OUT);
    167   settings.AddObserver(new CallbackAnimationObserver(
    168       base::Bind(&WindowSlider::CompleteWindowSlideAfterAnimation,
    169                  weak_factory_.GetWeakPtr())));
    170 
    171   gfx::Transform transform;
    172   transform.Translate(delta_x_ < 0 ? 0 : width, 0);
    173   sliding->SetTransform(transform);
    174 }
    175 
    176 void WindowSlider::ResetScroll() {
    177   if (!slider_.get())
    178     return;
    179 
    180   // Do not trigger any callbacks if this animation replaces any in-progress
    181   // animation.
    182   weak_factory_.InvalidateWeakPtrs();
    183 
    184   // Reset the state of the sliding layer.
    185   if (slider_.get()) {
    186     ui::Layer* layer = slider_.release();
    187     ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
    188     settings.SetPreemptionStrategy(
    189         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    190     settings.SetTweenType(ui::Tween::EASE_OUT);
    191 
    192     // Delete the layer and the shadow at the end of the animation.
    193     settings.AddObserver(new CallbackAnimationObserver(
    194         base::Bind(&DeleteLayerAndShadow,
    195                    base::Unretained(layer),
    196                    base::Unretained(shadow_.release()))));
    197 
    198     gfx::Transform transform;
    199     transform.Translate(delta_x_ < 0 ? layer->bounds().width() : 0, 0);
    200     layer->SetTransform(transform);
    201   }
    202 
    203   // Reset the state of the main layer.
    204   {
    205     ui::ScopedLayerAnimationSettings settings(owner_->layer()->GetAnimator());
    206     settings.SetPreemptionStrategy(
    207         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    208     settings.SetTweenType(ui::Tween::EASE_OUT);
    209     settings.AddObserver(new CallbackAnimationObserver(
    210         base::Bind(&WindowSlider::AbortWindowSlideAfterAnimation,
    211                    weak_factory_.GetWeakPtr())));
    212     owner_->layer()->SetTransform(gfx::Transform());
    213     owner_->layer()->SetLayerBrightness(0.f);
    214   }
    215 
    216   delta_x_ = 0.f;
    217 }
    218 
    219 void WindowSlider::CancelScroll() {
    220   ResetScroll();
    221 }
    222 
    223 void WindowSlider::CompleteWindowSlideAfterAnimation() {
    224   weak_factory_.InvalidateWeakPtrs();
    225   shadow_.reset();
    226   slider_.reset();
    227   delta_x_ = 0.f;
    228 
    229   delegate_->OnWindowSlideComplete();
    230 }
    231 
    232 void WindowSlider::AbortWindowSlideAfterAnimation() {
    233   weak_factory_.InvalidateWeakPtrs();
    234 
    235   delegate_->OnWindowSlideAborted();
    236 }
    237 
    238 void WindowSlider::OnKeyEvent(ui::KeyEvent* event) {
    239   CancelScroll();
    240 }
    241 
    242 void WindowSlider::OnMouseEvent(ui::MouseEvent* event) {
    243   if (!(event->flags() & ui::EF_IS_SYNTHESIZED))
    244     CancelScroll();
    245 }
    246 
    247 void WindowSlider::OnScrollEvent(ui::ScrollEvent* event) {
    248   if (event->type() == ui::ET_SCROLL)
    249     UpdateForScroll(event->x_offset_ordinal(), event->y_offset_ordinal());
    250   else if (event->type() == ui::ET_SCROLL_FLING_START)
    251     UpdateForFling(event->x_offset_ordinal(), event->y_offset_ordinal());
    252   else
    253     CancelScroll();
    254   event->SetHandled();
    255 }
    256 
    257 void WindowSlider::OnGestureEvent(ui::GestureEvent* event) {
    258   const ui::GestureEventDetails& details = event->details();
    259   switch (event->type()) {
    260     case ui::ET_GESTURE_SCROLL_BEGIN:
    261       ResetScroll();
    262       break;
    263 
    264     case ui::ET_GESTURE_SCROLL_UPDATE:
    265       UpdateForScroll(details.scroll_x(), details.scroll_y());
    266       break;
    267 
    268     case ui::ET_GESTURE_SCROLL_END:
    269       UpdateForFling(0.f, 0.f);
    270       break;
    271 
    272     case ui::ET_SCROLL_FLING_START:
    273       UpdateForFling(details.velocity_x(), details.velocity_y());
    274       break;
    275 
    276     case ui::ET_GESTURE_PINCH_BEGIN:
    277     case ui::ET_GESTURE_PINCH_UPDATE:
    278     case ui::ET_GESTURE_PINCH_END:
    279       CancelScroll();
    280       break;
    281 
    282     default:
    283       break;
    284   }
    285 
    286   event->SetHandled();
    287 }
    288 
    289 void WindowSlider::OnWindowRemovingFromRootWindow(aura::Window* window) {
    290   if (window == event_window_) {
    291     window->RemoveObserver(this);
    292     window->RemovePreTargetHandler(this);
    293     event_window_ = NULL;
    294   } else if (window == owner_) {
    295     window->RemoveObserver(this);
    296     owner_ = NULL;
    297     delete this;
    298   } else {
    299     NOTREACHED();
    300   }
    301 }
    302 
    303 }  // namespace content
    304