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/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_(NULL),
     71       left_right_delegate_(NULL) {
     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_ = NULL;
     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();
    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
    210