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