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 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 gfx::Range selection_range;
298 rwhva_->GetSelectionRange(&selection_range);
299 bool has_selection = !selection_range.is_empty();
300 switch (command_id) {
302 return editable && has_selection;
304 return has_selection;
305 case IDS_APP_PASTE: {
306 base::string16 result;
307 ui::Clipboard::GetForCurrentThread()->ReadText(
308 ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
309 return editable && !result.empty();
312 return editable && has_selection;
313 case IDS_APP_SELECT_ALL:
320 bool TouchEditableImplAura::GetAcceleratorForCommandId(
322 ui::Accelerator* accelerator) {
326 void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) {
327 RenderWidgetHost* host = rwhva_->GetRenderWidgetHost();
328 RenderViewHost* rvh = RenderViewHost::From(host);
329 WebContents* wc = WebContents::FromRenderViewHost(rvh);
331 switch (command_id) {
344 case IDS_APP_SELECT_ALL:
351 EndTouchEditing(false);
354 void TouchEditableImplAura::DestroyTouchSelection() {
355 EndTouchEditing(false);
358 ////////////////////////////////////////////////////////////////////////////////
359 // TouchEditableImplAura, private:
361 TouchEditableImplAura::TouchEditableImplAura()
362 : text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
364 selection_gesture_in_process_(false),
365 handles_hidden_due_to_scroll_(false),
366 scroll_in_progress_(false),
367 overscroll_in_progress_(false),
368 textfield_was_focused_on_tap_(false) {
371 void TouchEditableImplAura::Cleanup() {
373 rwhva_->set_touch_editing_client(NULL);
376 text_input_type_ = ui::TEXT_INPUT_TYPE_NONE;
377 EndTouchEditing(true);
378 selection_gesture_in_process_ = false;
379 handles_hidden_due_to_scroll_ = false;
380 scroll_in_progress_ = false;
381 overscroll_in_progress_ = false;
384 } // namespace content