Home | History | Annotate | Download | only in wm
      1 // Copyright 2014 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 "athena/wm/title_drag_controller.h"
      6 
      7 #include "base/bind.h"
      8 #include "ui/aura/window.h"
      9 #include "ui/aura/window_delegate.h"
     10 #include "ui/base/hit_test.h"
     11 #include "ui/compositor/closure_animation_observer.h"
     12 #include "ui/compositor/layer.h"
     13 #include "ui/compositor/scoped_layer_animation_settings.h"
     14 #include "ui/wm/core/shadow.h"
     15 #include "ui/wm/core/window_util.h"
     16 
     17 namespace {
     18 
     19 // The minimum amount to drag to confirm a window switch at the end of the
     20 // non-fling gesture.
     21 const int kMinDragDistanceForSwitch = 300;
     22 // The minimum velocity to confirm a window switch for a fling (only applicable
     23 // if the amount dragged was not sufficient, i.e. smaller than
     24 // kMinDragDistanceForSwitch).
     25 const int kMinDragVelocityForSwitch = 5000;
     26 
     27 }
     28 
     29 namespace athena {
     30 
     31 TitleDragController::TitleDragController(aura::Window* container,
     32                                          TitleDragControllerDelegate* delegate)
     33     : container_(container),
     34       delegate_(delegate),
     35       weak_ptr_(this) {
     36   CHECK(container_);
     37   CHECK(delegate_);
     38   container_->AddPreTargetHandler(this);
     39 }
     40 
     41 TitleDragController::~TitleDragController() {
     42   container_->RemovePreTargetHandler(this);
     43 }
     44 
     45 void TitleDragController::EndTransition(aura::Window* window, bool complete) {
     46   ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
     47   settings.SetPreemptionStrategy(
     48       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
     49   settings.AddObserver(new ui::ClosureAnimationObserver(
     50       base::Bind(&TitleDragController::OnTransitionEnd,
     51                  weak_ptr_.GetWeakPtr(),
     52                  window,
     53                  complete)));
     54   gfx::Transform transform;
     55   transform.Translate(0, complete ? window->bounds().height() : 0);
     56   window->SetTransform(transform);
     57 }
     58 
     59 void TitleDragController::OnTransitionEnd(aura::Window* window, bool complete) {
     60   weak_ptr_.InvalidateWeakPtrs();
     61   if (!tracker_.Contains(window))
     62     window = NULL;
     63   shadow_.reset();
     64   if (window) {
     65     window->SetTransform(gfx::Transform());
     66     tracker_.Remove(window);
     67   }
     68   if (complete && window && wm::IsActiveWindow(window))
     69     delegate_->OnTitleDragCompleted(window);
     70   else
     71     delegate_->OnTitleDragCanceled(window);
     72 }
     73 
     74 void TitleDragController::OnGestureEvent(ui::GestureEvent* gesture) {
     75   // Do not process any gesture events if an animation is still in progress from
     76   // a previous drag.
     77   if (weak_ptr_.HasWeakPtrs())
     78     return;
     79 
     80   if (gesture->type() == ui::ET_GESTURE_TAP_DOWN) {
     81     // It is possible to start a gesture sequence on a second window while a
     82     // drag is already in progress (e.g. the user starts interacting with the
     83     // window that is being revealed by the title-drag). Ignore these gesture
     84     // sequences.
     85     if (!tracker_.windows().empty())
     86       return;
     87     aura::Window* window = static_cast<aura::Window*>(gesture->target());
     88     if (!window || !window->delegate())
     89       return;
     90     int component =
     91         window->delegate()->GetNonClientComponent(gesture->location());
     92     if (component != HTCAPTION)
     93       return;
     94     if (!delegate_->GetWindowBehind(window))
     95       return;
     96     tracker_.Add(window);
     97     drag_start_location_ = gesture->root_location();
     98     return;
     99   }
    100 
    101   // If this gesture is for a different window, then ignore.
    102   aura::Window* window = static_cast<aura::Window*>(gesture->target());
    103   if (!tracker_.Contains(window))
    104     return;
    105 
    106   if (gesture->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
    107     delegate_->OnTitleDragStarted(window);
    108     shadow_.reset(new wm::Shadow());
    109     shadow_->Init(wm::Shadow::STYLE_ACTIVE);
    110     shadow_->SetContentBounds(gfx::Rect(window->bounds().size()));
    111     window->layer()->Add(shadow_->layer());
    112     gesture->SetHandled();
    113     return;
    114   }
    115 
    116   if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
    117     gfx::Vector2dF distance = gesture->root_location() - drag_start_location_;
    118     gfx::Transform transform;
    119     transform.Translate(0, std::max(0.f, distance.y()));
    120     window->SetTransform(transform);
    121     gesture->SetHandled();
    122     return;
    123   }
    124 
    125   if (gesture->type() == ui::ET_GESTURE_SCROLL_END) {
    126     gfx::Vector2dF distance = gesture->root_location() - drag_start_location_;
    127     EndTransition(window, distance.y() >= kMinDragDistanceForSwitch);
    128     gesture->SetHandled();
    129     return;
    130   }
    131 
    132   if (gesture->type() == ui::ET_SCROLL_FLING_START) {
    133     gfx::Vector2dF distance = gesture->root_location() - drag_start_location_;
    134     bool swipe_downwards = gesture->details().velocity_y() > 0;
    135     EndTransition(
    136         window,
    137         swipe_downwards &&
    138             (distance.y() >= kMinDragDistanceForSwitch ||
    139              gesture->details().velocity_y() >= kMinDragVelocityForSwitch));
    140     gesture->SetHandled();
    141   }
    142 }
    143 
    144 }  // namespace athena
    145