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
TitleDragController(aura::Window * container,TitleDragControllerDelegate * delegate)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
~TitleDragController()41 TitleDragController::~TitleDragController() {
42 container_->RemovePreTargetHandler(this);
43 }
44
EndTransition(aura::Window * window,bool complete)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
OnTransitionEnd(aura::Window * window,bool complete)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
OnGestureEvent(ui::GestureEvent * gesture)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