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 "grit/ui_strings.h"
14 #include "ui/aura/client/screen_position_client.h"
15 #include "ui/aura/window.h"
16 #include "ui/aura/window_tree_host.h"
17 #include "ui/base/clipboard/clipboard.h"
18 #include "ui/base/ui_base_switches_util.h"
19 #include "ui/gfx/range/range.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 touch_selection_controller_
->HideHandles(quick
);
108 touch_selection_controller_
.reset();
113 void TouchEditableImplAura::OnSelectionOrCursorChanged(const gfx::Rect
& anchor
,
114 const gfx::Rect
& focus
) {
115 selection_anchor_rect_
= anchor
;
116 selection_focus_rect_
= focus
;
118 // If touch editing handles were not visible, we bring them up only if
119 // there is non-zero selection on the page. And the current event is a
120 // gesture event (we dont want to show handles if the user is selecting
121 // using mouse or keyboard).
122 if (selection_gesture_in_process_
&& !scroll_in_progress_
&&
123 !overscroll_in_progress_
&&
124 selection_anchor_rect_
!= selection_focus_rect_
) {
126 selection_gesture_in_process_
= false;
129 UpdateEditingController();
132 void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type
) {
133 text_input_type_
= type
;
136 bool TouchEditableImplAura::HandleInputEvent(const ui::Event
* event
) {
138 if (event
->IsTouchEvent())
141 if (!event
->IsGestureEvent()) {
142 selection_gesture_in_process_
= false;
143 EndTouchEditing(false);
147 const ui::GestureEvent
* gesture_event
=
148 static_cast<const ui::GestureEvent
*>(event
);
149 switch (event
->type()) {
150 case ui::ET_GESTURE_TAP
:
151 if (gesture_event
->details().tap_count() > 1)
152 selection_gesture_in_process_
= true;
153 // When the user taps, we want to show touch editing handles if user
154 // tapped on selected text.
155 if (selection_anchor_rect_
!= selection_focus_rect_
) {
156 // UnionRects only works for rects with non-zero width.
157 gfx::Rect
anchor(selection_anchor_rect_
.origin(),
158 gfx::Size(1, selection_anchor_rect_
.height()));
159 gfx::Rect
focus(selection_focus_rect_
.origin(),
160 gfx::Size(1, selection_focus_rect_
.height()));
161 gfx::Rect selection_rect
= gfx::UnionRects(anchor
, focus
);
162 if (selection_rect
.Contains(gesture_event
->location())) {
167 // For single taps, not inside selected region, we want to show handles
168 // only when the tap is on an already focused textfield.
169 textfield_was_focused_on_tap_
= false;
170 if (gesture_event
->details().tap_count() == 1 &&
171 text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
)
172 textfield_was_focused_on_tap_
= true;
174 case ui::ET_GESTURE_LONG_PRESS
:
175 selection_gesture_in_process_
= true;
177 case ui::ET_GESTURE_SCROLL_BEGIN
:
178 // If selection handles are currently visible, we want to get them back up
179 // when scrolling ends. So we set |handles_hidden_due_to_scroll_| so that
180 // we can re-start touch editing on scroll end gesture.
181 scroll_in_progress_
= true;
182 handles_hidden_due_to_scroll_
= false;
183 if (touch_selection_controller_
)
184 handles_hidden_due_to_scroll_
= true;
185 EndTouchEditing(true);
187 case ui::ET_GESTURE_SCROLL_END
:
188 // Scroll has ended, but we might still be in overscroll animation.
189 if (handles_hidden_due_to_scroll_
&& !overscroll_in_progress_
&&
190 (selection_anchor_rect_
!= selection_focus_rect_
||
191 text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
)) {
193 UpdateEditingController();
195 // fall through to reset |scroll_in_progress_|.
196 case ui::ET_SCROLL_FLING_START
:
197 selection_gesture_in_process_
= false;
198 scroll_in_progress_
= false;
206 void TouchEditableImplAura::GestureEventAck(int gesture_event_type
) {
208 if (gesture_event_type
== blink::WebInputEvent::GestureTap
&&
209 text_input_type_
!= ui::TEXT_INPUT_TYPE_NONE
&&
210 textfield_was_focused_on_tap_
) {
212 UpdateEditingController();
216 void TouchEditableImplAura::OnViewDestroyed() {
220 ////////////////////////////////////////////////////////////////////////////////
221 // TouchEditableImplAura, ui::TouchEditable implementation:
223 void TouchEditableImplAura::SelectRect(const gfx::Point
& start
,
224 const gfx::Point
& end
) {
225 RenderWidgetHost
* host
= rwhva_
->GetRenderWidgetHost();
226 RenderViewHost
* rvh
= RenderViewHost::From(host
);
227 WebContentsImpl
* wc
=
228 static_cast<WebContentsImpl
*>(WebContents::FromRenderViewHost(rvh
));
229 wc
->SelectRange(start
, end
);
232 void TouchEditableImplAura::MoveCaretTo(const gfx::Point
& point
) {
236 RenderWidgetHostImpl
* host
= RenderWidgetHostImpl::From(
237 rwhva_
->GetRenderWidgetHost());
238 host
->MoveCaret(point
);
241 void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect
* p1
,
243 *p1
= selection_anchor_rect_
;
244 *p2
= selection_focus_rect_
;
247 gfx::Rect
TouchEditableImplAura::GetBounds() {
248 return rwhva_
? gfx::Rect(rwhva_
->GetNativeView()->bounds().size()) :
252 gfx::NativeView
TouchEditableImplAura::GetNativeView() const {
253 return rwhva_
? rwhva_
->GetNativeView()->GetToplevelWindow() : NULL
;
256 void TouchEditableImplAura::ConvertPointToScreen(gfx::Point
* point
) {
259 aura::Window
* window
= rwhva_
->GetNativeView();
260 aura::client::ScreenPositionClient
* screen_position_client
=
261 aura::client::GetScreenPositionClient(window
->GetRootWindow());
262 if (screen_position_client
)
263 screen_position_client
->ConvertPointToScreen(window
, point
);
266 void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point
* point
) {
269 aura::Window
* window
= rwhva_
->GetNativeView();
270 aura::client::ScreenPositionClient
* screen_position_client
=
271 aura::client::GetScreenPositionClient(window
->GetRootWindow());
272 if (screen_position_client
)
273 screen_position_client
->ConvertPointFromScreen(window
, point
);
276 bool TouchEditableImplAura::DrawsHandles() {
280 void TouchEditableImplAura::OpenContextMenu(const gfx::Point
& anchor
) {
283 gfx::Point point
= anchor
;
284 ConvertPointFromScreen(&point
);
285 RenderWidgetHost
* host
= rwhva_
->GetRenderWidgetHost();
286 host
->Send(new ViewMsg_ShowContextMenu(host
->GetRoutingID(), point
));
287 EndTouchEditing(false);
290 bool TouchEditableImplAura::IsCommandIdChecked(int command_id
) const {
295 bool TouchEditableImplAura::IsCommandIdEnabled(int command_id
) const {
298 bool editable
= rwhva_
->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE
;
299 gfx::Range selection_range
;
300 rwhva_
->GetSelectionRange(&selection_range
);
301 bool has_selection
= !selection_range
.is_empty();
302 switch (command_id
) {
304 return editable
&& has_selection
;
306 return has_selection
;
307 case IDS_APP_PASTE
: {
308 base::string16 result
;
309 ui::Clipboard::GetForCurrentThread()->ReadText(
310 ui::CLIPBOARD_TYPE_COPY_PASTE
, &result
);
311 return editable
&& !result
.empty();
314 return editable
&& has_selection
;
315 case IDS_APP_SELECT_ALL
:
322 bool TouchEditableImplAura::GetAcceleratorForCommandId(
324 ui::Accelerator
* accelerator
) {
328 void TouchEditableImplAura::ExecuteCommand(int command_id
, int event_flags
) {
329 RenderWidgetHost
* host
= rwhva_
->GetRenderWidgetHost();
330 RenderViewHost
* rvh
= RenderViewHost::From(host
);
331 WebContents
* wc
= WebContents::FromRenderViewHost(rvh
);
333 switch (command_id
) {
346 case IDS_APP_SELECT_ALL
:
353 EndTouchEditing(false);
356 void TouchEditableImplAura::DestroyTouchSelection() {
357 EndTouchEditing(false);
360 ////////////////////////////////////////////////////////////////////////////////
361 // TouchEditableImplAura, private:
363 TouchEditableImplAura::TouchEditableImplAura()
364 : text_input_type_(ui::TEXT_INPUT_TYPE_NONE
),
366 selection_gesture_in_process_(false),
367 handles_hidden_due_to_scroll_(false),
368 scroll_in_progress_(false),
369 overscroll_in_progress_(false),
370 textfield_was_focused_on_tap_(false) {
373 void TouchEditableImplAura::Cleanup() {
375 rwhva_
->set_touch_editing_client(NULL
);
378 text_input_type_
= ui::TEXT_INPUT_TYPE_NONE
;
379 EndTouchEditing(true);
380 selection_gesture_in_process_
= false;
381 handles_hidden_due_to_scroll_
= false;
382 scroll_in_progress_
= false;
383 overscroll_in_progress_
= false;
386 } // namespace content