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.
5 #include "content/browser/web_contents/touch_editable_impl_aura.h"
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/browser/web_contents/web_contents_impl.h"
10 #include "content/common/view_messages.h"
11 #include "content/public/browser/render_view_host.h"
12 #include "content/public/browser/render_widget_host.h"
13 #include "ui/aura/client/screen_position_client.h"
14 #include "ui/aura/window.h"
15 #include "ui/aura/window_tree_host.h"
16 #include "ui/base/clipboard/clipboard.h"
17 #include "ui/base/touch/selection_bound.h"
18 #include "ui/base/ui_base_switches_util.h"
19 #include "ui/gfx/range/range.h"
20 #include "ui/strings/grit/ui_strings.h"
21 #include "ui/wm/public/activation_client.h"
25 ////////////////////////////////////////////////////////////////////////////////
26 // TouchEditableImplAura, public:
28 TouchEditableImplAura::~TouchEditableImplAura() {
33 TouchEditableImplAura
* TouchEditableImplAura::Create() {
34 if (switches::IsTouchEditingEnabled())
35 return new TouchEditableImplAura();
39 void TouchEditableImplAura::AttachToView(RenderWidgetHostViewAura
* view
) {
48 rwhva_
->set_touch_editing_client(this);
51 void TouchEditableImplAura::UpdateEditingController() {
52 if (!rwhva_
|| !rwhva_
->HasFocus())
55 if (text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
||
56 selection_anchor_
!= selection_focus_
) {
57 if (touch_selection_controller_
)
58 touch_selection_controller_
->SelectionChanged();
60 EndTouchEditing(false);
64 void TouchEditableImplAura::OverscrollStarted() {
65 scrolls_in_progress_
++;
68 void TouchEditableImplAura::OverscrollCompleted() {
72 ////////////////////////////////////////////////////////////////////////////////
73 // TouchEditableImplAura, RenderWidgetHostViewAura::TouchEditingClient
76 void TouchEditableImplAura::StartTouchEditing() {
77 if (!rwhva_
|| !rwhva_
->HasFocus())
80 if (!touch_selection_controller_
) {
81 touch_selection_controller_
.reset(
82 ui::TouchEditingControllerDeprecated::Create(this));
84 if (touch_selection_controller_
)
85 touch_selection_controller_
->SelectionChanged();
88 void TouchEditableImplAura::EndTouchEditing(bool quick
) {
89 if (touch_selection_controller_
) {
90 if (touch_selection_controller_
->IsHandleDragInProgress()) {
91 touch_selection_controller_
->SelectionChanged();
93 selection_gesture_in_process_
= false;
94 touch_selection_controller_
->HideHandles(quick
);
95 touch_selection_controller_
.reset();
100 void TouchEditableImplAura::OnSelectionOrCursorChanged(
101 const ui::SelectionBound
& anchor
,
102 const ui::SelectionBound
& focus
) {
103 selection_anchor_
= anchor
;
104 selection_focus_
= focus
;
106 // If touch editing handles were not visible, we bring them up only if the
107 // current event is a gesture event, no scroll/fling/overscoll is in progress,
108 // and there is non-zero selection on the page
109 if (selection_gesture_in_process_
&& !scrolls_in_progress_
&&
110 selection_anchor_
!= selection_focus_
) {
112 selection_gesture_in_process_
= false;
115 UpdateEditingController();
118 void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type
) {
119 text_input_type_
= type
;
122 bool TouchEditableImplAura::HandleInputEvent(const ui::Event
* event
) {
124 if (!event
->IsGestureEvent()) {
125 // Ignore all non-gesture events. Non-gesture events that can deactivate
126 // touch editing are handled in TouchSelectionControllerImpl.
130 const ui::GestureEvent
* gesture_event
=
131 static_cast<const ui::GestureEvent
*>(event
);
132 switch (event
->type()) {
133 case ui::ET_GESTURE_TAP
:
134 // When the user taps, we want to show touch editing handles if user
135 // tapped on selected text.
136 if (gesture_event
->details().tap_count() == 1 &&
137 selection_anchor_
!= selection_focus_
) {
138 gfx::Rect selection_rect
=
139 ui::RectBetweenSelectionBounds(selection_anchor_
, selection_focus_
);
140 // When tap is on selection, show handles and mark event as handled only
141 // if handles are not present or text is not editable. Otherwise, do not
142 // set event as handles so that event is forwarded to the renderer to
143 // update selection/cursor.
144 if (selection_rect
.Contains(gesture_event
->location()) &&
145 (text_input_type_
== ui::TEXT_INPUT_TYPE_NONE
||
146 !touch_selection_controller_
)) {
151 // For single taps, not inside selected region, we want to show handles
152 // only when the tap is on an already focused textfield.
153 textfield_was_focused_on_tap_
=
154 gesture_event
->details().tap_count() == 1 &&
155 text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
;
157 case ui::ET_GESTURE_LONG_PRESS
:
158 selection_gesture_in_process_
= true;
160 case ui::ET_GESTURE_SCROLL_BEGIN
:
161 scrolls_in_progress_
++;
162 // We need to hide selection handles during scroll (including fling and
163 // overscroll), but they should be re-activated after scrolling if:
164 // - an existing scroll decided that handles should be shown after
166 // - the gesture in progress is going to end in selection; or
167 // - selection handles are currently active.
168 handles_hidden_due_to_scroll_
= handles_hidden_due_to_scroll_
||
169 selection_gesture_in_process_
||
170 touch_selection_controller_
!= NULL
;
171 selection_gesture_in_process_
= false;
172 EndTouchEditing(true);
174 case ui::ET_GESTURE_SCROLL_END
:
183 void TouchEditableImplAura::GestureEventAck(int gesture_event_type
) {
185 if (gesture_event_type
== blink::WebInputEvent::GestureTap
&&
186 text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
&&
187 textfield_was_focused_on_tap_
) {
189 UpdateEditingController();
193 void TouchEditableImplAura::DidStopFlinging() {
197 void TouchEditableImplAura::OnViewDestroyed() {
201 ////////////////////////////////////////////////////////////////////////////////
202 // TouchEditableImplAura, ui::TouchEditable implementation:
204 void TouchEditableImplAura::SelectRect(const gfx::Point
& start
,
205 const gfx::Point
& end
) {
206 RenderWidgetHost
* host
= rwhva_
->GetRenderWidgetHost();
207 RenderViewHost
* rvh
= RenderViewHost::From(host
);
208 WebContentsImpl
* wc
=
209 static_cast<WebContentsImpl
*>(WebContents::FromRenderViewHost(rvh
));
210 wc
->SelectRange(start
, end
);
213 void TouchEditableImplAura::MoveCaretTo(const gfx::Point
& point
) {
217 RenderWidgetHostImpl
* host
= RenderWidgetHostImpl::From(
218 rwhva_
->GetRenderWidgetHost());
219 host
->MoveCaret(point
);
222 void TouchEditableImplAura::GetSelectionEndPoints(ui::SelectionBound
* anchor
,
223 ui::SelectionBound
* focus
) {
224 *anchor
= selection_anchor_
;
225 *focus
= selection_focus_
;
228 gfx::Rect
TouchEditableImplAura::GetBounds() {
229 return rwhva_
? gfx::Rect(rwhva_
->GetNativeView()->bounds().size()) :
233 gfx::NativeView
TouchEditableImplAura::GetNativeView() const {
234 return rwhva_
? rwhva_
->GetNativeView()->GetToplevelWindow() : NULL
;
237 void TouchEditableImplAura::ConvertPointToScreen(gfx::Point
* point
) {
240 aura::Window
* window
= rwhva_
->GetNativeView();
241 aura::client::ScreenPositionClient
* screen_position_client
=
242 aura::client::GetScreenPositionClient(window
->GetRootWindow());
243 if (screen_position_client
)
244 screen_position_client
->ConvertPointToScreen(window
, point
);
247 void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point
* point
) {
250 aura::Window
* window
= rwhva_
->GetNativeView();
251 aura::client::ScreenPositionClient
* screen_position_client
=
252 aura::client::GetScreenPositionClient(window
->GetRootWindow());
253 if (screen_position_client
)
254 screen_position_client
->ConvertPointFromScreen(window
, point
);
257 bool TouchEditableImplAura::DrawsHandles() {
261 void TouchEditableImplAura::OpenContextMenu(const gfx::Point
& anchor
) {
264 gfx::Point point
= anchor
;
265 ConvertPointFromScreen(&point
);
266 RenderWidgetHost
* host
= rwhva_
->GetRenderWidgetHost();
267 host
->Send(new ViewMsg_ShowContextMenu(
268 host
->GetRoutingID(), ui::MENU_SOURCE_TOUCH_EDIT_MENU
, point
));
269 EndTouchEditing(false);
272 bool TouchEditableImplAura::IsCommandIdChecked(int command_id
) const {
277 bool TouchEditableImplAura::IsCommandIdEnabled(int command_id
) const {
280 bool editable
= rwhva_
->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE
;
281 bool readable
= rwhva_
->GetTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD
;
282 gfx::Range selection_range
;
283 rwhva_
->GetSelectionRange(&selection_range
);
284 bool has_selection
= !selection_range
.is_empty();
285 switch (command_id
) {
287 return editable
&& readable
&& has_selection
;
289 return readable
&& has_selection
;
290 case IDS_APP_PASTE
: {
291 base::string16 result
;
292 ui::Clipboard::GetForCurrentThread()->ReadText(
293 ui::CLIPBOARD_TYPE_COPY_PASTE
, &result
);
294 return editable
&& !result
.empty();
297 return editable
&& has_selection
;
298 case IDS_APP_SELECT_ALL
:
305 bool TouchEditableImplAura::GetAcceleratorForCommandId(
307 ui::Accelerator
* accelerator
) {
311 void TouchEditableImplAura::ExecuteCommand(int command_id
, int event_flags
) {
312 RenderWidgetHost
* host
= rwhva_
->GetRenderWidgetHost();
313 RenderViewHost
* rvh
= RenderViewHost::From(host
);
314 WebContents
* wc
= WebContents::FromRenderViewHost(rvh
);
316 switch (command_id
) {
329 case IDS_APP_SELECT_ALL
:
336 EndTouchEditing(false);
339 void TouchEditableImplAura::DestroyTouchSelection() {
340 EndTouchEditing(false);
343 ////////////////////////////////////////////////////////////////////////////////
344 // TouchEditableImplAura, private:
346 TouchEditableImplAura::TouchEditableImplAura()
347 : text_input_type_(ui::TEXT_INPUT_TYPE_NONE
),
349 selection_gesture_in_process_(false),
350 handles_hidden_due_to_scroll_(false),
351 scrolls_in_progress_(0),
352 textfield_was_focused_on_tap_(false) {
355 void TouchEditableImplAura::ScrollEnded() {
356 scrolls_in_progress_
--;
357 // If there is no scrolling left in progress, show selection handles if they
358 // were hidden due to scroll and there is a selection.
359 if (!scrolls_in_progress_
&& handles_hidden_due_to_scroll_
&&
360 (selection_anchor_
!= selection_focus_
||
361 text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
)) {
363 UpdateEditingController();
364 handles_hidden_due_to_scroll_
= false;
368 void TouchEditableImplAura::Cleanup() {
370 rwhva_
->set_touch_editing_client(NULL
);
373 text_input_type_
= ui::TEXT_INPUT_TYPE_NONE
;
374 EndTouchEditing(true);
375 selection_gesture_in_process_
= false;
376 handles_hidden_due_to_scroll_
= false;
377 scrolls_in_progress_
= 0;
380 } // namespace content