touch_editable_impl_aura.cc revision a3f7b4e666c476898878fa745f637129375cd889
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_widget_host.h" 11#include "grit/ui_strings.h" 12#include "ui/aura/client/activation_client.h" 13#include "ui/aura/client/screen_position_client.h" 14#include "ui/aura/root_window.h" 15#include "ui/aura/window.h" 16#include "ui/base/clipboard/clipboard.h" 17#include "ui/base/range/range.h" 18#include "ui/base/ui_base_switches_util.h" 19 20namespace content { 21 22//////////////////////////////////////////////////////////////////////////////// 23// TouchEditableImplAura, public: 24 25TouchEditableImplAura::~TouchEditableImplAura() { 26 Cleanup(); 27} 28 29// static 30TouchEditableImplAura* TouchEditableImplAura::Create() { 31 if (switches::IsTouchEditingEnabled()) 32 return new TouchEditableImplAura(); 33 return NULL; 34} 35 36void TouchEditableImplAura::AttachToView(RenderWidgetHostViewAura* view) { 37 if (rwhva_ == view) 38 return; 39 40 Cleanup(); 41 if (!view) 42 return; 43 44 rwhva_ = view; 45 rwhva_->set_touch_editing_client(this); 46} 47 48void TouchEditableImplAura::UpdateEditingController() { 49 if (!rwhva_ || !rwhva_->HasFocus()) 50 return; 51 52 // If touch editing handles were not visible, we bring them up only if 53 // there is non-zero selection on the page. And the current event is a 54 // gesture event (we dont want to show handles if the user is selecting 55 // using mouse or keyboard). 56 if (selection_gesture_in_process_ && 57 selection_anchor_rect_ != selection_focus_rect_) 58 StartTouchEditing(); 59 60 if (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE || 61 selection_anchor_rect_ != selection_focus_rect_) { 62 if (touch_selection_controller_) 63 touch_selection_controller_->SelectionChanged(); 64 } else { 65 EndTouchEditing(); 66 } 67} 68 69//////////////////////////////////////////////////////////////////////////////// 70// TouchEditableImplAura, RenderWidgetHostViewAura::TouchEditingClient 71// implementation: 72 73void TouchEditableImplAura::StartTouchEditing() { 74 if (!touch_selection_controller_) { 75 touch_selection_controller_.reset( 76 ui::TouchSelectionController::create(this)); 77 } 78 if (touch_selection_controller_) 79 touch_selection_controller_->SelectionChanged(); 80} 81 82void TouchEditableImplAura::EndTouchEditing() { 83 if (touch_selection_controller_) { 84 if (touch_selection_controller_->IsHandleDragInProgress()) 85 touch_selection_controller_->SelectionChanged(); 86 else 87 touch_selection_controller_.reset(); 88 } 89} 90 91void TouchEditableImplAura::OnSelectionOrCursorChanged(const gfx::Rect& anchor, 92 const gfx::Rect& focus) { 93 selection_anchor_rect_ = anchor; 94 selection_focus_rect_ = focus; 95 UpdateEditingController(); 96} 97 98void TouchEditableImplAura::OnTextInputTypeChanged(ui::TextInputType type) { 99 text_input_type_ = type; 100} 101 102bool TouchEditableImplAura::HandleInputEvent(const ui::Event* event) { 103 DCHECK(rwhva_); 104 if (event->IsTouchEvent()) 105 return false; 106 107 if (!event->IsGestureEvent()) { 108 EndTouchEditing(); 109 return false; 110 } 111 112 const ui::GestureEvent* gesture_event = 113 static_cast<const ui::GestureEvent*>(event); 114 switch (event->type()) { 115 case ui::ET_GESTURE_TAP: 116 tap_gesture_tap_count_queue_.push(gesture_event->details().tap_count()); 117 if (gesture_event->details().tap_count() > 1) 118 selection_gesture_in_process_ = true; 119 // When the user taps, we want to show touch editing handles if user 120 // tapped on selected text. 121 if (selection_anchor_rect_ != selection_focus_rect_) { 122 // UnionRects only works for rects with non-zero width. 123 gfx::Rect anchor(selection_anchor_rect_.origin(), 124 gfx::Size(1, selection_anchor_rect_.height())); 125 gfx::Rect focus(selection_focus_rect_.origin(), 126 gfx::Size(1, selection_focus_rect_.height())); 127 gfx::Rect selection_rect = gfx::UnionRects(anchor, focus); 128 if (selection_rect.Contains(gesture_event->location())) { 129 StartTouchEditing(); 130 return true; 131 } 132 } 133 // For single taps, not inside selected region, we want to show handles 134 // only when the tap is on an already focused textfield. 135 is_tap_on_focused_textfield_ = false; 136 if (gesture_event->details().tap_count() == 1 && 137 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE) 138 is_tap_on_focused_textfield_ = true; 139 break; 140 case ui::ET_GESTURE_LONG_PRESS: 141 selection_gesture_in_process_ = true; 142 break; 143 case ui::ET_GESTURE_SCROLL_BEGIN: 144 // If selection handles are currently visible, we want to get them back up 145 // when scrolling ends. So we set |handles_hidden_due_to_scroll_| so that 146 // we can re-start touch editing when we call |UpdateEditingController()| 147 // on scroll end gesture. 148 handles_hidden_due_to_scroll_ = false; 149 if (touch_selection_controller_) 150 handles_hidden_due_to_scroll_ = true; 151 EndTouchEditing(); 152 break; 153 case ui::ET_GESTURE_SCROLL_END: 154 if (handles_hidden_due_to_scroll_ && 155 (selection_anchor_rect_ != selection_focus_rect_ || 156 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)) { 157 StartTouchEditing(); 158 UpdateEditingController(); 159 } 160 break; 161 default: 162 break; 163 } 164 return false; 165} 166 167void TouchEditableImplAura::GestureEventAck(int gesture_event_type) { 168 DCHECK(rwhva_); 169 if (gesture_event_type == WebKit::WebInputEvent::GestureTap && 170 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE && 171 is_tap_on_focused_textfield_) { 172 StartTouchEditing(); 173 if (touch_selection_controller_) 174 touch_selection_controller_->SelectionChanged(); 175 } 176 177 if (gesture_event_type == WebKit::WebInputEvent::GestureLongPress) 178 selection_gesture_in_process_ = false; 179 if (gesture_event_type == WebKit::WebInputEvent::GestureTap) { 180 if (tap_gesture_tap_count_queue_.front() > 1) 181 selection_gesture_in_process_ = false; 182 tap_gesture_tap_count_queue_.pop(); 183 } 184} 185 186void TouchEditableImplAura::OnViewDestroyed() { 187 Cleanup(); 188} 189 190//////////////////////////////////////////////////////////////////////////////// 191// TouchEditableImplAura, ui::TouchEditable implementation: 192 193void TouchEditableImplAura::SelectRect(const gfx::Point& start, 194 const gfx::Point& end) { 195 if (!rwhva_) 196 return; 197 198 RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( 199 rwhva_->GetRenderWidgetHost()); 200 host->SelectRange(start, end); 201} 202 203void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) { 204 if (!rwhva_) 205 return; 206 207 RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( 208 rwhva_->GetRenderWidgetHost()); 209 host->MoveCaret(point); 210} 211 212void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect* p1, 213 gfx::Rect* p2) { 214 *p1 = selection_anchor_rect_; 215 *p2 = selection_focus_rect_; 216} 217 218gfx::Rect TouchEditableImplAura::GetBounds() { 219 return rwhva_ ? rwhva_->GetNativeView()->bounds() : gfx::Rect(); 220} 221 222gfx::NativeView TouchEditableImplAura::GetNativeView() { 223 return rwhva_ ? rwhva_->GetNativeView()->GetRootWindow() : NULL; 224} 225 226void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) { 227 if (!rwhva_) 228 return; 229 aura::Window* window = rwhva_->GetNativeView(); 230 aura::client::ScreenPositionClient* screen_position_client = 231 aura::client::GetScreenPositionClient(window->GetRootWindow()); 232 if (screen_position_client) 233 screen_position_client->ConvertPointToScreen(window, point); 234} 235 236void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point* point) { 237 if (!rwhva_) 238 return; 239 aura::Window* window = rwhva_->GetNativeView(); 240 aura::client::ScreenPositionClient* screen_position_client = 241 aura::client::GetScreenPositionClient(window->GetRootWindow()); 242 if (screen_position_client) 243 screen_position_client->ConvertPointFromScreen(window, point); 244} 245 246bool TouchEditableImplAura::DrawsHandles() { 247 return false; 248} 249 250void TouchEditableImplAura::OpenContextMenu(const gfx::Point& anchor) { 251 if (!rwhva_) 252 return; 253 gfx::Point point = anchor; 254 ConvertPointFromScreen(&point); 255 RenderWidgetHost* host = rwhva_->GetRenderWidgetHost(); 256 host->Send(new ViewMsg_ShowContextMenu(host->GetRoutingID(), point)); 257 EndTouchEditing(); 258} 259 260bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const { 261 NOTREACHED(); 262 return false; 263} 264 265bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const { 266 if (!rwhva_) 267 return false; 268 bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE; 269 ui::Range selection_range; 270 rwhva_->GetSelectionRange(&selection_range); 271 bool has_selection = !selection_range.is_empty(); 272 switch (command_id) { 273 case IDS_APP_CUT: 274 return editable && has_selection; 275 case IDS_APP_COPY: 276 return has_selection; 277 case IDS_APP_PASTE: { 278 string16 result; 279 ui::Clipboard::GetForCurrentThread()->ReadText( 280 ui::Clipboard::BUFFER_STANDARD, &result); 281 return editable && !result.empty(); 282 } 283 case IDS_APP_DELETE: 284 return editable && has_selection; 285 case IDS_APP_SELECT_ALL: 286 return true; 287 default: 288 return false; 289 } 290} 291 292bool TouchEditableImplAura::GetAcceleratorForCommandId( 293 int command_id, 294 ui::Accelerator* accelerator) { 295 return false; 296} 297 298void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) { 299 if (!rwhva_) 300 return; 301 RenderWidgetHost* host = rwhva_->GetRenderWidgetHost(); 302 switch (command_id) { 303 case IDS_APP_CUT: 304 host->Cut(); 305 break; 306 case IDS_APP_COPY: 307 host->Copy(); 308 break; 309 case IDS_APP_PASTE: 310 host->Paste(); 311 break; 312 case IDS_APP_DELETE: 313 host->Delete(); 314 break; 315 case IDS_APP_SELECT_ALL: 316 host->SelectAll(); 317 break; 318 default: 319 NOTREACHED(); 320 break; 321 } 322 EndTouchEditing(); 323} 324 325//////////////////////////////////////////////////////////////////////////////// 326// TouchEditableImplAura, private: 327 328TouchEditableImplAura::TouchEditableImplAura() 329 : text_input_type_(ui::TEXT_INPUT_TYPE_NONE), 330 rwhva_(NULL), 331 selection_gesture_in_process_(false), 332 handles_hidden_due_to_scroll_(false), 333 is_tap_on_focused_textfield_(false) { 334} 335 336void TouchEditableImplAura::Cleanup() { 337 if (rwhva_) { 338 rwhva_->set_touch_editing_client(NULL); 339 rwhva_ = NULL; 340 } 341 touch_selection_controller_.reset(); 342} 343 344} // namespace content 345