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/ui_base_switches_util.h"
18 #include "ui/gfx/range/range.h"
19 #include "ui/strings/grit/ui_strings.h"
20 #include "ui/wm/public/activation_client.h"
24 ////////////////////////////////////////////////////////////////////////////////
25 // TouchEditableImplAura, public:
27 TouchEditableImplAura::~TouchEditableImplAura() {
32 TouchEditableImplAura
* TouchEditableImplAura::Create() {
33 if (switches::IsTouchEditingEnabled())
34 return new TouchEditableImplAura();
38 void TouchEditableImplAura::AttachToView(RenderWidgetHostViewAura
* view
) {
47 rwhva_
->set_touch_editing_client(this);
50 void TouchEditableImplAura::UpdateEditingController() {
51 if (!rwhva_
|| !rwhva_
->HasFocus())
54 if (text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
||
55 selection_anchor_rect_
!= selection_focus_rect_
) {
56 if (touch_selection_controller_
)
57 touch_selection_controller_
->SelectionChanged();
59 EndTouchEditing(false);
63 void TouchEditableImplAura::OverscrollStarted() {
64 overscroll_in_progress_
= true;
67 void TouchEditableImplAura::OverscrollCompleted() {
68 // We might receive multiple OverscrollStarted() and OverscrollCompleted()
69 // during the same scroll session (for example, when the scroll direction
70 // changes). We want to show the handles only when:
71 // 1. Overscroll has completed
72 // 2. Scrolling session is over, i.e. we have received ET_GESTURE_SCROLL_END.
73 // 3. If we had hidden the handles when scrolling started
74 // 4. If there is still a need to show handles (there is a non-empty selection
75 // or non-NONE |text_input_type_|)
76 if (overscroll_in_progress_
&& !scroll_in_progress_
&&
77 handles_hidden_due_to_scroll_
&&
78 (selection_anchor_rect_
!= selection_focus_rect_
||
79 text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
)) {
81 UpdateEditingController();
83 overscroll_in_progress_
= false;
86 ////////////////////////////////////////////////////////////////////////////////
87 // TouchEditableImplAura, RenderWidgetHostViewAura::TouchEditingClient
90 void TouchEditableImplAura::StartTouchEditing() {
91 if (!rwhva_
|| !rwhva_
->HasFocus())
94 if (!touch_selection_controller_
) {
95 touch_selection_controller_
.reset(
96 ui::TouchSelectionController::create(this));
98 if (touch_selection_controller_
)
99 touch_selection_controller_
->SelectionChanged();
102 void TouchEditableImplAura::EndTouchEditing(bool quick
) {
103 if (touch_selection_controller_
) {
104 if (touch_selection_controller_
->IsHandleDragInProgress()) {
105 touch_selection_controller_
->SelectionChanged();
107 selection_gesture_in_process_
= false;
108 touch_selection_controller_
->HideHandles(quick
);
109 touch_selection_controller_
.reset();
114 void TouchEditableImplAura::OnSelectionOrCursorChanged(const gfx::Rect
& anchor
,
115 const gfx::Rect
& focus
) {
116 selection_anchor_rect_
= anchor
;
117 selection_focus_rect_
= focus
;
119 // If touch editing handles were not visible, we bring them up only if
120 // there is non-zero selection on the page. And the current event is a
121 // gesture event (we dont want to show handles if the user is selecting
122 // using mouse or keyboard).
123 if (selection_gesture_in_process_
&& !scroll_in_progress_
&&
124 !overscroll_in_progress_
&&
125 selection_anchor_rect_
!= selection_focus_rect_
) {
127 selection_gesture_in_process_
= false;
130 UpdateEditingController();
133 void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type
) {
134 text_input_type_
= type
;
137 bool TouchEditableImplAura::HandleInputEvent(const ui::Event
* event
) {
139 if (!event
->IsGestureEvent()) {
140 // Ignore all non-gesture events. Non-gesture events that can deactivate
141 // touch editing are handled in TouchSelectionControllerImpl.
145 const ui::GestureEvent
* gesture_event
=
146 static_cast<const ui::GestureEvent
*>(event
);
147 switch (event
->type()) {
148 case ui::ET_GESTURE_TAP
:
149 // When the user taps, we want to show touch editing handles if user
150 // tapped on selected text.
151 if (gesture_event
->details().tap_count() == 1 &&
152 selection_anchor_rect_
!= selection_focus_rect_
) {
153 // UnionRects only works for rects with non-zero width.
154 gfx::Rect
anchor(selection_anchor_rect_
.origin(),
155 gfx::Size(1, selection_anchor_rect_
.height()));
156 gfx::Rect
focus(selection_focus_rect_
.origin(),
157 gfx::Size(1, selection_focus_rect_
.height()));
158 gfx::Rect selection_rect
= gfx::UnionRects(anchor
, focus
);
159 if (selection_rect
.Contains(gesture_event
->location())) {
164 // For single taps, not inside selected region, we want to show handles
165 // only when the tap is on an already focused textfield.
166 textfield_was_focused_on_tap_
= false;
167 if (gesture_event
->details().tap_count() == 1 &&
168 text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
)
169 textfield_was_focused_on_tap_
= true;
171 case ui::ET_GESTURE_LONG_PRESS
:
172 selection_gesture_in_process_
= true;
174 case ui::ET_GESTURE_SCROLL_BEGIN
:
175 // If selection handles are currently visible, we want to get them back up
176 // when scrolling ends. So we set |handles_hidden_due_to_scroll_| so that
177 // we can re-start touch editing on scroll end gesture.
178 scroll_in_progress_
= true;
179 handles_hidden_due_to_scroll_
= false;
180 if (touch_selection_controller_
)
181 handles_hidden_due_to_scroll_
= true;
182 EndTouchEditing(true);
184 case ui::ET_GESTURE_SCROLL_END
:
185 // Scroll has ended, but we might still be in overscroll animation.
186 if (handles_hidden_due_to_scroll_
&& !overscroll_in_progress_
&&
187 (selection_anchor_rect_
!= selection_focus_rect_
||
188 text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
)) {
190 UpdateEditingController();
192 // fall through to reset |scroll_in_progress_|.
193 case ui::ET_SCROLL_FLING_START
:
194 selection_gesture_in_process_
= false;
195 scroll_in_progress_
= false;
203 void TouchEditableImplAura::GestureEventAck(int gesture_event_type
) {
205 if (gesture_event_type
== blink::WebInputEvent::GestureTap
&&
206 text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
&&
207 textfield_was_focused_on_tap_
) {
209 UpdateEditingController();
213 void TouchEditableImplAura::OnViewDestroyed() {
217 ////////////////////////////////////////////////////////////////////////////////
218 // TouchEditableImplAura, ui::TouchEditable implementation:
220 void TouchEditableImplAura::SelectRect(const gfx::Point
& start
,
221 const gfx::Point
& end
) {
222 RenderWidgetHost
* host
= rwhva_
->GetRenderWidgetHost();
223 RenderViewHost
* rvh
= RenderViewHost::From(host
);
224 WebContentsImpl
* wc
=
225 static_cast<WebContentsImpl
*>(WebContents::FromRenderViewHost(rvh
));
226 wc
->SelectRange(start
, end
);
229 void TouchEditableImplAura::MoveCaretTo(const gfx::Point
& point
) {
233 RenderWidgetHostImpl
* host
= RenderWidgetHostImpl::From(
234 rwhva_
->GetRenderWidgetHost());
235 host
->MoveCaret(point
);
238 void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect
* p1
,
240 *p1
= selection_anchor_rect_
;
241 *p2
= selection_focus_rect_
;
244 gfx::Rect
TouchEditableImplAura::GetBounds() {
245 return rwhva_
? gfx::Rect(rwhva_
->GetNativeView()->bounds().size()) :
249 gfx::NativeView
TouchEditableImplAura::GetNativeView() const {
250 return rwhva_
? rwhva_
->GetNativeView()->GetToplevelWindow() : NULL
;
253 void TouchEditableImplAura::ConvertPointToScreen(gfx::Point
* point
) {
256 aura::Window
* window
= rwhva_
->GetNativeView();
257 aura::client::ScreenPositionClient
* screen_position_client
=
258 aura::client::GetScreenPositionClient(window
->GetRootWindow());
259 if (screen_position_client
)
260 screen_position_client
->ConvertPointToScreen(window
, point
);
263 void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point
* point
) {
266 aura::Window
* window
= rwhva_
->GetNativeView();
267 aura::client::ScreenPositionClient
* screen_position_client
=
268 aura::client::GetScreenPositionClient(window
->GetRootWindow());
269 if (screen_position_client
)
270 screen_position_client
->ConvertPointFromScreen(window
, point
);
273 bool TouchEditableImplAura::DrawsHandles() {
277 void TouchEditableImplAura::OpenContextMenu(const gfx::Point
& anchor
) {
280 gfx::Point point
= anchor
;
281 ConvertPointFromScreen(&point
);
282 RenderWidgetHost
* host
= rwhva_
->GetRenderWidgetHost();
283 host
->Send(new ViewMsg_ShowContextMenu(
284 host
->GetRoutingID(), ui::MENU_SOURCE_TOUCH_EDIT_MENU
, point
));
285 EndTouchEditing(false);
288 bool TouchEditableImplAura::IsCommandIdChecked(int command_id
) const {
293 bool TouchEditableImplAura::IsCommandIdEnabled(int command_id
) const {
296 bool editable
= rwhva_
->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE
;
297 bool readable
= rwhva_
->GetTextInputType() != ui::TEXT_INPUT_TYPE_PASSWORD
;
298 gfx::Range selection_range
;
299 rwhva_
->GetSelectionRange(&selection_range
);
300 bool has_selection
= !selection_range
.is_empty();
301 switch (command_id
) {
303 return editable
&& readable
&& has_selection
;
305 return readable
&& has_selection
;
306 case IDS_APP_PASTE
: {
307 base::string16 result
;
308 ui::Clipboard::GetForCurrentThread()->ReadText(
309 ui::CLIPBOARD_TYPE_COPY_PASTE
, &result
);
310 return editable
&& !result
.empty();
313 return editable
&& has_selection
;
314 case IDS_APP_SELECT_ALL
:
321 bool TouchEditableImplAura::GetAcceleratorForCommandId(
323 ui::Accelerator
* accelerator
) {
327 void TouchEditableImplAura::ExecuteCommand(int command_id
, int event_flags
) {
328 RenderWidgetHost
* host
= rwhva_
->GetRenderWidgetHost();
329 RenderViewHost
* rvh
= RenderViewHost::From(host
);
330 WebContents
* wc
= WebContents::FromRenderViewHost(rvh
);
332 switch (command_id
) {
345 case IDS_APP_SELECT_ALL
:
352 EndTouchEditing(false);
355 void TouchEditableImplAura::DestroyTouchSelection() {
356 EndTouchEditing(false);
359 ////////////////////////////////////////////////////////////////////////////////
360 // TouchEditableImplAura, private:
362 TouchEditableImplAura::TouchEditableImplAura()
363 : text_input_type_(ui::TEXT_INPUT_TYPE_NONE
),
365 selection_gesture_in_process_(false),
366 handles_hidden_due_to_scroll_(false),
367 scroll_in_progress_(false),
368 overscroll_in_progress_(false),
369 textfield_was_focused_on_tap_(false) {
372 void TouchEditableImplAura::Cleanup() {
374 rwhva_
->set_touch_editing_client(NULL
);
377 text_input_type_
= ui::TEXT_INPUT_TYPE_NONE
;
378 EndTouchEditing(true);
379 selection_gesture_in_process_
= false;
380 handles_hidden_due_to_scroll_
= false;
381 scroll_in_progress_
= false;
382 overscroll_in_progress_
= false;
385 } // namespace content