OLD | NEW |
| (Empty) |
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/bezel_controller.h" | |
6 | |
7 #include "ui/aura/window.h" | |
8 #include "ui/events/event_handler.h" | |
9 #include "ui/gfx/display.h" | |
10 #include "ui/gfx/geometry/point_conversions.h" | |
11 #include "ui/gfx/screen.h" | |
12 #include "ui/wm/core/coordinate_conversion.h" | |
13 | |
14 namespace athena { | |
15 namespace { | |
16 | |
17 // Using bezel swipes, the first touch that is registered is usually within | |
18 // 5-10 pixels from the edge, but sometimes as far as 29 pixels away. | |
19 // So setting this width fairly high for now. | |
20 const float kBezelWidth = 20.0f; | |
21 | |
22 const float kScrollDeltaNone = 0; | |
23 | |
24 bool ShouldProcessGesture(ui::EventType event_type) { | |
25 return event_type == ui::ET_GESTURE_SCROLL_UPDATE || | |
26 event_type == ui::ET_GESTURE_SCROLL_BEGIN || | |
27 event_type == ui::ET_GESTURE_BEGIN || | |
28 event_type == ui::ET_GESTURE_END; | |
29 } | |
30 | |
31 gfx::Display GetDisplay(aura::Window* window) { | |
32 gfx::Screen* screen = gfx::Screen::GetScreenFor(window); | |
33 return screen->GetDisplayNearestWindow(window); | |
34 } | |
35 | |
36 float GetDistance(const gfx::PointF& location, | |
37 aura::Window* window, | |
38 BezelController::Bezel bezel) { | |
39 DCHECK(bezel == BezelController::BEZEL_LEFT || | |
40 bezel == BezelController::BEZEL_RIGHT); | |
41 // Convert location from window coordinates to screen coordinates. | |
42 gfx::Point point_in_screen(gfx::ToRoundedPoint(location)); | |
43 wm::ConvertPointToScreen(window, &point_in_screen); | |
44 return bezel == BezelController::BEZEL_LEFT | |
45 ? point_in_screen.x() | |
46 : point_in_screen.x() - GetDisplay(window).bounds().width(); | |
47 } | |
48 | |
49 // Returns the bezel corresponding to the |location| in |window| or BEZEL_NONE | |
50 // if the location is outside of the bezel area. | |
51 // Only implemented for LEFT and RIGHT bezels. | |
52 BezelController::Bezel GetBezel(const gfx::PointF& location, | |
53 aura::Window* window) { | |
54 int screen_width = GetDisplay(window).bounds().width(); | |
55 gfx::Point point_in_screen(gfx::ToRoundedPoint(location)); | |
56 wm::ConvertPointToScreen(window, &point_in_screen); | |
57 if (point_in_screen.x() < kBezelWidth) | |
58 return BezelController::BEZEL_LEFT; | |
59 if (point_in_screen.x() > screen_width - kBezelWidth) | |
60 return BezelController::BEZEL_RIGHT; | |
61 return BezelController::BEZEL_NONE; | |
62 } | |
63 | |
64 } // namespace | |
65 | |
66 BezelController::BezelController(aura::Window* container) | |
67 : container_(container), | |
68 state_(NONE), | |
69 scroll_bezel_(BEZEL_NONE), | |
70 scroll_target_(nullptr), | |
71 left_right_delegate_(nullptr) { | |
72 } | |
73 | |
74 void BezelController::SetState(BezelController::State state) { | |
75 // Use SetState(State, float) if |state| is one of the BEZEL_SCROLLING states. | |
76 DCHECK_NE(state, BEZEL_SCROLLING_TWO_FINGERS); | |
77 DCHECK_NE(state, BEZEL_SCROLLING_ONE_FINGER); | |
78 SetState(state, kScrollDeltaNone); | |
79 } | |
80 | |
81 void BezelController::SetState(BezelController::State state, | |
82 float scroll_delta) { | |
83 if (!left_right_delegate_ || state == state_) | |
84 return; | |
85 | |
86 State old_state = state_; | |
87 state_ = state; | |
88 | |
89 if (state == NONE) { | |
90 scroll_bezel_ = BEZEL_NONE; | |
91 scroll_target_ = nullptr; | |
92 } | |
93 | |
94 if (state == BEZEL_SCROLLING_TWO_FINGERS) { | |
95 left_right_delegate_->BezelScrollBegin(scroll_bezel_, scroll_delta); | |
96 } else if (old_state == BEZEL_SCROLLING_TWO_FINGERS) { | |
97 // If BezelScrollEnd() hides |scroll_target_|, ET_GESTURE_END is dispatched | |
98 // and we get a reentrant call to SetState(). | |
99 left_right_delegate_->BezelScrollEnd(0.0f); | |
100 } | |
101 } | |
102 | |
103 void BezelController::OnGestureEvent(ui::GestureEvent* event) { | |
104 // TODO(mfomitchev): Currently we aren't retargetting or consuming any of the | |
105 // touch events. This means that content can prevent the generation of gesture | |
106 // events and two-finger scroll won't work. Possible solution to this problem | |
107 // is hosting our own gesture recognizer or retargetting touch events at the | |
108 // bezel. | |
109 | |
110 if (!left_right_delegate_) | |
111 return; | |
112 | |
113 ui::EventType type = event->type(); | |
114 if (!ShouldProcessGesture(type)) | |
115 return; | |
116 | |
117 const ui::GestureEventDetails& event_details = event->details(); | |
118 int num_touch_points = event_details.touch_points(); | |
119 if (num_touch_points == 1 && type == ui::ET_GESTURE_BEGIN) { | |
120 // Reset the state when the first finger touches and starts a gesture. | |
121 // Normally, the state gets reset when the last finger is lifted and we | |
122 // receive ET_GESTURE_END. However ET_GESTURE_END doesn't always get | |
123 // dispatched. (E.g. if the gesture target was hidden or deleted). | |
124 // Since we can't rely on receiving ET_GESTURE_END when the last finger is | |
125 // lifted, we also reset the state on ET_GESTURE_BEGIN when the first | |
126 // finger touches the screen. | |
127 SetState(NONE); | |
128 } | |
129 | |
130 if (scroll_target_ && event->target() != scroll_target_) | |
131 return; | |
132 | |
133 const gfx::PointF& event_location = event->location_f(); | |
134 float scroll_delta = kScrollDeltaNone; | |
135 aura::Window* target_window = static_cast<aura::Window*>(event->target()); | |
136 if (scroll_bezel_ != BEZEL_NONE) | |
137 scroll_delta = GetDistance(event_location, target_window, scroll_bezel_); | |
138 | |
139 if (type == ui::ET_GESTURE_BEGIN) { | |
140 if (num_touch_points > 2) { | |
141 SetState(IGNORE_CURRENT_SCROLL); | |
142 return; | |
143 } | |
144 BezelController::Bezel event_bezel = | |
145 GetBezel(event->location_f(), target_window); | |
146 switch (state_) { | |
147 case NONE: | |
148 scroll_bezel_ = event_bezel; | |
149 scroll_target_ = event->target(); | |
150 if (event_bezel != BEZEL_LEFT && event_bezel != BEZEL_RIGHT) | |
151 SetState(IGNORE_CURRENT_SCROLL); | |
152 else | |
153 SetState(BEZEL_GESTURE_STARTED); | |
154 break; | |
155 case IGNORE_CURRENT_SCROLL: | |
156 break; | |
157 case BEZEL_GESTURE_STARTED: | |
158 case BEZEL_SCROLLING_ONE_FINGER: | |
159 DCHECK_EQ(num_touch_points, 2); | |
160 DCHECK(scroll_target_); | |
161 DCHECK_NE(scroll_bezel_, BEZEL_NONE); | |
162 | |
163 if (event_bezel != scroll_bezel_) { | |
164 SetState(IGNORE_CURRENT_SCROLL); | |
165 return; | |
166 } | |
167 if (state_ == BEZEL_SCROLLING_ONE_FINGER) | |
168 SetState(BEZEL_SCROLLING_TWO_FINGERS); | |
169 break; | |
170 case BEZEL_SCROLLING_TWO_FINGERS: | |
171 // Should've exited above | |
172 NOTREACHED(); | |
173 break; | |
174 } | |
175 } else if (type == ui::ET_GESTURE_END) { | |
176 if (state_ == NONE) | |
177 return; | |
178 | |
179 CHECK(scroll_target_); | |
180 if (num_touch_points == 1) { | |
181 SetState(NONE); | |
182 } else { | |
183 SetState(IGNORE_CURRENT_SCROLL); | |
184 } | |
185 } else if (type == ui::ET_GESTURE_SCROLL_BEGIN) { | |
186 DCHECK(state_ == IGNORE_CURRENT_SCROLL || state_ == BEZEL_GESTURE_STARTED); | |
187 if (state_ != BEZEL_GESTURE_STARTED) | |
188 return; | |
189 | |
190 if (num_touch_points == 1) { | |
191 SetState(BEZEL_SCROLLING_ONE_FINGER, scroll_delta); | |
192 return; | |
193 } | |
194 | |
195 DCHECK_EQ(num_touch_points, 2); | |
196 SetState(BEZEL_SCROLLING_TWO_FINGERS, scroll_delta); | |
197 if (left_right_delegate_->BezelCanScroll()) | |
198 event->SetHandled(); | |
199 } else if (type == ui::ET_GESTURE_SCROLL_UPDATE) { | |
200 if (state_ != BEZEL_SCROLLING_TWO_FINGERS) | |
201 return; | |
202 | |
203 left_right_delegate_->BezelScrollUpdate(scroll_delta); | |
204 if (left_right_delegate_->BezelCanScroll()) | |
205 event->SetHandled(); | |
206 } | |
207 } | |
208 | |
209 } // namespace athena | |
OLD | NEW |