touch_editable_impl_aura.cc revision 010d83a9304c5a91596085d917d248abff47903a
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/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" 21 22namespace content { 23 24//////////////////////////////////////////////////////////////////////////////// 25// TouchEditableImplAura, public: 26 27TouchEditableImplAura::~TouchEditableImplAura() { 28 Cleanup(); 29} 30 31// static 32TouchEditableImplAura* TouchEditableImplAura::Create() { 33 if (switches::IsTouchEditingEnabled()) 34 return new TouchEditableImplAura(); 35 return NULL; 36} 37 38void TouchEditableImplAura::AttachToView(RenderWidgetHostViewAura* view) { 39 if (rwhva_ == view) 40 return; 41 42 Cleanup(); 43 if (!view) 44 return; 45 46 rwhva_ = view; 47 rwhva_->set_touch_editing_client(this); 48} 49 50void TouchEditableImplAura::UpdateEditingController() { 51 if (!rwhva_ || !rwhva_->HasFocus()) 52 return; 53 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(); 58 } else { 59 EndTouchEditing(false); 60 } 61} 62 63void TouchEditableImplAura::OverscrollStarted() { 64 overscroll_in_progress_ = true; 65} 66 67void 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)) { 80 StartTouchEditing(); 81 UpdateEditingController(); 82 } 83 overscroll_in_progress_ = false; 84} 85 86//////////////////////////////////////////////////////////////////////////////// 87// TouchEditableImplAura, RenderWidgetHostViewAura::TouchEditingClient 88// implementation: 89 90void TouchEditableImplAura::StartTouchEditing() { 91 if (!rwhva_ || !rwhva_->HasFocus()) 92 return; 93 94 if (!touch_selection_controller_) { 95 touch_selection_controller_.reset( 96 ui::TouchSelectionController::create(this)); 97 } 98 if (touch_selection_controller_) 99 touch_selection_controller_->SelectionChanged(); 100} 101 102void TouchEditableImplAura::EndTouchEditing(bool quick) { 103 if (touch_selection_controller_) { 104 if (touch_selection_controller_->IsHandleDragInProgress()) { 105 touch_selection_controller_->SelectionChanged(); 106 } else { 107 touch_selection_controller_->HideHandles(quick); 108 touch_selection_controller_.reset(); 109 } 110 } 111} 112 113void TouchEditableImplAura::OnSelectionOrCursorChanged(const gfx::Rect& anchor, 114 const gfx::Rect& focus) { 115 selection_anchor_rect_ = anchor; 116 selection_focus_rect_ = focus; 117 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_) { 125 StartTouchEditing(); 126 selection_gesture_in_process_ = false; 127 } 128 129 UpdateEditingController(); 130} 131 132void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type) { 133 text_input_type_ = type; 134} 135 136bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) { 137 DCHECK(rwhva_); 138 if (event->IsTouchEvent()) 139 return false; 140 141 if (!event->IsGestureEvent()) { 142 selection_gesture_in_process_ = false; 143 EndTouchEditing(false); 144 return false; 145 } 146 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())) { 163 StartTouchEditing(); 164 return true; 165 } 166 } 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; 173 break; 174 case ui::ET_GESTURE_LONG_PRESS: 175 selection_gesture_in_process_ = true; 176 break; 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); 186 break; 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)) { 192 StartTouchEditing(); 193 UpdateEditingController(); 194 } 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; 199 break; 200 default: 201 break; 202 } 203 return false; 204} 205 206void TouchEditableImplAura::GestureEventAck(int gesture_event_type) { 207 DCHECK(rwhva_); 208 if (gesture_event_type == blink::WebInputEvent::GestureTap && 209 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE && 210 textfield_was_focused_on_tap_) { 211 StartTouchEditing(); 212 UpdateEditingController(); 213 } 214} 215 216void TouchEditableImplAura::OnViewDestroyed() { 217 Cleanup(); 218} 219 220//////////////////////////////////////////////////////////////////////////////// 221// TouchEditableImplAura, ui::TouchEditable implementation: 222 223void 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); 230} 231 232void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) { 233 if (!rwhva_) 234 return; 235 236 RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( 237 rwhva_->GetRenderWidgetHost()); 238 host->MoveCaret(point); 239} 240 241void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect* p1, 242 gfx::Rect* p2) { 243 *p1 = selection_anchor_rect_; 244 *p2 = selection_focus_rect_; 245} 246 247gfx::Rect TouchEditableImplAura::GetBounds() { 248 return rwhva_ ? gfx::Rect(rwhva_->GetNativeView()->bounds().size()) : 249 gfx::Rect(); 250} 251 252gfx::NativeView TouchEditableImplAura::GetNativeView() const { 253 return rwhva_ ? rwhva_->GetNativeView()->GetToplevelWindow() : NULL; 254} 255 256void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) { 257 if (!rwhva_) 258 return; 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); 264} 265 266void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point* point) { 267 if (!rwhva_) 268 return; 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); 274} 275 276bool TouchEditableImplAura::DrawsHandles() { 277 return false; 278} 279 280void TouchEditableImplAura::OpenContextMenu(const gfx::Point& anchor) { 281 if (!rwhva_) 282 return; 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); 288} 289 290bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const { 291 NOTREACHED(); 292 return false; 293} 294 295bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const { 296 if (!rwhva_) 297 return false; 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) { 303 case IDS_APP_CUT: 304 return editable && has_selection; 305 case IDS_APP_COPY: 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(); 312 } 313 case IDS_APP_DELETE: 314 return editable && has_selection; 315 case IDS_APP_SELECT_ALL: 316 return true; 317 default: 318 return false; 319 } 320} 321 322bool TouchEditableImplAura::GetAcceleratorForCommandId( 323 int command_id, 324 ui::Accelerator* accelerator) { 325 return false; 326} 327 328void 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); 332 333 switch (command_id) { 334 case IDS_APP_CUT: 335 wc->Cut(); 336 break; 337 case IDS_APP_COPY: 338 wc->Copy(); 339 break; 340 case IDS_APP_PASTE: 341 wc->Paste(); 342 break; 343 case IDS_APP_DELETE: 344 wc->Delete(); 345 break; 346 case IDS_APP_SELECT_ALL: 347 wc->SelectAll(); 348 break; 349 default: 350 NOTREACHED(); 351 break; 352 } 353 EndTouchEditing(false); 354} 355 356void TouchEditableImplAura::DestroyTouchSelection() { 357 EndTouchEditing(false); 358} 359 360//////////////////////////////////////////////////////////////////////////////// 361// TouchEditableImplAura, private: 362 363TouchEditableImplAura::TouchEditableImplAura() 364 : text_input_type_(ui::TEXT_INPUT_TYPE_NONE), 365 rwhva_(NULL), 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) { 371} 372 373void TouchEditableImplAura::Cleanup() { 374 if (rwhva_) { 375 rwhva_->set_touch_editing_client(NULL); 376 rwhva_ = NULL; 377 } 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; 384} 385 386} // namespace content 387