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/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