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.
5 #include "athena/wm/bezel_controller.h"
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"
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;
22 const float kScrollDeltaNone = 0;
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;
31 gfx::Display GetDisplay(aura::Window* window) {
32 gfx::Screen* screen = gfx::Screen::GetScreenFor(window);
33 return screen->GetDisplayNearestWindow(window);
36 float GetDistance(const gfx::PointF& location,
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
46 : point_in_screen.x() - GetDisplay(window).bounds().width();
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;
66 BezelController::BezelController(aura::Window* container)
67 : container_(container),
69 scroll_bezel_(BEZEL_NONE),
71 left_right_delegate_(NULL) {
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);
81 void BezelController::SetState(BezelController::State state,
83 if (!left_right_delegate_ || state == state_)
86 State old_state = state_;
90 scroll_bezel_ = BEZEL_NONE;
91 scroll_target_ = NULL;
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();
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
110 if (!left_right_delegate_)
113 ui::EventType type = event->type();
114 if (!ShouldProcessGesture(type))
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.
130 if (scroll_target_ && event->target() != scroll_target_)
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_);
139 if (type == ui::ET_GESTURE_BEGIN) {
140 if (num_touch_points > 2) {
141 SetState(IGNORE_CURRENT_SCROLL);
144 BezelController::Bezel event_bezel =
145 GetBezel(event->location_f(), target_window);
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);
153 SetState(BEZEL_GESTURE_STARTED);
155 case IGNORE_CURRENT_SCROLL:
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);
163 if (event_bezel != scroll_bezel_) {
164 SetState(IGNORE_CURRENT_SCROLL);
167 if (state_ == BEZEL_SCROLLING_ONE_FINGER)
168 SetState(BEZEL_SCROLLING_TWO_FINGERS);
170 case BEZEL_SCROLLING_TWO_FINGERS:
171 // Should've exited above
175 } else if (type == ui::ET_GESTURE_END) {
179 CHECK(scroll_target_);
180 if (num_touch_points == 1) {
183 SetState(IGNORE_CURRENT_SCROLL);
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)
190 if (num_touch_points == 1) {
191 SetState(BEZEL_SCROLLING_ONE_FINGER, scroll_delta);
195 DCHECK_EQ(num_touch_points, 2);
196 SetState(BEZEL_SCROLLING_TWO_FINGERS, scroll_delta);
197 if (left_right_delegate_->BezelCanScroll())
199 } else if (type == ui::ET_GESTURE_SCROLL_UPDATE) {
200 if (state_ != BEZEL_SCROLLING_TWO_FINGERS)
203 left_right_delegate_->BezelScrollUpdate(scroll_delta);
204 if (left_right_delegate_->BezelCanScroll())
209 } // namespace athena