touch_editable_impl_aura.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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 if (gesture_event->details().tap_count() > 1) 117 selection_gesture_in_process_ = true; 118 // When the user taps, we want to show touch editing handles if user 119 // tapped on selected text. 120 if (selection_anchor_rect_ != selection_focus_rect_) { 121 // UnionRects only works for rects with non-zero width. 122 gfx::Rect anchor(selection_anchor_rect_.origin(), 123 gfx::Size(1, selection_anchor_rect_.height())); 124 gfx::Rect focus(selection_focus_rect_.origin(), 125 gfx::Size(1, selection_focus_rect_.height())); 126 gfx::Rect selection_rect = gfx::UnionRects(anchor, focus); 127 if (selection_rect.Contains(gesture_event->location())) { 128 StartTouchEditing(); 129 return true; 130 } 131 } 132 // For single taps, not inside selected region, we want to show handles 133 // only when the tap is on an already focused textfield. 134 if (gesture_event->details().tap_count() == 1 && 135 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE) 136 selection_gesture_in_process_ = true; 137 break; 138 case ui::ET_GESTURE_LONG_PRESS: 139 selection_gesture_in_process_ = true; 140 break; 141 case ui::ET_GESTURE_SCROLL_BEGIN: 142 // If selection handles are currently visible, we want to get them back up 143 // when scrolling ends. So we set |handles_hidden_due_to_scroll_| so that 144 // we can re-start touch editing when we call |UpdateEditingController()| 145 // on scroll end gesture. 146 handles_hidden_due_to_scroll_ = false; 147 if (touch_selection_controller_) 148 handles_hidden_due_to_scroll_ = true; 149 EndTouchEditing(); 150 break; 151 case ui::ET_GESTURE_SCROLL_END: 152 if (handles_hidden_due_to_scroll_ && 153 (selection_anchor_rect_ != selection_focus_rect_ || 154 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE)) { 155 StartTouchEditing(); 156 UpdateEditingController(); 157 } 158 break; 159 default: 160 break; 161 } 162 return false; 163} 164 165void TouchEditableImplAura::GestureEventAck(int gesture_event_type) { 166 DCHECK(rwhva_); 167 if (gesture_event_type == WebKit::WebInputEvent::GestureTap && 168 text_input_type_ != ui::TEXT_INPUT_TYPE_NONE && 169 selection_gesture_in_process_) { 170 StartTouchEditing(); 171 if (touch_selection_controller_) 172 touch_selection_controller_->SelectionChanged(); 173 } 174 175 if (gesture_event_type == WebKit::WebInputEvent::GestureLongPress || 176 gesture_event_type == WebKit::WebInputEvent::GestureTap) 177 selection_gesture_in_process_ = false; 178} 179 180void TouchEditableImplAura::OnViewDestroyed() { 181 Cleanup(); 182} 183 184//////////////////////////////////////////////////////////////////////////////// 185// TouchEditableImplAura, ui::TouchEditable implementation: 186 187void TouchEditableImplAura::SelectRect(const gfx::Point& start, 188 const gfx::Point& end) { 189 if (!rwhva_) 190 return; 191 192 RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( 193 rwhva_->GetRenderWidgetHost()); 194 host->SelectRange(start, end); 195} 196 197void TouchEditableImplAura::MoveCaretTo(const gfx::Point& point) { 198 if (!rwhva_) 199 return; 200 201 RenderWidgetHostImpl* host = RenderWidgetHostImpl::From( 202 rwhva_->GetRenderWidgetHost()); 203 host->MoveCaret(point); 204} 205 206void TouchEditableImplAura::GetSelectionEndPoints(gfx::Rect* p1, 207 gfx::Rect* p2) { 208 *p1 = selection_anchor_rect_; 209 *p2 = selection_focus_rect_; 210} 211 212gfx::Rect TouchEditableImplAura::GetBounds() { 213 return rwhva_ ? rwhva_->GetNativeView()->bounds() : gfx::Rect(); 214} 215 216gfx::NativeView TouchEditableImplAura::GetNativeView() { 217 return rwhva_ ? rwhva_->GetNativeView()->GetRootWindow() : NULL; 218} 219 220void TouchEditableImplAura::ConvertPointToScreen(gfx::Point* point) { 221 if (!rwhva_) 222 return; 223 aura::Window* window = rwhva_->GetNativeView(); 224 aura::client::ScreenPositionClient* screen_position_client = 225 aura::client::GetScreenPositionClient(window->GetRootWindow()); 226 if (screen_position_client) 227 screen_position_client->ConvertPointToScreen(window, point); 228} 229 230void TouchEditableImplAura::ConvertPointFromScreen(gfx::Point* point) { 231 if (!rwhva_) 232 return; 233 aura::Window* window = rwhva_->GetNativeView(); 234 aura::client::ScreenPositionClient* screen_position_client = 235 aura::client::GetScreenPositionClient(window->GetRootWindow()); 236 if (screen_position_client) 237 screen_position_client->ConvertPointFromScreen(window, point); 238} 239 240bool TouchEditableImplAura::DrawsHandles() { 241 return false; 242} 243 244void TouchEditableImplAura::OpenContextMenu(const gfx::Point& anchor) { 245 if (!rwhva_) 246 return; 247 gfx::Point point = anchor; 248 ConvertPointFromScreen(&point); 249 RenderWidgetHost* host = rwhva_->GetRenderWidgetHost(); 250 host->Send(new ViewMsg_ShowContextMenu(host->GetRoutingID(), point)); 251 EndTouchEditing(); 252} 253 254bool TouchEditableImplAura::IsCommandIdChecked(int command_id) const { 255 NOTREACHED(); 256 return false; 257} 258 259bool TouchEditableImplAura::IsCommandIdEnabled(int command_id) const { 260 if (!rwhva_) 261 return false; 262 bool editable = rwhva_->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE; 263 ui::Range selection_range; 264 rwhva_->GetSelectionRange(&selection_range); 265 bool has_selection = !selection_range.is_empty(); 266 switch (command_id) { 267 case IDS_APP_CUT: 268 return editable && has_selection; 269 case IDS_APP_COPY: 270 return has_selection; 271 case IDS_APP_PASTE: { 272 string16 result; 273 ui::Clipboard::GetForCurrentThread()->ReadText( 274 ui::Clipboard::BUFFER_STANDARD, &result); 275 return editable && !result.empty(); 276 } 277 case IDS_APP_DELETE: 278 return editable && has_selection; 279 case IDS_APP_SELECT_ALL: 280 return true; 281 default: 282 return false; 283 } 284} 285 286bool TouchEditableImplAura::GetAcceleratorForCommandId( 287 int command_id, 288 ui::Accelerator* accelerator) { 289 return false; 290} 291 292void TouchEditableImplAura::ExecuteCommand(int command_id, int event_flags) { 293 if (!rwhva_) 294 return; 295 RenderWidgetHost* host = rwhva_->GetRenderWidgetHost(); 296 switch (command_id) { 297 case IDS_APP_CUT: 298 host->Cut(); 299 break; 300 case IDS_APP_COPY: 301 host->Copy(); 302 break; 303 case IDS_APP_PASTE: 304 host->Paste(); 305 break; 306 case IDS_APP_DELETE: 307 host->Delete(); 308 break; 309 case IDS_APP_SELECT_ALL: 310 host->SelectAll(); 311 break; 312 default: 313 NOTREACHED(); 314 break; 315 } 316 EndTouchEditing(); 317} 318 319//////////////////////////////////////////////////////////////////////////////// 320// TouchEditableImplAura, private: 321 322TouchEditableImplAura::TouchEditableImplAura() 323 : text_input_type_(ui::TEXT_INPUT_TYPE_NONE), 324 rwhva_(NULL), 325 selection_gesture_in_process_(false), 326 handles_hidden_due_to_scroll_(false) { 327} 328 329void TouchEditableImplAura::Cleanup() { 330 if (rwhva_) { 331 rwhva_->set_touch_editing_client(NULL); 332 rwhva_ = NULL; 333 } 334 touch_selection_controller_.reset(); 335} 336 337} // namespace content 338