touch_editable_impl_aura.cc revision a3f7b4e666c476898878fa745f637129375cd889
1// Copyright (c) 2013 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 "content/browser/web_contents/touch_editable_impl_aura.h"
6
7#include "content/browser/renderer_host/render_widget_host_impl.h"
8#include "content/browser/renderer_host/render_widget_host_view_aura.h"
9#include "content/common/view_messages.h"
10#include "content/public/browser/render_widget_host.h"
11#include "grit/ui_strings.h"
12#include "ui/aura/client/activation_client.h"
13#include "ui/aura/client/screen_position_client.h"
14#include "ui/aura/root_window.h"
15#include "ui/aura/window.h"
16#include "ui/base/clipboard/clipboard.h"
17#include "ui/base/range/range.h"
18#include "ui/base/ui_base_switches_util.h"
19
20namespace content {
21
22////////////////////////////////////////////////////////////////////////////////
23// TouchEditableImplAura, public:
24
25TouchEditableImplAura::~TouchEditableImplAura() {
26  Cleanup();
27}
28
29// static
30TouchEditableImplAura* TouchEditableImplAura::Create() {
31  if (switches::IsTouchEditingEnabled())
32    return new TouchEditableImplAura();
33  return NULL;
34}
35
36void TouchEditableImplAura::AttachToView(RenderWidgetHostViewAura* view) {
37  if (rwhva_ == view)
38    return;
39
40  Cleanup();
41  if (!view)
42    return;
43
44  rwhva_ = view;
45  rwhva_->set_touch_editing_client(this);
46}
47
48void TouchEditableImplAura::UpdateEditingController() {
49  if (!rwhva_ || !rwhva_->HasFocus())
50    return;
51
52  // If touch editing handles were not visible, we bring them up only if
53  // there is non-zero selection on the page. And the current event is a
54  // gesture event (we dont want to show handles if the user is selecting
55  // using mouse or keyboard).
56  if (selection_gesture_in_process_ &&
57      selection_anchor_rect_ != selection_focus_rect_)
58    StartTouchEditing();
59
60  if (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE ||
61      selection_anchor_rect_ != selection_focus_rect_) {
62    if (touch_selection_controller_)
63      touch_selection_controller_->SelectionChanged();
64  } else {
65    EndTouchEditing();
66  }
67}
68
69////////////////////////////////////////////////////////////////////////////////
70// TouchEditableImplAura, RenderWidgetHostViewAura::TouchEditingClient
71// implementation:
72
73void TouchEditableImplAura::StartTouchEditing() {
74  if (!touch_selection_controller_) {
75    touch_selection_controller_.reset(
76        ui::TouchSelectionController::create(this));
77  }
78  if (touch_selection_controller_)
79    touch_selection_controller_->SelectionChanged();
80}
81
82void TouchEditableImplAura::EndTouchEditing() {
83  if (touch_selection_controller_) {
84    if (touch_selection_controller_->IsHandleDragInProgress())
85      touch_selection_controller_->SelectionChanged();
86    else
87      touch_selection_controller_.reset();
88  }
89}
90
91void TouchEditableImplAura::OnSelectionOrCursorChanged(const gfx::Rect& anchor,
92                                                       const gfx::Rect& focus) {
93  selection_anchor_rect_ = anchor;
94  selection_focus_rect_ = focus;
95  UpdateEditingController();
96}
97
98void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type) {
99  text_input_type_ = type;
100}
101
102bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) {
103  DCHECK(rwhva_);
104  if (event->IsTouchEvent())
105    return false;
106
107  if (!event->IsGestureEvent()) {
108    EndTouchEditing();
109    return false;
110  }
111
112  const ui::GestureEvent* gesture_event =
113      static_cast<const ui::GestureEvent*>(event);
114  switch (event->type()) {
115    case ui::ET_GESTURE_TAP:
116      tap_gesture_tap_count_queue_.push(gesture_event->details().tap_count());
117      if (gesture_event->details().tap_count() > 1)
118        selection_gesture_in_process_ = true;
119      // When the user taps, we want to show touch editing handles if user
120      // tapped on selected text.
121      if (selection_anchor_rect_ != selection_focus_rect_) {
122        // UnionRects only works for rects with non-zero width.
123        gfx::Rect anchor(selection_anchor_rect_.origin(),
124                         gfx::Size(1, selection_anchor_rect_.height()));
125        gfx::Rect focus(selection_focus_rect_.origin(),
126                        gfx::Size(1, selection_focus_rect_.height()));
127        gfx::Rect selection_rect = gfx::UnionRects(anchor, focus);
128        if (selection_rect.Contains(gesture_event->location())) {
129          StartTouchEditing();
130          return true;
131        }
132      }
133      // For single taps, not inside selected region, we want to show handles
134      // only when the tap is on an already focused textfield.
135      is_tap_on_focused_textfield_ = false;
136      if (gesture_event->details().tap_count() == 1 &&
137          text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)
138        is_tap_on_focused_textfield_ = true;
139      break;
140    case ui::ET_GESTURE_LONG_PRESS:
141      selection_gesture_in_process_ = true;
142      break;
143    case ui::ET_GESTURE_SCROLL_BEGIN:
144      // If selection handles are currently visible, we want to get them back up
145      // when scrolling ends. So we set |handles_hidden_due_to_scroll_| so that
146      // we can re-start touch editing when we call |UpdateEditingController()|
147      // on scroll end gesture.
148      handles_hidden_due_to_scroll_ = false;
149      if (touch_selection_controller_)
150        handles_hidden_due_to_scroll_ = true;
151      EndTouchEditing();
152      break;
153    case ui::ET_GESTURE_SCROLL_END:
154      if (handles_hidden_due_to_scroll_ &&
155          (selection_anchor_rect_ != selection_focus_rect_ ||
156              text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)) {
157        StartTouchEditing();
158        UpdateEditingController();
159      }
160      break;
161    default:
162      break;
163  }
164  return false;
165}
166
167void TouchEditableImplAura::GestureEventAck(int gesture_event_type) {
168  DCHECK(rwhva_);
169  if (gesture_event_type == WebKit::WebInputEvent::GestureTap &&
170      text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
171      is_tap_on_focused_textfield_) {
172    StartTouchEditing();
173    if (touch_selection_controller_)
174      touch_selection_controller_->SelectionChanged();
175  }
176
177  if (gesture_event_type == WebKit::WebInputEvent::GestureLongPress)
178    selection_gesture_in_process_ = false;
179  if (gesture_event_type == WebKit::WebInputEvent::GestureTap) {
180    if (tap_gesture_tap_count_queue_.front() > 1)
181      selection_gesture_in_process_ = false;
182    tap_gesture_tap_count_queue_.pop();
183  }
184}
185
186void TouchEditableImplAura::OnViewDestroyed() {
187  Cleanup();
188}
189
190////////////////////////////////////////////////////////////////////////////////
191// TouchEditableImplAura, ui::TouchEditable implementation:
192
193void TouchEditableImplAura::SelectRect(const gfx::Point& start,
194                                       const gfx::Point& end) {
195  if (!rwhva_)
196    return;
197
198  RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
199      rwhva_->GetRenderWidgetHost());
200  host->SelectRange(start, end);
201}
202
203void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) {
204  if (!rwhva_)
205    return;
206
207  RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
208      rwhva_->GetRenderWidgetHost());
209  host->MoveCaret(point);
210}
211
212void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect* p1,
213                                                  gfx::Rect* p2) {
214  *p1 = selection_anchor_rect_;
215  *p2 = selection_focus_rect_;
216}
217
218gfx::Rect TouchEditableImplAura::GetBounds() {
219  return rwhva_ ? rwhva_->GetNativeView()->bounds() : gfx::Rect();
220}
221
222gfx::NativeView TouchEditableImplAura::GetNativeView() {
223  return rwhva_ ? rwhva_->GetNativeView()->GetRootWindow() : NULL;
224}
225
226void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) {
227  if (!rwhva_)
228    return;
229  aura::Window* window = rwhva_->GetNativeView();
230  aura::client::ScreenPositionClient* screen_position_client =
231      aura::client::GetScreenPositionClient(window->GetRootWindow());
232  if (screen_position_client)
233    screen_position_client->ConvertPointToScreen(window, point);
234}
235
236void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point* point) {
237  if (!rwhva_)
238    return;
239  aura::Window* window = rwhva_->GetNativeView();
240  aura::client::ScreenPositionClient* screen_position_client =
241      aura::client::GetScreenPositionClient(window->GetRootWindow());
242  if (screen_position_client)
243    screen_position_client->ConvertPointFromScreen(window, point);
244}
245
246bool TouchEditableImplAura::DrawsHandles() {
247  return false;
248}
249
250void TouchEditableImplAura::OpenContextMenu(const gfx::Point& anchor) {
251  if (!rwhva_)
252    return;
253  gfx::Point point = anchor;
254  ConvertPointFromScreen(&point);
255  RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
256  host->Send(new ViewMsg_ShowContextMenu(host->GetRoutingID(), point));
257  EndTouchEditing();
258}
259
260bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const {
261  NOTREACHED();
262  return false;
263}
264
265bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const {
266  if (!rwhva_)
267    return false;
268  bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE;
269  ui::Range selection_range;
270  rwhva_->GetSelectionRange(&selection_range);
271  bool has_selection = !selection_range.is_empty();
272  switch (command_id) {
273    case IDS_APP_CUT:
274      return editable && has_selection;
275    case IDS_APP_COPY:
276      return has_selection;
277    case IDS_APP_PASTE: {
278      string16 result;
279      ui::Clipboard::GetForCurrentThread()->ReadText(
280          ui::Clipboard::BUFFER_STANDARD, &result);
281      return editable && !result.empty();
282    }
283    case IDS_APP_DELETE:
284      return editable && has_selection;
285    case IDS_APP_SELECT_ALL:
286      return true;
287    default:
288      return false;
289  }
290}
291
292bool TouchEditableImplAura::GetAcceleratorForCommandId(
293    int command_id,
294    ui::Accelerator* accelerator) {
295  return false;
296}
297
298void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) {
299  if (!rwhva_)
300    return;
301  RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
302  switch (command_id) {
303    case IDS_APP_CUT:
304      host->Cut();
305      break;
306    case IDS_APP_COPY:
307      host->Copy();
308      break;
309    case IDS_APP_PASTE:
310      host->Paste();
311      break;
312    case IDS_APP_DELETE:
313      host->Delete();
314      break;
315    case IDS_APP_SELECT_ALL:
316      host->SelectAll();
317      break;
318    default:
319      NOTREACHED();
320      break;
321  }
322  EndTouchEditing();
323}
324
325////////////////////////////////////////////////////////////////////////////////
326// TouchEditableImplAura, private:
327
328TouchEditableImplAura::TouchEditableImplAura()
329    : text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
330      rwhva_(NULL),
331      selection_gesture_in_process_(false),
332      handles_hidden_due_to_scroll_(false),
333      is_tap_on_focused_textfield_(false) {
334}
335
336void TouchEditableImplAura::Cleanup() {
337  if (rwhva_) {
338    rwhva_->set_touch_editing_client(NULL);
339    rwhva_ = NULL;
340  }
341  touch_selection_controller_.reset();
342}
343
344}  // namespace content
345