touch_editable_impl_aura.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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      if (gesture_event->details().tap_count() > 1)
117        selection_gesture_in_process_ = true;
118      // When the user taps, we want to show touch editing handles if user
119      // tapped on selected text.
120      if (selection_anchor_rect_ != selection_focus_rect_) {
121        // UnionRects only works for rects with non-zero width.
122        gfx::Rect anchor(selection_anchor_rect_.origin(),
123                         gfx::Size(1, selection_anchor_rect_.height()));
124        gfx::Rect focus(selection_focus_rect_.origin(),
125                        gfx::Size(1, selection_focus_rect_.height()));
126        gfx::Rect selection_rect = gfx::UnionRects(anchor, focus);
127        if (selection_rect.Contains(gesture_event->location())) {
128          StartTouchEditing();
129          return true;
130        }
131      }
132      // For single taps, not inside selected region, we want to show handles
133      // only when the tap is on an already focused textfield.
134      if (gesture_event->details().tap_count() == 1 &&
135          text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)
136        selection_gesture_in_process_ = true;
137      break;
138    case ui::ET_GESTURE_LONG_PRESS:
139      selection_gesture_in_process_ = true;
140      break;
141    case ui::ET_GESTURE_SCROLL_BEGIN:
142      // If selection handles are currently visible, we want to get them back up
143      // when scrolling ends. So we set |handles_hidden_due_to_scroll_| so that
144      // we can re-start touch editing when we call |UpdateEditingController()|
145      // on scroll end gesture.
146      handles_hidden_due_to_scroll_ = false;
147      if (touch_selection_controller_)
148        handles_hidden_due_to_scroll_ = true;
149      EndTouchEditing();
150      break;
151    case ui::ET_GESTURE_SCROLL_END:
152      if (handles_hidden_due_to_scroll_ &&
153          (selection_anchor_rect_ != selection_focus_rect_ ||
154              text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)) {
155        StartTouchEditing();
156        UpdateEditingController();
157      }
158      break;
159    default:
160      break;
161  }
162  return false;
163}
164
165void TouchEditableImplAura::GestureEventAck(int gesture_event_type) {
166  DCHECK(rwhva_);
167  if (gesture_event_type == WebKit::WebInputEvent::GestureTap &&
168      text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
169      selection_gesture_in_process_) {
170    StartTouchEditing();
171    if (touch_selection_controller_)
172      touch_selection_controller_->SelectionChanged();
173  }
174
175  if (gesture_event_type == WebKit::WebInputEvent::GestureLongPress ||
176      gesture_event_type == WebKit::WebInputEvent::GestureTap)
177    selection_gesture_in_process_ = false;
178}
179
180void TouchEditableImplAura::OnViewDestroyed() {
181  Cleanup();
182}
183
184////////////////////////////////////////////////////////////////////////////////
185// TouchEditableImplAura, ui::TouchEditable implementation:
186
187void TouchEditableImplAura::SelectRect(const gfx::Point& start,
188                                       const gfx::Point& end) {
189  if (!rwhva_)
190    return;
191
192  RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
193      rwhva_->GetRenderWidgetHost());
194  host->SelectRange(start, end);
195}
196
197void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) {
198  if (!rwhva_)
199    return;
200
201  RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
202      rwhva_->GetRenderWidgetHost());
203  host->MoveCaret(point);
204}
205
206void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect* p1,
207                                                  gfx::Rect* p2) {
208  *p1 = selection_anchor_rect_;
209  *p2 = selection_focus_rect_;
210}
211
212gfx::Rect TouchEditableImplAura::GetBounds() {
213  return rwhva_ ? rwhva_->GetNativeView()->bounds() : gfx::Rect();
214}
215
216gfx::NativeView TouchEditableImplAura::GetNativeView() {
217  return rwhva_ ? rwhva_->GetNativeView()->GetRootWindow() : NULL;
218}
219
220void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) {
221  if (!rwhva_)
222    return;
223  aura::Window* window = rwhva_->GetNativeView();
224  aura::client::ScreenPositionClient* screen_position_client =
225      aura::client::GetScreenPositionClient(window->GetRootWindow());
226  if (screen_position_client)
227    screen_position_client->ConvertPointToScreen(window, point);
228}
229
230void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point* point) {
231  if (!rwhva_)
232    return;
233  aura::Window* window = rwhva_->GetNativeView();
234  aura::client::ScreenPositionClient* screen_position_client =
235      aura::client::GetScreenPositionClient(window->GetRootWindow());
236  if (screen_position_client)
237    screen_position_client->ConvertPointFromScreen(window, point);
238}
239
240bool TouchEditableImplAura::DrawsHandles() {
241  return false;
242}
243
244void TouchEditableImplAura::OpenContextMenu(const gfx::Point& anchor) {
245  if (!rwhva_)
246    return;
247  gfx::Point point = anchor;
248  ConvertPointFromScreen(&point);
249  RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
250  host->Send(new ViewMsg_ShowContextMenu(host->GetRoutingID(), point));
251  EndTouchEditing();
252}
253
254bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const {
255  NOTREACHED();
256  return false;
257}
258
259bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const {
260  if (!rwhva_)
261    return false;
262  bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE;
263  ui::Range selection_range;
264  rwhva_->GetSelectionRange(&selection_range);
265  bool has_selection = !selection_range.is_empty();
266  switch (command_id) {
267    case IDS_APP_CUT:
268      return editable && has_selection;
269    case IDS_APP_COPY:
270      return has_selection;
271    case IDS_APP_PASTE: {
272      string16 result;
273      ui::Clipboard::GetForCurrentThread()->ReadText(
274          ui::Clipboard::BUFFER_STANDARD, &result);
275      return editable && !result.empty();
276    }
277    case IDS_APP_DELETE:
278      return editable && has_selection;
279    case IDS_APP_SELECT_ALL:
280      return true;
281    default:
282      return false;
283  }
284}
285
286bool TouchEditableImplAura::GetAcceleratorForCommandId(
287    int command_id,
288    ui::Accelerator* accelerator) {
289  return false;
290}
291
292void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) {
293  if (!rwhva_)
294    return;
295  RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
296  switch (command_id) {
297    case IDS_APP_CUT:
298      host->Cut();
299      break;
300    case IDS_APP_COPY:
301      host->Copy();
302      break;
303    case IDS_APP_PASTE:
304      host->Paste();
305      break;
306    case IDS_APP_DELETE:
307      host->Delete();
308      break;
309    case IDS_APP_SELECT_ALL:
310      host->SelectAll();
311      break;
312    default:
313      NOTREACHED();
314      break;
315  }
316  EndTouchEditing();
317}
318
319////////////////////////////////////////////////////////////////////////////////
320// TouchEditableImplAura, private:
321
322TouchEditableImplAura::TouchEditableImplAura()
323    : text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
324      rwhva_(NULL),
325      selection_gesture_in_process_(false),
326      handles_hidden_due_to_scroll_(false) {
327}
328
329void TouchEditableImplAura::Cleanup() {
330  if (rwhva_) {
331    rwhva_->set_touch_editing_client(NULL);
332    rwhva_ = NULL;
333  }
334  touch_selection_controller_.reset();
335}
336
337}  // namespace content
338