touch_editable_impl_aura.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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_frame_host.h" 11#include "content/public/browser/render_view_host.h" 12#include "content/public/browser/render_widget_host.h" 13#include "content/public/browser/web_contents.h" 14#include "grit/ui_strings.h" 15#include "ui/aura/client/activation_client.h" 16#include "ui/aura/client/screen_position_client.h" 17#include "ui/aura/window.h" 18#include "ui/aura/window_event_dispatcher.h" 19#include "ui/base/clipboard/clipboard.h" 20#include "ui/base/ui_base_switches_util.h" 21#include "ui/gfx/range/range.h" 22 23namespace content { 24 25//////////////////////////////////////////////////////////////////////////////// 26// TouchEditableImplAura, public: 27 28TouchEditableImplAura::~TouchEditableImplAura() { 29 Cleanup(); 30} 31 32// static 33TouchEditableImplAura* TouchEditableImplAura::Create() { 34 if (switches::IsTouchEditingEnabled()) 35 return new TouchEditableImplAura(); 36 return NULL; 37} 38 39void TouchEditableImplAura::AttachToView(RenderWidgetHostViewAura* view) { 40 if (rwhva_ == view) 41 return; 42 43 Cleanup(); 44 if (!view) 45 return; 46 47 rwhva_ = view; 48 rwhva_->set_touch_editing_client(this); 49} 50 51void TouchEditableImplAura::UpdateEditingController() { 52 if (!rwhva_ || !rwhva_->HasFocus()) 53 return; 54 55 // If touch editing handles were not visible, we bring them up only if 56 // there is non-zero selection on the page. And the current event is a 57 // gesture event (we dont want to show handles if the user is selecting 58 // using mouse or keyboard). 59 if (selection_gesture_in_process_ && !scroll_in_progress_ && 60 selection_anchor_rect_ != selection_focus_rect_) 61 StartTouchEditing(); 62 63 if (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE || 64 selection_anchor_rect_ != selection_focus_rect_) { 65 if (touch_selection_controller_) 66 touch_selection_controller_->SelectionChanged(); 67 } else { 68 EndTouchEditing(false); 69 } 70} 71 72void TouchEditableImplAura::OverscrollStarted() { 73 overscroll_in_progress_ = true; 74} 75 76void TouchEditableImplAura::OverscrollCompleted() { 77 // We might receive multiple OverscrollStarted() and OverscrollCompleted() 78 // during the same scroll session (for example, when the scroll direction 79 // changes). We want to show the handles only when: 80 // 1. Overscroll has completed 81 // 2. Scrolling session is over, i.e. we have received ET_GESTURE_SCROLL_END. 82 // 3. If we had hidden the handles when scrolling started 83 // 4. If there is still a need to show handles (there is a non-empty selection 84 // or non-NONE |text_input_type_|) 85 if (overscroll_in_progress_ && !scroll_in_progress_ && 86 handles_hidden_due_to_scroll_ && 87 (selection_anchor_rect_ != selection_focus_rect_ || 88 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)) { 89 StartTouchEditing(); 90 UpdateEditingController(); 91 } 92 overscroll_in_progress_ = false; 93} 94 95//////////////////////////////////////////////////////////////////////////////// 96// TouchEditableImplAura, RenderWidgetHostViewAura::TouchEditingClient 97// implementation: 98 99void TouchEditableImplAura::StartTouchEditing() { 100 if (!rwhva_ || !rwhva_->HasFocus()) 101 return; 102 103 if (!touch_selection_controller_) { 104 touch_selection_controller_.reset( 105 ui::TouchSelectionController::create(this)); 106 } 107 if (touch_selection_controller_) 108 touch_selection_controller_->SelectionChanged(); 109} 110 111void TouchEditableImplAura::EndTouchEditing(bool quick) { 112 if (touch_selection_controller_) { 113 if (touch_selection_controller_->IsHandleDragInProgress()) { 114 touch_selection_controller_->SelectionChanged(); 115 } else { 116 touch_selection_controller_->HideHandles(quick); 117 touch_selection_controller_.reset(); 118 } 119 } 120} 121 122void TouchEditableImplAura::OnSelectionOrCursorChanged(const gfx::Rect& anchor, 123 const gfx::Rect& focus) { 124 selection_anchor_rect_ = anchor; 125 selection_focus_rect_ = focus; 126 UpdateEditingController(); 127} 128 129void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type) { 130 text_input_type_ = type; 131} 132 133bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) { 134 DCHECK(rwhva_); 135 if (event->IsTouchEvent()) 136 return false; 137 138 if (!event->IsGestureEvent()) { 139 EndTouchEditing(false); 140 return false; 141 } 142 143 const ui::GestureEvent* gesture_event = 144 static_cast<const ui::GestureEvent*>(event); 145 switch (event->type()) { 146 case ui::ET_GESTURE_TAP: 147 tap_gesture_tap_count_queue_.push(gesture_event->details().tap_count()); 148 if (gesture_event->details().tap_count() > 1) 149 selection_gesture_in_process_ = true; 150 // When the user taps, we want to show touch editing handles if user 151 // tapped on selected text. 152 if (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())) { 160 StartTouchEditing(); 161 return true; 162 } 163 } 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 is_tap_on_focused_textfield_ = false; 167 if (gesture_event->details().tap_count() == 1 && 168 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE) 169 is_tap_on_focused_textfield_ = true; 170 break; 171 case ui::ET_GESTURE_LONG_PRESS: 172 selection_gesture_in_process_ = true; 173 break; 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 when we call |UpdateEditingController()| 178 // on scroll end gesture. 179 scroll_in_progress_ = true; 180 handles_hidden_due_to_scroll_ = false; 181 if (touch_selection_controller_) 182 handles_hidden_due_to_scroll_ = true; 183 EndTouchEditing(true); 184 break; 185 case ui::ET_GESTURE_SCROLL_END: 186 // Scroll has ended, but we might still be in overscroll animation. 187 if (handles_hidden_due_to_scroll_ && !overscroll_in_progress_ && 188 (selection_anchor_rect_ != selection_focus_rect_ || 189 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)) { 190 StartTouchEditing(); 191 UpdateEditingController(); 192 } 193 // fall through to reset |scroll_in_progress_|. 194 case ui::ET_SCROLL_FLING_START: 195 selection_gesture_in_process_ = false; 196 scroll_in_progress_ = false; 197 break; 198 default: 199 break; 200 } 201 return false; 202} 203 204void TouchEditableImplAura::GestureEventAck(int gesture_event_type) { 205 DCHECK(rwhva_); 206 if (gesture_event_type == blink::WebInputEvent::GestureTap && 207 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE && 208 is_tap_on_focused_textfield_) { 209 StartTouchEditing(); 210 if (touch_selection_controller_) 211 touch_selection_controller_->SelectionChanged(); 212 } 213 214 if (gesture_event_type == blink::WebInputEvent::GestureLongPress) 215 selection_gesture_in_process_ = false; 216 if (gesture_event_type == blink::WebInputEvent::GestureTap) { 217 if (tap_gesture_tap_count_queue_.front() > 1) 218 selection_gesture_in_process_ = false; 219 tap_gesture_tap_count_queue_.pop(); 220 } 221} 222 223void TouchEditableImplAura::OnViewDestroyed() { 224 Cleanup(); 225} 226 227//////////////////////////////////////////////////////////////////////////////// 228// TouchEditableImplAura, ui::TouchEditable implementation: 229 230void TouchEditableImplAura::SelectRect(const gfx::Point& start, 231 const gfx::Point& end) { 232 if (!rwhva_) 233 return; 234 235 RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( 236 rwhva_->GetRenderWidgetHost()); 237 host->SelectRange(start, end); 238} 239 240void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) { 241 if (!rwhva_) 242 return; 243 244 RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( 245 rwhva_->GetRenderWidgetHost()); 246 host->MoveCaret(point); 247} 248 249void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect* p1, 250 gfx::Rect* p2) { 251 *p1 = selection_anchor_rect_; 252 *p2 = selection_focus_rect_; 253} 254 255gfx::Rect TouchEditableImplAura::GetBounds() { 256 return rwhva_ ? gfx::Rect(rwhva_->GetNativeView()->bounds().size()) : 257 gfx::Rect(); 258} 259 260gfx::NativeView TouchEditableImplAura::GetNativeView() const { 261 return rwhva_ ? rwhva_->GetNativeView()->GetToplevelWindow() : NULL; 262} 263 264void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) { 265 if (!rwhva_) 266 return; 267 aura::Window* window = rwhva_->GetNativeView(); 268 aura::client::ScreenPositionClient* screen_position_client = 269 aura::client::GetScreenPositionClient(window->GetRootWindow()); 270 if (screen_position_client) 271 screen_position_client->ConvertPointToScreen(window, point); 272} 273 274void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point* point) { 275 if (!rwhva_) 276 return; 277 aura::Window* window = rwhva_->GetNativeView(); 278 aura::client::ScreenPositionClient* screen_position_client = 279 aura::client::GetScreenPositionClient(window->GetRootWindow()); 280 if (screen_position_client) 281 screen_position_client->ConvertPointFromScreen(window, point); 282} 283 284bool TouchEditableImplAura::DrawsHandles() { 285 return false; 286} 287 288void TouchEditableImplAura::OpenContextMenu(const gfx::Point& anchor) { 289 if (!rwhva_) 290 return; 291 gfx::Point point = anchor; 292 ConvertPointFromScreen(&point); 293 RenderWidgetHost* host = rwhva_->GetRenderWidgetHost(); 294 host->Send(new ViewMsg_ShowContextMenu(host->GetRoutingID(), point)); 295 EndTouchEditing(false); 296} 297 298bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const { 299 NOTREACHED(); 300 return false; 301} 302 303bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const { 304 if (!rwhva_) 305 return false; 306 bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE; 307 gfx::Range selection_range; 308 rwhva_->GetSelectionRange(&selection_range); 309 bool has_selection = !selection_range.is_empty(); 310 switch (command_id) { 311 case IDS_APP_CUT: 312 return editable && has_selection; 313 case IDS_APP_COPY: 314 return has_selection; 315 case IDS_APP_PASTE: { 316 base::string16 result; 317 ui::Clipboard::GetForCurrentThread()->ReadText( 318 ui::CLIPBOARD_TYPE_COPY_PASTE, &result); 319 return editable && !result.empty(); 320 } 321 case IDS_APP_DELETE: 322 return editable && has_selection; 323 case IDS_APP_SELECT_ALL: 324 return true; 325 default: 326 return false; 327 } 328} 329 330bool TouchEditableImplAura::GetAcceleratorForCommandId( 331 int command_id, 332 ui::Accelerator* accelerator) { 333 return false; 334} 335 336void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) { 337 if (!rwhva_) 338 return; 339 RenderWidgetHost* host = rwhva_->GetRenderWidgetHost(); 340 RenderViewHost* rvh = RenderViewHost::From(host); 341 WebContents* wc = WebContents::FromRenderViewHost(rvh); 342 RenderFrameHost* focused_frame = wc->GetFocusedFrame(); 343 switch (command_id) { 344 case IDS_APP_CUT: 345 focused_frame->Cut(); 346 break; 347 case IDS_APP_COPY: 348 focused_frame->Copy(); 349 break; 350 case IDS_APP_PASTE: 351 focused_frame->Paste(); 352 break; 353 case IDS_APP_DELETE: 354 host->Delete(); 355 break; 356 case IDS_APP_SELECT_ALL: 357 host->SelectAll(); 358 break; 359 default: 360 NOTREACHED(); 361 break; 362 } 363 EndTouchEditing(false); 364} 365 366//////////////////////////////////////////////////////////////////////////////// 367// TouchEditableImplAura, private: 368 369TouchEditableImplAura::TouchEditableImplAura() 370 : text_input_type_(ui::TEXT_INPUT_TYPE_NONE), 371 rwhva_(NULL), 372 selection_gesture_in_process_(false), 373 handles_hidden_due_to_scroll_(false), 374 scroll_in_progress_(false), 375 overscroll_in_progress_(false), 376 is_tap_on_focused_textfield_(false) { 377} 378 379void TouchEditableImplAura::Cleanup() { 380 if (rwhva_) { 381 rwhva_->set_touch_editing_client(NULL); 382 rwhva_ = NULL; 383 } 384 text_input_type_ = ui::TEXT_INPUT_TYPE_NONE; 385 EndTouchEditing(true); 386 handles_hidden_due_to_scroll_ = false; 387 scroll_in_progress_ = false; 388 overscroll_in_progress_ = false; 389} 390 391} // namespace content 392