summaryrefslogtreecommitdiff
path: root/chromium/content/browser/renderer_host/input/stylus_text_selector.cc
blob: 11cace7e8256c2a9c4335768900154ea2ce1df35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/input/stylus_text_selector.h"

#include "ui/events/event_constants.h"
#include "ui/events/gesture_detection/gesture_detector.h"
#include "ui/events/gesture_detection/gesture_provider_config_helper.h"
#include "ui/events/gesture_detection/motion_event.h"

using ui::GestureDetector;
using ui::MotionEvent;

namespace content {
namespace {
scoped_ptr<GestureDetector> CreateGestureDetector(
    ui::GestureListener* listener) {
  GestureDetector::Config config =
      ui::GetGestureProviderConfig(
          ui::GestureProviderConfigType::CURRENT_PLATFORM)
          .gesture_detector_config;

  ui::DoubleTapListener* null_double_tap_listener = nullptr;

  // Doubletap, showpress and longpress detection are not required, and
  // should be explicitly disabled for efficiency.
  scoped_ptr<ui::GestureDetector> detector(
      new ui::GestureDetector(config, listener, null_double_tap_listener));
  detector->set_longpress_enabled(false);
  detector->set_showpress_enabled(false);

  return detector.Pass();
}

}  // namespace

StylusTextSelector::StylusTextSelector(StylusTextSelectorClient* client)
    : client_(client),
      text_selection_triggered_(false),
      secondary_button_pressed_(false),
      dragging_(false),
      dragged_(false),
      anchor_x_(0.0f),
      anchor_y_(0.0f) {
  DCHECK(client);
}

StylusTextSelector::~StylusTextSelector() {
}

bool StylusTextSelector::OnTouchEvent(const MotionEvent& event) {
  // Only trigger selection on ACTION_DOWN to prevent partial touch or gesture
  // sequences from being forwarded.
  if (event.GetAction() == MotionEvent::ACTION_DOWN)
    text_selection_triggered_ = ShouldStartTextSelection(event);

  if (!text_selection_triggered_)
    return false;

  secondary_button_pressed_ =
      event.GetButtonState() == MotionEvent::BUTTON_SECONDARY;

  switch (event.GetAction()) {
    case MotionEvent::ACTION_DOWN:
      dragging_ = false;
      dragged_ = false;
      anchor_x_ = event.GetX();
      anchor_y_ = event.GetY();
      break;

    case MotionEvent::ACTION_MOVE:
      if (!secondary_button_pressed_) {
        dragging_ = false;
        anchor_x_ = event.GetX();
        anchor_y_ = event.GetY();
      }
      break;

    case MotionEvent::ACTION_UP:
    case MotionEvent::ACTION_CANCEL:
      if (dragged_)
        client_->OnStylusSelectEnd();
      dragged_ = false;
      dragging_ = false;
      break;

    case MotionEvent::ACTION_POINTER_UP:
    case MotionEvent::ACTION_POINTER_DOWN:
      break;
    case MotionEvent::ACTION_NONE:
      NOTREACHED();
      break;
  }

  if (!gesture_detector_)
    gesture_detector_ = CreateGestureDetector(this);

  gesture_detector_->OnTouchEvent(event);

  // Always return true, even if |gesture_detector_| technically doesn't
  // consume the event. This prevents forwarding of a partial touch stream.
  return true;
}

bool StylusTextSelector::OnSingleTapUp(const MotionEvent& e, int tap_count) {
  DCHECK(text_selection_triggered_);
  DCHECK(!dragging_);
  client_->OnStylusSelectTap(e.GetEventTime(), e.GetX(), e.GetY());
  return true;
}

bool StylusTextSelector::OnScroll(const MotionEvent& e1,
                                  const MotionEvent& e2,
                                  float distance_x,
                                  float distance_y) {
  DCHECK(text_selection_triggered_);

  // Return if Stylus button is not pressed.
  if (!secondary_button_pressed_)
    return true;

  if (!dragging_) {
    dragging_ = true;
    dragged_ = true;
    client_->OnStylusSelectBegin(anchor_x_, anchor_y_, e2.GetX(), e2.GetY());
  } else {
    client_->OnStylusSelectUpdate(e2.GetX(), e2.GetY());
  }

  return true;
}

// static
bool StylusTextSelector::ShouldStartTextSelection(const MotionEvent& event) {
  DCHECK_GT(event.GetPointerCount(), 0u);
  // Currently we are supporting stylus-only cases.
  const bool is_stylus = event.GetToolType(0) == MotionEvent::TOOL_TYPE_STYLUS;
  const bool is_only_secondary_button_pressed =
      event.GetButtonState() == MotionEvent::BUTTON_SECONDARY;
  return is_stylus && is_only_secondary_button_pressed;
}

}  // namespace content