OLD | NEW |
(Empty) | |
| 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 "ui/views/controls/textfield/native_textfield_views.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <set> |
| 9 |
| 10 #include "base/bind.h" |
| 11 #include "base/debug/trace_event.h" |
| 12 #include "base/i18n/case_conversion.h" |
| 13 #include "base/logging.h" |
| 14 #include "base/message_loop/message_loop.h" |
| 15 #include "base/strings/utf_string_conversions.h" |
| 16 #include "grit/ui_strings.h" |
| 17 #include "third_party/icu/source/common/unicode/uchar.h" |
| 18 #include "third_party/skia/include/core/SkColor.h" |
| 19 #include "ui/base/clipboard/clipboard.h" |
| 20 #include "ui/base/dragdrop/drag_drop_types.h" |
| 21 #include "ui/base/dragdrop/drag_utils.h" |
| 22 #include "ui/base/l10n/l10n_util.h" |
| 23 #include "ui/base/ui_base_switches_util.h" |
| 24 #include "ui/compositor/layer.h" |
| 25 #include "ui/events/event.h" |
| 26 #include "ui/gfx/canvas.h" |
| 27 #include "ui/gfx/font_list.h" |
| 28 #include "ui/gfx/insets.h" |
| 29 #include "ui/gfx/range/range.h" |
| 30 #include "ui/gfx/render_text.h" |
| 31 #include "ui/gfx/text_constants.h" |
| 32 #include "ui/native_theme/native_theme.h" |
| 33 #include "ui/views/background.h" |
| 34 #include "ui/views/border.h" |
| 35 #include "ui/views/controls/focusable_border.h" |
| 36 #include "ui/views/controls/menu/menu_item_view.h" |
| 37 #include "ui/views/controls/menu/menu_model_adapter.h" |
| 38 #include "ui/views/controls/menu/menu_runner.h" |
| 39 #include "ui/views/controls/textfield/textfield.h" |
| 40 #include "ui/views/controls/textfield/textfield_controller.h" |
| 41 #include "ui/views/controls/textfield/textfield_views_model.h" |
| 42 #include "ui/views/drag_utils.h" |
| 43 #include "ui/views/ime/input_method.h" |
| 44 #include "ui/views/metrics.h" |
| 45 #include "ui/views/widget/widget.h" |
| 46 |
| 47 #if defined(USE_AURA) |
| 48 #include "ui/base/cursor/cursor.h" |
| 49 #endif |
| 50 |
| 51 #if defined(OS_WIN) && defined(USE_AURA) |
| 52 #include "base/win/win_util.h" |
| 53 #endif |
| 54 |
| 55 namespace { |
| 56 |
| 57 void ConvertRectToScreen(const views::View* src, gfx::Rect* r) { |
| 58 DCHECK(src); |
| 59 |
| 60 gfx::Point new_origin = r->origin(); |
| 61 views::View::ConvertPointToScreen(src, &new_origin); |
| 62 r->set_origin(new_origin); |
| 63 } |
| 64 |
| 65 } // namespace |
| 66 |
| 67 namespace views { |
| 68 |
| 69 const char NativeTextfieldViews::kViewClassName[] = |
| 70 "views/NativeTextfieldViews"; |
| 71 |
| 72 NativeTextfieldViews::NativeTextfieldViews(Textfield* parent) |
| 73 : textfield_(parent), |
| 74 model_(new TextfieldViewsModel(this)), |
| 75 text_border_(new FocusableBorder()), |
| 76 is_cursor_visible_(false), |
| 77 is_drop_cursor_visible_(false), |
| 78 skip_input_method_cancel_composition_(false), |
| 79 initiating_drag_(false), |
| 80 cursor_timer_(this), |
| 81 aggregated_clicks_(0) { |
| 82 set_border(text_border_); |
| 83 GetRenderText()->SetFontList(textfield_->font_list()); |
| 84 UpdateColorsFromTheme(GetNativeTheme()); |
| 85 set_context_menu_controller(this); |
| 86 parent->set_context_menu_controller(this); |
| 87 set_drag_controller(this); |
| 88 } |
| 89 |
| 90 NativeTextfieldViews::~NativeTextfieldViews() { |
| 91 } |
| 92 |
| 93 //////////////////////////////////////////////////////////////////////////////// |
| 94 // NativeTextfieldViews, View overrides: |
| 95 |
| 96 bool NativeTextfieldViews::OnMousePressed(const ui::MouseEvent& event) { |
| 97 OnBeforeUserAction(); |
| 98 TrackMouseClicks(event); |
| 99 |
| 100 TextfieldController* controller = textfield_->GetController(); |
| 101 if (!(controller && controller->HandleMouseEvent(textfield_, event)) && |
| 102 !textfield_->OnMousePressed(event)) { |
| 103 HandleMousePressEvent(event); |
| 104 } |
| 105 |
| 106 OnAfterUserAction(); |
| 107 touch_selection_controller_.reset(); |
| 108 return true; |
| 109 } |
| 110 |
| 111 bool NativeTextfieldViews::ExceededDragThresholdFromLastClickLocation( |
| 112 const ui::MouseEvent& event) { |
| 113 return ExceededDragThreshold(event.location() - last_click_location_); |
| 114 } |
| 115 |
| 116 bool NativeTextfieldViews::OnMouseDragged(const ui::MouseEvent& event) { |
| 117 // Don't adjust the cursor on a potential drag and drop, or if the mouse |
| 118 // movement from the last mouse click does not exceed the drag threshold. |
| 119 if (initiating_drag_ || !ExceededDragThresholdFromLastClickLocation(event) || |
| 120 !event.IsOnlyLeftMouseButton()) { |
| 121 return true; |
| 122 } |
| 123 |
| 124 OnBeforeUserAction(); |
| 125 // TODO: Remove once NativeTextfield implementations are consolidated to |
| 126 // Textfield. |
| 127 if (!textfield_->OnMouseDragged(event)) { |
| 128 MoveCursorTo(event.location(), true); |
| 129 if (aggregated_clicks_ == 1) { |
| 130 model_->SelectWord(); |
| 131 // Expand the selection so the initially selected word remains selected. |
| 132 gfx::Range selection = GetRenderText()->selection(); |
| 133 const size_t min = std::min(selection.GetMin(), |
| 134 double_click_word_.GetMin()); |
| 135 const size_t max = std::max(selection.GetMax(), |
| 136 double_click_word_.GetMax()); |
| 137 const bool reversed = selection.is_reversed(); |
| 138 selection.set_start(reversed ? max : min); |
| 139 selection.set_end(reversed ? min : max); |
| 140 model_->SelectRange(selection); |
| 141 } |
| 142 SchedulePaint(); |
| 143 } |
| 144 OnAfterUserAction(); |
| 145 return true; |
| 146 } |
| 147 |
| 148 void NativeTextfieldViews::OnMouseReleased(const ui::MouseEvent& event) { |
| 149 OnBeforeUserAction(); |
| 150 // TODO: Remove once NativeTextfield implementations are consolidated to |
| 151 // Textfield. |
| 152 textfield_->OnMouseReleased(event); |
| 153 // Cancel suspected drag initiations, the user was clicking in the selection. |
| 154 if (initiating_drag_ && MoveCursorTo(event.location(), false)) |
| 155 SchedulePaint(); |
| 156 initiating_drag_ = false; |
| 157 OnAfterUserAction(); |
| 158 } |
| 159 |
| 160 void NativeTextfieldViews::OnGestureEvent(ui::GestureEvent* event) { |
| 161 textfield_->OnGestureEvent(event); |
| 162 if (event->handled()) |
| 163 return; |
| 164 |
| 165 switch (event->type()) { |
| 166 case ui::ET_GESTURE_TAP_DOWN: |
| 167 OnBeforeUserAction(); |
| 168 textfield_->RequestFocus(); |
| 169 // We don't deselect if the point is in the selection |
| 170 // because TAP_DOWN may turn into a LONG_PRESS. |
| 171 if (!GetRenderText()->IsPointInSelection(event->location()) && |
| 172 MoveCursorTo(event->location(), false)) |
| 173 SchedulePaint(); |
| 174 OnAfterUserAction(); |
| 175 event->SetHandled(); |
| 176 break; |
| 177 case ui::ET_GESTURE_SCROLL_UPDATE: |
| 178 OnBeforeUserAction(); |
| 179 if (MoveCursorTo(event->location(), true)) |
| 180 SchedulePaint(); |
| 181 OnAfterUserAction(); |
| 182 event->SetHandled(); |
| 183 break; |
| 184 case ui::ET_GESTURE_SCROLL_END: |
| 185 case ui::ET_SCROLL_FLING_START: |
| 186 CreateTouchSelectionControllerAndNotifyIt(); |
| 187 event->SetHandled(); |
| 188 break; |
| 189 case ui::ET_GESTURE_TAP: |
| 190 if (event->details().tap_count() == 1) { |
| 191 CreateTouchSelectionControllerAndNotifyIt(); |
| 192 } else { |
| 193 OnBeforeUserAction(); |
| 194 SelectAll(false); |
| 195 OnAfterUserAction(); |
| 196 event->SetHandled(); |
| 197 } |
| 198 break; |
| 199 case ui::ET_GESTURE_LONG_PRESS: |
| 200 // If long press happens outside selection, select word and show context |
| 201 // menu (If touch selection is enabled, context menu is shown by the |
| 202 // |touch_selection_controller_|, hence we mark the event handled. |
| 203 // Otherwise, the regular context menu will be shown by views). |
| 204 // If long press happens in selected text and touch drag drop is enabled, |
| 205 // we will turn off touch selection (if one exists) and let views do drag |
| 206 // drop. |
| 207 if (!GetRenderText()->IsPointInSelection(event->location())) { |
| 208 OnBeforeUserAction(); |
| 209 model_->SelectWord(); |
| 210 touch_selection_controller_.reset( |
| 211 ui::TouchSelectionController::create(this)); |
| 212 OnCaretBoundsChanged(); |
| 213 SchedulePaint(); |
| 214 OnAfterUserAction(); |
| 215 if (touch_selection_controller_.get()) |
| 216 event->SetHandled(); |
| 217 } else if (switches::IsTouchDragDropEnabled()) { |
| 218 initiating_drag_ = true; |
| 219 touch_selection_controller_.reset(); |
| 220 } else { |
| 221 if (!touch_selection_controller_.get()) |
| 222 CreateTouchSelectionControllerAndNotifyIt(); |
| 223 if (touch_selection_controller_.get()) |
| 224 event->SetHandled(); |
| 225 } |
| 226 return; |
| 227 case ui::ET_GESTURE_LONG_TAP: |
| 228 if (!touch_selection_controller_.get()) |
| 229 CreateTouchSelectionControllerAndNotifyIt(); |
| 230 |
| 231 // If touch selection is enabled, the context menu on long tap will be |
| 232 // shown by the |touch_selection_controller_|, hence we mark the event |
| 233 // handled so views does not try to show context menu on it. |
| 234 if (touch_selection_controller_.get()) |
| 235 event->SetHandled(); |
| 236 break; |
| 237 default: |
| 238 View::OnGestureEvent(event); |
| 239 return; |
| 240 } |
| 241 PlatformGestureEventHandling(event); |
| 242 } |
| 243 |
| 244 bool NativeTextfieldViews::OnKeyPressed(const ui::KeyEvent& event) { |
| 245 // OnKeyPressed/OnKeyReleased/OnFocus/OnBlur will never be invoked on |
| 246 // NativeTextfieldViews as it will never gain focus. |
| 247 NOTREACHED(); |
| 248 return false; |
| 249 } |
| 250 |
| 251 bool NativeTextfieldViews::OnKeyReleased(const ui::KeyEvent& event) { |
| 252 NOTREACHED(); |
| 253 return false; |
| 254 } |
| 255 |
| 256 ui::TextInputClient* NativeTextfieldViews::GetTextInputClient() { |
| 257 return textfield_->read_only() ? NULL : this; |
| 258 } |
| 259 |
| 260 bool NativeTextfieldViews::GetDropFormats( |
| 261 int* formats, |
| 262 std::set<OSExchangeData::CustomFormat>* custom_formats) { |
| 263 if (!textfield_->enabled() || textfield_->read_only()) |
| 264 return false; |
| 265 // TODO(msw): Can we support URL, FILENAME, etc.? |
| 266 *formats = ui::OSExchangeData::STRING; |
| 267 TextfieldController* controller = textfield_->GetController(); |
| 268 if (controller) |
| 269 controller->AppendDropFormats(formats, custom_formats); |
| 270 return true; |
| 271 } |
| 272 |
| 273 bool NativeTextfieldViews::CanDrop(const OSExchangeData& data) { |
| 274 int formats; |
| 275 std::set<OSExchangeData::CustomFormat> custom_formats; |
| 276 GetDropFormats(&formats, &custom_formats); |
| 277 return textfield_->enabled() && !textfield_->read_only() && |
| 278 data.HasAnyFormat(formats, custom_formats); |
| 279 } |
| 280 |
| 281 int NativeTextfieldViews::OnDragUpdated(const ui::DropTargetEvent& event) { |
| 282 DCHECK(CanDrop(event.data())); |
| 283 |
| 284 const gfx::Range& selection = GetRenderText()->selection(); |
| 285 drop_cursor_position_ = GetRenderText()->FindCursorPosition(event.location()); |
| 286 bool in_selection = !selection.is_empty() && |
| 287 selection.Contains(gfx::Range(drop_cursor_position_.caret_pos())); |
| 288 is_drop_cursor_visible_ = !in_selection; |
| 289 // TODO(msw): Pan over text when the user drags to the visible text edge. |
| 290 OnCaretBoundsChanged(); |
| 291 SchedulePaint(); |
| 292 |
| 293 if (initiating_drag_) { |
| 294 if (in_selection) |
| 295 return ui::DragDropTypes::DRAG_NONE; |
| 296 return event.IsControlDown() ? ui::DragDropTypes::DRAG_COPY : |
| 297 ui::DragDropTypes::DRAG_MOVE; |
| 298 } |
| 299 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE; |
| 300 } |
| 301 |
| 302 void NativeTextfieldViews::OnDragExited() { |
| 303 is_drop_cursor_visible_ = false; |
| 304 SchedulePaint(); |
| 305 } |
| 306 |
| 307 int NativeTextfieldViews::OnPerformDrop(const ui::DropTargetEvent& event) { |
| 308 DCHECK(CanDrop(event.data())); |
| 309 |
| 310 is_drop_cursor_visible_ = false; |
| 311 |
| 312 TextfieldController* controller = textfield_->GetController(); |
| 313 if (controller) { |
| 314 int drag_operation = controller->OnDrop(event.data()); |
| 315 if (drag_operation != ui::DragDropTypes::DRAG_NONE) |
| 316 return drag_operation; |
| 317 } |
| 318 |
| 319 DCHECK(!initiating_drag_ || |
| 320 !GetRenderText()->IsPointInSelection(event.location())); |
| 321 OnBeforeUserAction(); |
| 322 skip_input_method_cancel_composition_ = true; |
| 323 |
| 324 gfx::SelectionModel drop_destination_model = |
| 325 GetRenderText()->FindCursorPosition(event.location()); |
| 326 base::string16 text; |
| 327 event.data().GetString(&text); |
| 328 text = GetTextForDisplay(text); |
| 329 |
| 330 // Delete the current selection for a drag and drop within this view. |
| 331 const bool move = initiating_drag_ && !event.IsControlDown() && |
| 332 event.source_operations() & ui::DragDropTypes::DRAG_MOVE; |
| 333 if (move) { |
| 334 // Adjust the drop destination if it is on or after the current selection. |
| 335 size_t drop = drop_destination_model.caret_pos(); |
| 336 drop -= GetSelectedRange().Intersect(gfx::Range(0, drop)).length(); |
| 337 model_->DeleteSelectionAndInsertTextAt(text, drop); |
| 338 } else { |
| 339 model_->MoveCursorTo(drop_destination_model); |
| 340 // Drop always inserts text even if the textfield is not in insert mode. |
| 341 model_->InsertText(text); |
| 342 } |
| 343 skip_input_method_cancel_composition_ = false; |
| 344 UpdateAfterChange(true, true); |
| 345 OnAfterUserAction(); |
| 346 return move ? ui::DragDropTypes::DRAG_MOVE : ui::DragDropTypes::DRAG_COPY; |
| 347 } |
| 348 |
| 349 void NativeTextfieldViews::OnDragDone() { |
| 350 initiating_drag_ = false; |
| 351 is_drop_cursor_visible_ = false; |
| 352 } |
| 353 |
| 354 void NativeTextfieldViews::OnPaint(gfx::Canvas* canvas) { |
| 355 OnPaintBackground(canvas); |
| 356 PaintTextAndCursor(canvas); |
| 357 if (textfield_->draw_border()) |
| 358 OnPaintBorder(canvas); |
| 359 } |
| 360 |
| 361 void NativeTextfieldViews::OnFocus() { |
| 362 NOTREACHED(); |
| 363 } |
| 364 |
| 365 void NativeTextfieldViews::OnBlur() { |
| 366 NOTREACHED(); |
| 367 } |
| 368 |
| 369 void NativeTextfieldViews::OnNativeThemeChanged(const ui::NativeTheme* theme) { |
| 370 UpdateColorsFromTheme(theme); |
| 371 } |
| 372 |
| 373 void NativeTextfieldViews::SelectRect(const gfx::Point& start, |
| 374 const gfx::Point& end) { |
| 375 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| 376 return; |
| 377 |
| 378 gfx::SelectionModel start_caret = GetRenderText()->FindCursorPosition(start); |
| 379 gfx::SelectionModel end_caret = GetRenderText()->FindCursorPosition(end); |
| 380 gfx::SelectionModel selection( |
| 381 gfx::Range(start_caret.caret_pos(), end_caret.caret_pos()), |
| 382 end_caret.caret_affinity()); |
| 383 |
| 384 OnBeforeUserAction(); |
| 385 model_->SelectSelectionModel(selection); |
| 386 OnCaretBoundsChanged(); |
| 387 SchedulePaint(); |
| 388 OnAfterUserAction(); |
| 389 } |
| 390 |
| 391 void NativeTextfieldViews::MoveCaretTo(const gfx::Point& point) { |
| 392 SelectRect(point, point); |
| 393 } |
| 394 |
| 395 void NativeTextfieldViews::GetSelectionEndPoints(gfx::Rect* p1, |
| 396 gfx::Rect* p2) { |
| 397 gfx::RenderText* render_text = GetRenderText(); |
| 398 const gfx::SelectionModel& sel = render_text->selection_model(); |
| 399 gfx::SelectionModel start_sel = |
| 400 render_text->GetSelectionModelForSelectionStart(); |
| 401 *p1 = render_text->GetCursorBounds(start_sel, true); |
| 402 *p2 = render_text->GetCursorBounds(sel, true); |
| 403 } |
| 404 |
| 405 gfx::Rect NativeTextfieldViews::GetBounds() { |
| 406 return bounds(); |
| 407 } |
| 408 |
| 409 gfx::NativeView NativeTextfieldViews::GetNativeView() { |
| 410 return GetWidget()->GetNativeView(); |
| 411 } |
| 412 |
| 413 void NativeTextfieldViews::ConvertPointToScreen(gfx::Point* point) { |
| 414 View::ConvertPointToScreen(this, point); |
| 415 } |
| 416 |
| 417 void NativeTextfieldViews::ConvertPointFromScreen(gfx::Point* point) { |
| 418 View::ConvertPointFromScreen(this, point); |
| 419 } |
| 420 |
| 421 bool NativeTextfieldViews::DrawsHandles() { |
| 422 return false; |
| 423 } |
| 424 |
| 425 void NativeTextfieldViews::OpenContextMenu(const gfx::Point& anchor) { |
| 426 touch_selection_controller_.reset(); |
| 427 ShowContextMenu(anchor, ui::MENU_SOURCE_TOUCH_EDIT_MENU); |
| 428 } |
| 429 |
| 430 gfx::NativeCursor NativeTextfieldViews::GetCursor(const ui::MouseEvent& event) { |
| 431 bool in_selection = GetRenderText()->IsPointInSelection(event.location()); |
| 432 bool drag_event = event.type() == ui::ET_MOUSE_DRAGGED; |
| 433 bool text_cursor = !initiating_drag_ && (drag_event || !in_selection); |
| 434 #if defined(USE_AURA) |
| 435 return text_cursor ? ui::kCursorIBeam : ui::kCursorNull; |
| 436 #elif defined(OS_WIN) |
| 437 static HCURSOR ibeam = LoadCursor(NULL, IDC_IBEAM); |
| 438 static HCURSOR arrow = LoadCursor(NULL, IDC_ARROW); |
| 439 return text_cursor ? ibeam : arrow; |
| 440 #endif |
| 441 } |
| 442 |
| 443 ///////////////////////////////////////////////////////////////// |
| 444 // NativeTextfieldViews, ContextMenuController overrides: |
| 445 void NativeTextfieldViews::ShowContextMenuForView( |
| 446 View* source, |
| 447 const gfx::Point& point, |
| 448 ui::MenuSourceType source_type) { |
| 449 UpdateContextMenu(); |
| 450 if (context_menu_runner_->RunMenuAt(GetWidget(), NULL, |
| 451 gfx::Rect(point, gfx::Size()), views::MenuItemView::TOPLEFT, |
| 452 source_type, |
| 453 MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) == |
| 454 MenuRunner::MENU_DELETED) |
| 455 return; |
| 456 } |
| 457 |
| 458 ///////////////////////////////////////////////////////////////// |
| 459 // NativeTextfieldViews, views::DragController overrides: |
| 460 void NativeTextfieldViews::WriteDragDataForView(views::View* sender, |
| 461 const gfx::Point& press_pt, |
| 462 OSExchangeData* data) { |
| 463 DCHECK_NE(ui::DragDropTypes::DRAG_NONE, |
| 464 GetDragOperationsForView(sender, press_pt)); |
| 465 data->SetString(GetSelectedText()); |
| 466 scoped_ptr<gfx::Canvas> canvas( |
| 467 views::GetCanvasForDragImage(textfield_->GetWidget(), size())); |
| 468 GetRenderText()->DrawSelectedTextForDrag(canvas.get()); |
| 469 drag_utils::SetDragImageOnDataObject(*canvas, size(), |
| 470 press_pt.OffsetFromOrigin(), |
| 471 data); |
| 472 TextfieldController* controller = textfield_->GetController(); |
| 473 if (controller) |
| 474 controller->OnWriteDragData(data); |
| 475 } |
| 476 |
| 477 int NativeTextfieldViews::GetDragOperationsForView(views::View* sender, |
| 478 const gfx::Point& p) { |
| 479 int drag_operations = ui::DragDropTypes::DRAG_COPY; |
| 480 if (!textfield_->enabled() || textfield_->IsObscured() || |
| 481 !GetRenderText()->IsPointInSelection(p)) |
| 482 drag_operations = ui::DragDropTypes::DRAG_NONE; |
| 483 else if (sender == this && !textfield_->read_only()) |
| 484 drag_operations = |
| 485 ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY; |
| 486 TextfieldController* controller = textfield_->GetController(); |
| 487 if (controller) |
| 488 controller->OnGetDragOperationsForTextfield(&drag_operations); |
| 489 return drag_operations; |
| 490 } |
| 491 |
| 492 bool NativeTextfieldViews::CanStartDragForView(View* sender, |
| 493 const gfx::Point& press_pt, |
| 494 const gfx::Point& p) { |
| 495 return initiating_drag_ && GetRenderText()->IsPointInSelection(press_pt); |
| 496 } |
| 497 |
| 498 ///////////////////////////////////////////////////////////////// |
| 499 // NativeTextfieldViews, NativeTextifieldWrapper overrides: |
| 500 |
| 501 base::string16 NativeTextfieldViews::GetText() const { |
| 502 return model_->GetText(); |
| 503 } |
| 504 |
| 505 void NativeTextfieldViews::UpdateText() { |
| 506 model_->SetText(GetTextForDisplay(textfield_->text())); |
| 507 OnCaretBoundsChanged(); |
| 508 SchedulePaint(); |
| 509 textfield_->NotifyAccessibilityEvent( |
| 510 ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true); |
| 511 } |
| 512 |
| 513 void NativeTextfieldViews::AppendText(const base::string16& text) { |
| 514 if (text.empty()) |
| 515 return; |
| 516 model_->Append(GetTextForDisplay(text)); |
| 517 OnCaretBoundsChanged(); |
| 518 SchedulePaint(); |
| 519 } |
| 520 |
| 521 void NativeTextfieldViews::InsertOrReplaceText(const base::string16& text) { |
| 522 if (text.empty()) |
| 523 return; |
| 524 model_->InsertText(text); |
| 525 OnCaretBoundsChanged(); |
| 526 SchedulePaint(); |
| 527 } |
| 528 |
| 529 base::i18n::TextDirection NativeTextfieldViews::GetTextDirection() const { |
| 530 return GetRenderText()->GetTextDirection(); |
| 531 } |
| 532 |
| 533 base::string16 NativeTextfieldViews::GetSelectedText() const { |
| 534 return model_->GetSelectedText(); |
| 535 } |
| 536 |
| 537 void NativeTextfieldViews::SelectAll(bool reversed) { |
| 538 model_->SelectAll(reversed); |
| 539 OnCaretBoundsChanged(); |
| 540 SchedulePaint(); |
| 541 } |
| 542 |
| 543 void NativeTextfieldViews::ClearSelection() { |
| 544 model_->ClearSelection(); |
| 545 OnCaretBoundsChanged(); |
| 546 SchedulePaint(); |
| 547 } |
| 548 |
| 549 void NativeTextfieldViews::UpdateBorder() { |
| 550 // By default, if a caller calls Textfield::RemoveBorder() and does not set |
| 551 // any explicit margins, they should get zero margins. But also call |
| 552 // UpdateXXXMargins() so we respect any explicitly-set margins. |
| 553 // |
| 554 // NOTE: If someday Textfield supports toggling |draw_border_| back on, we'll |
| 555 // need to update this conditional to set the insets to their default values. |
| 556 if (!textfield_->draw_border()) |
| 557 text_border_->SetInsets(0, 0, 0, 0); |
| 558 UpdateHorizontalMargins(); |
| 559 UpdateVerticalMargins(); |
| 560 } |
| 561 |
| 562 void NativeTextfieldViews::UpdateTextColor() { |
| 563 SetColor(textfield_->GetTextColor()); |
| 564 } |
| 565 |
| 566 void NativeTextfieldViews::UpdateBackgroundColor() { |
| 567 const SkColor color = textfield_->GetBackgroundColor(); |
| 568 set_background(Background::CreateSolidBackground(color)); |
| 569 GetRenderText()->set_background_is_transparent(SkColorGetA(color) != 0xFF); |
| 570 SchedulePaint(); |
| 571 } |
| 572 |
| 573 void NativeTextfieldViews::UpdateReadOnly() { |
| 574 OnTextInputTypeChanged(); |
| 575 } |
| 576 |
| 577 void NativeTextfieldViews::UpdateFont() { |
| 578 GetRenderText()->SetFontList(textfield_->font_list()); |
| 579 OnCaretBoundsChanged(); |
| 580 } |
| 581 |
| 582 void NativeTextfieldViews::UpdateIsObscured() { |
| 583 GetRenderText()->SetObscured(textfield_->IsObscured()); |
| 584 OnCaretBoundsChanged(); |
| 585 SchedulePaint(); |
| 586 OnTextInputTypeChanged(); |
| 587 } |
| 588 |
| 589 void NativeTextfieldViews::UpdateEnabled() { |
| 590 SetEnabled(textfield_->enabled()); |
| 591 SchedulePaint(); |
| 592 OnTextInputTypeChanged(); |
| 593 } |
| 594 |
| 595 void NativeTextfieldViews::UpdateHorizontalMargins() { |
| 596 int left, right; |
| 597 if (!textfield_->GetHorizontalMargins(&left, &right)) |
| 598 return; |
| 599 gfx::Insets inset = GetInsets(); |
| 600 text_border_->SetInsets(inset.top(), left, inset.bottom(), right); |
| 601 OnBoundsChanged(GetBounds()); |
| 602 } |
| 603 |
| 604 void NativeTextfieldViews::UpdateVerticalMargins() { |
| 605 int top, bottom; |
| 606 if (!textfield_->GetVerticalMargins(&top, &bottom)) |
| 607 return; |
| 608 gfx::Insets inset = GetInsets(); |
| 609 text_border_->SetInsets(top, inset.left(), bottom, inset.right()); |
| 610 OnBoundsChanged(GetBounds()); |
| 611 } |
| 612 |
| 613 bool NativeTextfieldViews::IsIMEComposing() const { |
| 614 return model_->HasCompositionText(); |
| 615 } |
| 616 |
| 617 const gfx::Range& NativeTextfieldViews::GetSelectedRange() const { |
| 618 return GetRenderText()->selection(); |
| 619 } |
| 620 |
| 621 void NativeTextfieldViews::SelectRange(const gfx::Range& range) { |
| 622 model_->SelectRange(range); |
| 623 OnCaretBoundsChanged(); |
| 624 SchedulePaint(); |
| 625 textfield_->NotifyAccessibilityEvent( |
| 626 ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true); |
| 627 } |
| 628 |
| 629 const gfx::SelectionModel& NativeTextfieldViews::GetSelectionModel() const { |
| 630 return GetRenderText()->selection_model(); |
| 631 } |
| 632 |
| 633 void NativeTextfieldViews::SelectSelectionModel( |
| 634 const gfx::SelectionModel& sel) { |
| 635 model_->SelectSelectionModel(sel); |
| 636 OnCaretBoundsChanged(); |
| 637 SchedulePaint(); |
| 638 } |
| 639 |
| 640 size_t NativeTextfieldViews::GetCursorPosition() const { |
| 641 return model_->GetCursorPosition(); |
| 642 } |
| 643 |
| 644 bool NativeTextfieldViews::GetCursorEnabled() const { |
| 645 return GetRenderText()->cursor_enabled(); |
| 646 } |
| 647 |
| 648 void NativeTextfieldViews::SetCursorEnabled(bool enabled) { |
| 649 GetRenderText()->SetCursorEnabled(enabled); |
| 650 } |
| 651 |
| 652 bool NativeTextfieldViews::HandleKeyPressed(const ui::KeyEvent& e) { |
| 653 TextfieldController* controller = textfield_->GetController(); |
| 654 bool handled = false; |
| 655 if (controller) |
| 656 handled = controller->HandleKeyEvent(textfield_, e); |
| 657 touch_selection_controller_.reset(); |
| 658 return handled || HandleKeyEvent(e); |
| 659 } |
| 660 |
| 661 bool NativeTextfieldViews::HandleKeyReleased(const ui::KeyEvent& e) { |
| 662 return false; // crbug.com/127520 |
| 663 } |
| 664 |
| 665 void NativeTextfieldViews::HandleFocus() { |
| 666 GetRenderText()->set_focused(true); |
| 667 is_cursor_visible_ = true; |
| 668 SchedulePaint(); |
| 669 GetInputMethod()->OnFocus(); |
| 670 OnCaretBoundsChanged(); |
| 671 |
| 672 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); |
| 673 if (caret_blink_ms != 0) { |
| 674 base::MessageLoop::current()->PostDelayedTask( |
| 675 FROM_HERE, |
| 676 base::Bind(&NativeTextfieldViews::UpdateCursor, |
| 677 cursor_timer_.GetWeakPtr()), |
| 678 base::TimeDelta::FromMilliseconds(caret_blink_ms)); |
| 679 } |
| 680 } |
| 681 |
| 682 void NativeTextfieldViews::HandleBlur() { |
| 683 GetRenderText()->set_focused(false); |
| 684 GetInputMethod()->OnBlur(); |
| 685 // Stop blinking cursor. |
| 686 cursor_timer_.InvalidateWeakPtrs(); |
| 687 if (is_cursor_visible_) { |
| 688 is_cursor_visible_ = false; |
| 689 RepaintCursor(); |
| 690 } |
| 691 |
| 692 touch_selection_controller_.reset(); |
| 693 } |
| 694 |
| 695 void NativeTextfieldViews::ClearEditHistory() { |
| 696 model_->ClearEditHistory(); |
| 697 } |
| 698 |
| 699 int NativeTextfieldViews::GetFontHeight() { |
| 700 return GetRenderText()->font_list().GetHeight(); |
| 701 } |
| 702 |
| 703 int NativeTextfieldViews::GetTextfieldBaseline() const { |
| 704 return GetRenderText()->GetBaseline(); |
| 705 } |
| 706 |
| 707 int NativeTextfieldViews::GetWidthNeededForText() const { |
| 708 return GetRenderText()->GetContentWidth() + GetInsets().width(); |
| 709 } |
| 710 |
| 711 bool NativeTextfieldViews::HasTextBeingDragged() { |
| 712 return initiating_drag_; |
| 713 } |
| 714 |
| 715 gfx::Point NativeTextfieldViews::GetContextMenuLocation() { |
| 716 return GetCaretBounds().bottom_right(); |
| 717 } |
| 718 |
| 719 ///////////////////////////////////////////////////////////////// |
| 720 // NativeTextfieldViews, ui::SimpleMenuModel::Delegate overrides: |
| 721 |
| 722 bool NativeTextfieldViews::IsCommandIdChecked(int command_id) const { |
| 723 return true; |
| 724 } |
| 725 |
| 726 bool NativeTextfieldViews::IsCommandIdEnabled(int command_id) const { |
| 727 TextfieldController* controller = textfield_->GetController(); |
| 728 if (controller && controller->HandlesCommand(command_id)) |
| 729 return controller->IsCommandIdEnabled(command_id); |
| 730 |
| 731 bool editable = !textfield_->read_only(); |
| 732 base::string16 result; |
| 733 switch (command_id) { |
| 734 case IDS_APP_UNDO: |
| 735 return editable && model_->CanUndo(); |
| 736 case IDS_APP_CUT: |
| 737 return editable && model_->HasSelection() && !textfield_->IsObscured(); |
| 738 case IDS_APP_COPY: |
| 739 return model_->HasSelection() && !textfield_->IsObscured(); |
| 740 case IDS_APP_PASTE: |
| 741 ui::Clipboard::GetForCurrentThread()->ReadText( |
| 742 ui::CLIPBOARD_TYPE_COPY_PASTE, &result); |
| 743 return editable && !result.empty(); |
| 744 case IDS_APP_DELETE: |
| 745 return editable && model_->HasSelection(); |
| 746 case IDS_APP_SELECT_ALL: |
| 747 return !model_->GetText().empty(); |
| 748 default: |
| 749 return controller->IsCommandIdEnabled(command_id); |
| 750 } |
| 751 } |
| 752 |
| 753 bool NativeTextfieldViews::GetAcceleratorForCommandId(int command_id, |
| 754 ui::Accelerator* accelerator) { |
| 755 return false; |
| 756 } |
| 757 |
| 758 bool NativeTextfieldViews::IsItemForCommandIdDynamic(int command_id) const { |
| 759 const TextfieldController* controller = textfield_->GetController(); |
| 760 return controller && controller->IsItemForCommandIdDynamic(command_id); |
| 761 } |
| 762 |
| 763 base::string16 NativeTextfieldViews::GetLabelForCommandId( |
| 764 int command_id) const { |
| 765 const TextfieldController* controller = textfield_->GetController(); |
| 766 return controller ? controller->GetLabelForCommandId(command_id) : |
| 767 base::string16(); |
| 768 } |
| 769 |
| 770 void NativeTextfieldViews::ExecuteCommand(int command_id, int event_flags) { |
| 771 touch_selection_controller_.reset(); |
| 772 if (!IsCommandIdEnabled(command_id)) |
| 773 return; |
| 774 |
| 775 TextfieldController* controller = textfield_->GetController(); |
| 776 if (controller && controller->HandlesCommand(command_id)) { |
| 777 controller->ExecuteCommand(command_id, 0); |
| 778 } else { |
| 779 bool text_changed = false; |
| 780 switch (command_id) { |
| 781 case IDS_APP_UNDO: |
| 782 OnBeforeUserAction(); |
| 783 text_changed = model_->Undo(); |
| 784 UpdateAfterChange(text_changed, text_changed); |
| 785 OnAfterUserAction(); |
| 786 break; |
| 787 case IDS_APP_CUT: |
| 788 OnBeforeUserAction(); |
| 789 text_changed = Cut(); |
| 790 UpdateAfterChange(text_changed, text_changed); |
| 791 OnAfterUserAction(); |
| 792 break; |
| 793 case IDS_APP_COPY: |
| 794 OnBeforeUserAction(); |
| 795 Copy(); |
| 796 OnAfterUserAction(); |
| 797 break; |
| 798 case IDS_APP_PASTE: |
| 799 OnBeforeUserAction(); |
| 800 text_changed = Paste(); |
| 801 UpdateAfterChange(text_changed, text_changed); |
| 802 OnAfterUserAction(); |
| 803 break; |
| 804 case IDS_APP_DELETE: |
| 805 OnBeforeUserAction(); |
| 806 text_changed = model_->Delete(); |
| 807 UpdateAfterChange(text_changed, text_changed); |
| 808 OnAfterUserAction(); |
| 809 break; |
| 810 case IDS_APP_SELECT_ALL: |
| 811 OnBeforeUserAction(); |
| 812 SelectAll(false); |
| 813 UpdateAfterChange(false, true); |
| 814 OnAfterUserAction(); |
| 815 break; |
| 816 default: |
| 817 controller->ExecuteCommand(command_id, 0); |
| 818 break; |
| 819 } |
| 820 } |
| 821 } |
| 822 |
| 823 void NativeTextfieldViews::SetColor(SkColor value) { |
| 824 GetRenderText()->SetColor(value); |
| 825 SchedulePaint(); |
| 826 } |
| 827 |
| 828 void NativeTextfieldViews::ApplyColor(SkColor value, const gfx::Range& range) { |
| 829 GetRenderText()->ApplyColor(value, range); |
| 830 SchedulePaint(); |
| 831 } |
| 832 |
| 833 void NativeTextfieldViews::SetStyle(gfx::TextStyle style, bool value) { |
| 834 GetRenderText()->SetStyle(style, value); |
| 835 SchedulePaint(); |
| 836 } |
| 837 |
| 838 void NativeTextfieldViews::ApplyStyle(gfx::TextStyle style, |
| 839 bool value, |
| 840 const gfx::Range& range) { |
| 841 GetRenderText()->ApplyStyle(style, value, range); |
| 842 SchedulePaint(); |
| 843 } |
| 844 |
| 845 void NativeTextfieldViews::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
| 846 // Set the RenderText display area. |
| 847 gfx::Insets insets = GetInsets(); |
| 848 gfx::Rect display_rect(insets.left(), |
| 849 insets.top(), |
| 850 width() - insets.width(), |
| 851 height() - insets.height()); |
| 852 GetRenderText()->SetDisplayRect(display_rect); |
| 853 OnCaretBoundsChanged(); |
| 854 } |
| 855 |
| 856 /////////////////////////////////////////////////////////////////////////////// |
| 857 // NativeTextfieldViews, ui::TextInputClient implementation, private: |
| 858 |
| 859 void NativeTextfieldViews::SetCompositionText( |
| 860 const ui::CompositionText& composition) { |
| 861 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) |
| 862 return; |
| 863 |
| 864 OnBeforeUserAction(); |
| 865 skip_input_method_cancel_composition_ = true; |
| 866 model_->SetCompositionText(composition); |
| 867 skip_input_method_cancel_composition_ = false; |
| 868 UpdateAfterChange(true, true); |
| 869 OnAfterUserAction(); |
| 870 } |
| 871 |
| 872 void NativeTextfieldViews::ConfirmCompositionText() { |
| 873 if (!model_->HasCompositionText()) |
| 874 return; |
| 875 |
| 876 OnBeforeUserAction(); |
| 877 skip_input_method_cancel_composition_ = true; |
| 878 model_->ConfirmCompositionText(); |
| 879 skip_input_method_cancel_composition_ = false; |
| 880 UpdateAfterChange(true, true); |
| 881 OnAfterUserAction(); |
| 882 } |
| 883 |
| 884 void NativeTextfieldViews::ClearCompositionText() { |
| 885 if (!model_->HasCompositionText()) |
| 886 return; |
| 887 |
| 888 OnBeforeUserAction(); |
| 889 skip_input_method_cancel_composition_ = true; |
| 890 model_->CancelCompositionText(); |
| 891 skip_input_method_cancel_composition_ = false; |
| 892 UpdateAfterChange(true, true); |
| 893 OnAfterUserAction(); |
| 894 } |
| 895 |
| 896 void NativeTextfieldViews::InsertText(const base::string16& text) { |
| 897 // TODO(suzhe): Filter invalid characters. |
| 898 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty()) |
| 899 return; |
| 900 |
| 901 OnBeforeUserAction(); |
| 902 skip_input_method_cancel_composition_ = true; |
| 903 if (GetRenderText()->insert_mode()) |
| 904 model_->InsertText(GetTextForDisplay(text)); |
| 905 else |
| 906 model_->ReplaceText(GetTextForDisplay(text)); |
| 907 skip_input_method_cancel_composition_ = false; |
| 908 UpdateAfterChange(true, true); |
| 909 OnAfterUserAction(); |
| 910 } |
| 911 |
| 912 void NativeTextfieldViews::InsertChar(base::char16 ch, int flags) { |
| 913 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || |
| 914 !ShouldInsertChar(ch, flags)) { |
| 915 return; |
| 916 } |
| 917 |
| 918 OnBeforeUserAction(); |
| 919 skip_input_method_cancel_composition_ = true; |
| 920 if (GetRenderText()->insert_mode()) |
| 921 model_->InsertChar(ch); |
| 922 else |
| 923 model_->ReplaceChar(ch); |
| 924 skip_input_method_cancel_composition_ = false; |
| 925 |
| 926 model_->SetText(GetTextForDisplay(GetText())); |
| 927 |
| 928 UpdateAfterChange(true, true); |
| 929 OnAfterUserAction(); |
| 930 |
| 931 if (textfield_->IsObscured()) { |
| 932 const base::TimeDelta& reveal_duration = |
| 933 textfield_->obscured_reveal_duration(); |
| 934 if (reveal_duration != base::TimeDelta()) { |
| 935 const size_t change_offset = model_->GetCursorPosition(); |
| 936 DCHECK_GT(change_offset, 0u); |
| 937 RevealObscuredChar(change_offset - 1, reveal_duration); |
| 938 } |
| 939 } |
| 940 } |
| 941 |
| 942 gfx::NativeWindow NativeTextfieldViews::GetAttachedWindow() const { |
| 943 // Imagine the following hierarchy. |
| 944 // [NativeWidget A] - FocusManager |
| 945 // [View] |
| 946 // [NativeWidget B] |
| 947 // [View] |
| 948 // [View X] |
| 949 // An important thing is that [NativeWidget A] owns Win32 input focus even |
| 950 // when [View X] is logically focused by FocusManager. As a result, an Win32 |
| 951 // IME may want to interact with the native view of [NativeWidget A] rather |
| 952 // than that of [NativeWidget B]. This is why we need to call |
| 953 // GetTopLevelWidget() here. |
| 954 return GetWidget()->GetTopLevelWidget()->GetNativeView(); |
| 955 } |
| 956 |
| 957 ui::TextInputType NativeTextfieldViews::GetTextInputType() const { |
| 958 return textfield_->GetTextInputType(); |
| 959 } |
| 960 |
| 961 ui::TextInputMode NativeTextfieldViews::GetTextInputMode() const { |
| 962 return ui::TEXT_INPUT_MODE_DEFAULT; |
| 963 } |
| 964 |
| 965 bool NativeTextfieldViews::CanComposeInline() const { |
| 966 return true; |
| 967 } |
| 968 |
| 969 gfx::Rect NativeTextfieldViews::GetCaretBounds() const { |
| 970 // TextInputClient::GetCaretBounds is expected to return a value in screen |
| 971 // coordinates. |
| 972 gfx::Rect rect = GetRenderText()->GetUpdatedCursorBounds(); |
| 973 ConvertRectToScreen(this, &rect); |
| 974 return rect; |
| 975 } |
| 976 |
| 977 bool NativeTextfieldViews::GetCompositionCharacterBounds( |
| 978 uint32 index, |
| 979 gfx::Rect* rect) const { |
| 980 DCHECK(rect); |
| 981 if (!HasCompositionText()) |
| 982 return false; |
| 983 const gfx::Range& composition_range = GetRenderText()->GetCompositionRange(); |
| 984 DCHECK(!composition_range.is_empty()); |
| 985 |
| 986 size_t text_index = composition_range.start() + index; |
| 987 if (composition_range.end() <= text_index) |
| 988 return false; |
| 989 if (!GetRenderText()->IsCursorablePosition(text_index)) { |
| 990 text_index = GetRenderText()->IndexOfAdjacentGrapheme( |
| 991 text_index, gfx::CURSOR_BACKWARD); |
| 992 } |
| 993 if (text_index < composition_range.start()) |
| 994 return false; |
| 995 const gfx::SelectionModel caret(text_index, gfx::CURSOR_BACKWARD); |
| 996 *rect = GetRenderText()->GetCursorBounds(caret, false); |
| 997 ConvertRectToScreen(this, rect); |
| 998 |
| 999 return true; |
| 1000 } |
| 1001 |
| 1002 bool NativeTextfieldViews::HasCompositionText() const { |
| 1003 return model_->HasCompositionText(); |
| 1004 } |
| 1005 |
| 1006 bool NativeTextfieldViews::GetTextRange(gfx::Range* range) const { |
| 1007 if (!ImeEditingAllowed()) |
| 1008 return false; |
| 1009 |
| 1010 model_->GetTextRange(range); |
| 1011 return true; |
| 1012 } |
| 1013 |
| 1014 bool NativeTextfieldViews::GetCompositionTextRange(gfx::Range* range) const { |
| 1015 if (!ImeEditingAllowed()) |
| 1016 return false; |
| 1017 |
| 1018 model_->GetCompositionTextRange(range); |
| 1019 return true; |
| 1020 } |
| 1021 |
| 1022 bool NativeTextfieldViews::GetSelectionRange(gfx::Range* range) const { |
| 1023 if (!ImeEditingAllowed()) |
| 1024 return false; |
| 1025 *range = GetSelectedRange(); |
| 1026 return true; |
| 1027 } |
| 1028 |
| 1029 bool NativeTextfieldViews::SetSelectionRange(const gfx::Range& range) { |
| 1030 if (!ImeEditingAllowed() || !range.IsValid()) |
| 1031 return false; |
| 1032 |
| 1033 OnBeforeUserAction(); |
| 1034 SelectRange(range); |
| 1035 OnAfterUserAction(); |
| 1036 return true; |
| 1037 } |
| 1038 |
| 1039 bool NativeTextfieldViews::DeleteRange(const gfx::Range& range) { |
| 1040 if (!ImeEditingAllowed() || range.is_empty()) |
| 1041 return false; |
| 1042 |
| 1043 OnBeforeUserAction(); |
| 1044 model_->SelectRange(range); |
| 1045 if (model_->HasSelection()) { |
| 1046 model_->DeleteSelection(); |
| 1047 UpdateAfterChange(true, true); |
| 1048 } |
| 1049 OnAfterUserAction(); |
| 1050 return true; |
| 1051 } |
| 1052 |
| 1053 bool NativeTextfieldViews::GetTextFromRange( |
| 1054 const gfx::Range& range, |
| 1055 base::string16* text) const { |
| 1056 if (!ImeEditingAllowed() || !range.IsValid()) |
| 1057 return false; |
| 1058 |
| 1059 gfx::Range text_range; |
| 1060 if (!GetTextRange(&text_range) || !text_range.Contains(range)) |
| 1061 return false; |
| 1062 |
| 1063 *text = model_->GetTextFromRange(range); |
| 1064 return true; |
| 1065 } |
| 1066 |
| 1067 void NativeTextfieldViews::OnInputMethodChanged() { |
| 1068 // TODO(msw): NOTIMPLEMENTED(); see http://crbug.com/140402 |
| 1069 } |
| 1070 |
| 1071 bool NativeTextfieldViews::ChangeTextDirectionAndLayoutAlignment( |
| 1072 base::i18n::TextDirection direction) { |
| 1073 // Restore text directionality mode when the indicated direction matches the |
| 1074 // current forced mode; otherwise, force the mode indicated. This helps users |
| 1075 // manage BiDi text layout without getting stuck in forced LTR or RTL modes. |
| 1076 const gfx::DirectionalityMode mode = direction == base::i18n::RIGHT_TO_LEFT ? |
| 1077 gfx::DIRECTIONALITY_FORCE_RTL : gfx::DIRECTIONALITY_FORCE_LTR; |
| 1078 if (mode == GetRenderText()->directionality_mode()) |
| 1079 GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT); |
| 1080 else |
| 1081 GetRenderText()->SetDirectionalityMode(mode); |
| 1082 SchedulePaint(); |
| 1083 return true; |
| 1084 } |
| 1085 |
| 1086 void NativeTextfieldViews::ExtendSelectionAndDelete( |
| 1087 size_t before, |
| 1088 size_t after) { |
| 1089 gfx::Range range = GetSelectedRange(); |
| 1090 DCHECK_GE(range.start(), before); |
| 1091 |
| 1092 range.set_start(range.start() - before); |
| 1093 range.set_end(range.end() + after); |
| 1094 gfx::Range text_range; |
| 1095 if (GetTextRange(&text_range) && text_range.Contains(range)) |
| 1096 DeleteRange(range); |
| 1097 } |
| 1098 |
| 1099 void NativeTextfieldViews::EnsureCaretInRect(const gfx::Rect& rect) { |
| 1100 } |
| 1101 |
| 1102 void NativeTextfieldViews::OnCandidateWindowShown() { |
| 1103 } |
| 1104 |
| 1105 void NativeTextfieldViews::OnCandidateWindowUpdated() { |
| 1106 } |
| 1107 |
| 1108 void NativeTextfieldViews::OnCandidateWindowHidden() { |
| 1109 } |
| 1110 |
| 1111 void NativeTextfieldViews::OnCompositionTextConfirmedOrCleared() { |
| 1112 if (skip_input_method_cancel_composition_) |
| 1113 return; |
| 1114 DCHECK(textfield_->GetInputMethod()); |
| 1115 textfield_->GetInputMethod()->CancelComposition(textfield_); |
| 1116 } |
| 1117 |
| 1118 gfx::RenderText* NativeTextfieldViews::GetRenderText() const { |
| 1119 return model_->render_text(); |
| 1120 } |
| 1121 |
| 1122 base::string16 NativeTextfieldViews::GetTextForDisplay( |
| 1123 const base::string16& text) { |
| 1124 return textfield_->style() & Textfield::STYLE_LOWERCASE ? |
| 1125 base::i18n::ToLower(text) : text; |
| 1126 } |
| 1127 |
| 1128 void NativeTextfieldViews::UpdateColorsFromTheme(const ui::NativeTheme* theme) { |
| 1129 UpdateTextColor(); |
| 1130 UpdateBackgroundColor(); |
| 1131 gfx::RenderText* render_text = GetRenderText(); |
| 1132 render_text->set_cursor_color(textfield_->GetTextColor()); |
| 1133 render_text->set_selection_color(theme->GetSystemColor( |
| 1134 ui::NativeTheme::kColorId_TextfieldSelectionColor)); |
| 1135 render_text->set_selection_background_focused_color(theme->GetSystemColor( |
| 1136 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused)); |
| 1137 } |
| 1138 |
| 1139 void NativeTextfieldViews::UpdateCursor() { |
| 1140 const size_t caret_blink_ms = Textfield::GetCaretBlinkMs(); |
| 1141 is_cursor_visible_ = !is_cursor_visible_ || (caret_blink_ms == 0); |
| 1142 RepaintCursor(); |
| 1143 if (caret_blink_ms != 0) { |
| 1144 base::MessageLoop::current()->PostDelayedTask( |
| 1145 FROM_HERE, |
| 1146 base::Bind(&NativeTextfieldViews::UpdateCursor, |
| 1147 cursor_timer_.GetWeakPtr()), |
| 1148 base::TimeDelta::FromMilliseconds(caret_blink_ms)); |
| 1149 } |
| 1150 } |
| 1151 |
| 1152 void NativeTextfieldViews::RepaintCursor() { |
| 1153 gfx::Rect r(GetRenderText()->GetUpdatedCursorBounds()); |
| 1154 r.Inset(-1, -1, -1, -1); |
| 1155 SchedulePaintInRect(r); |
| 1156 } |
| 1157 |
| 1158 void NativeTextfieldViews::PaintTextAndCursor(gfx::Canvas* canvas) { |
| 1159 TRACE_EVENT0("views", "NativeTextfieldViews::PaintTextAndCursor"); |
| 1160 canvas->Save(); |
| 1161 GetRenderText()->set_cursor_visible(!is_drop_cursor_visible_ && |
| 1162 is_cursor_visible_ && !model_->HasSelection()); |
| 1163 // Draw the text, cursor, and selection. |
| 1164 GetRenderText()->Draw(canvas); |
| 1165 |
| 1166 // Draw the detached drop cursor that marks where the text will be dropped. |
| 1167 if (is_drop_cursor_visible_) |
| 1168 GetRenderText()->DrawCursor(canvas, drop_cursor_position_); |
| 1169 |
| 1170 // Draw placeholder text if needed. |
| 1171 if (model_->GetText().empty() && |
| 1172 !textfield_->GetPlaceholderText().empty()) { |
| 1173 canvas->DrawStringRect( |
| 1174 textfield_->GetPlaceholderText(), |
| 1175 GetRenderText()->font_list(), |
| 1176 textfield_->placeholder_text_color(), |
| 1177 GetRenderText()->display_rect()); |
| 1178 } |
| 1179 canvas->Restore(); |
| 1180 } |
| 1181 |
| 1182 bool NativeTextfieldViews::HandleKeyEvent(const ui::KeyEvent& key_event) { |
| 1183 // TODO(oshima): Refactor and consolidate with ExecuteCommand. |
| 1184 if (key_event.type() == ui::ET_KEY_PRESSED) { |
| 1185 ui::KeyboardCode key_code = key_event.key_code(); |
| 1186 if (key_code == ui::VKEY_TAB || key_event.IsUnicodeKeyCode()) |
| 1187 return false; |
| 1188 |
| 1189 OnBeforeUserAction(); |
| 1190 const bool editable = !textfield_->read_only(); |
| 1191 const bool readable = !textfield_->IsObscured(); |
| 1192 const bool shift = key_event.IsShiftDown(); |
| 1193 const bool control = key_event.IsControlDown(); |
| 1194 const bool alt = key_event.IsAltDown() || key_event.IsAltGrDown(); |
| 1195 bool text_changed = false; |
| 1196 bool cursor_changed = false; |
| 1197 switch (key_code) { |
| 1198 case ui::VKEY_Z: |
| 1199 if (control && !shift && !alt && editable) |
| 1200 cursor_changed = text_changed = model_->Undo(); |
| 1201 else if (control && shift && !alt && editable) |
| 1202 cursor_changed = text_changed = model_->Redo(); |
| 1203 break; |
| 1204 case ui::VKEY_Y: |
| 1205 if (control && !alt && editable) |
| 1206 cursor_changed = text_changed = model_->Redo(); |
| 1207 break; |
| 1208 case ui::VKEY_A: |
| 1209 if (control && !alt) { |
| 1210 model_->SelectAll(false); |
| 1211 cursor_changed = true; |
| 1212 } |
| 1213 break; |
| 1214 case ui::VKEY_X: |
| 1215 if (control && !alt && editable && readable) |
| 1216 cursor_changed = text_changed = Cut(); |
| 1217 break; |
| 1218 case ui::VKEY_C: |
| 1219 if (control && !alt && readable) |
| 1220 Copy(); |
| 1221 break; |
| 1222 case ui::VKEY_V: |
| 1223 if (control && !alt && editable) |
| 1224 cursor_changed = text_changed = Paste(); |
| 1225 break; |
| 1226 case ui::VKEY_RIGHT: |
| 1227 case ui::VKEY_LEFT: { |
| 1228 // We should ignore the alt-left/right keys because alt key doesn't make |
| 1229 // any special effects for them and they can be shortcut keys such like |
| 1230 // forward/back of the browser history. |
| 1231 if (alt) |
| 1232 break; |
| 1233 const gfx::Range selection_range = GetSelectedRange(); |
| 1234 model_->MoveCursor( |
| 1235 control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, |
| 1236 (key_code == ui::VKEY_RIGHT) ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT, |
| 1237 shift); |
| 1238 cursor_changed = GetSelectedRange() != selection_range; |
| 1239 break; |
| 1240 } |
| 1241 case ui::VKEY_END: |
| 1242 case ui::VKEY_HOME: |
| 1243 if ((key_code == ui::VKEY_HOME) == |
| 1244 (GetRenderText()->GetTextDirection() == base::i18n::RIGHT_TO_LEFT)) |
| 1245 model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, shift); |
| 1246 else |
| 1247 model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, shift); |
| 1248 cursor_changed = true; |
| 1249 break; |
| 1250 case ui::VKEY_BACK: |
| 1251 case ui::VKEY_DELETE: |
| 1252 if (!editable) |
| 1253 break; |
| 1254 if (!model_->HasSelection()) { |
| 1255 gfx::VisualCursorDirection direction = (key_code == ui::VKEY_DELETE) ? |
| 1256 gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT; |
| 1257 if (shift && control) { |
| 1258 // If both shift and control are pressed, then erase up to the |
| 1259 // beginning/end of the buffer in ChromeOS. In windows, do nothing. |
| 1260 #if defined(OS_WIN) |
| 1261 break; |
| 1262 #else |
| 1263 model_->MoveCursor(gfx::LINE_BREAK, direction, true); |
| 1264 #endif |
| 1265 } else if (control) { |
| 1266 // If only control is pressed, then erase the previous/next word. |
| 1267 model_->MoveCursor(gfx::WORD_BREAK, direction, true); |
| 1268 } |
| 1269 } |
| 1270 if (key_code == ui::VKEY_BACK) |
| 1271 model_->Backspace(); |
| 1272 else if (shift && model_->HasSelection() && readable) |
| 1273 Cut(); |
| 1274 else |
| 1275 model_->Delete(); |
| 1276 |
| 1277 // Consume backspace and delete keys even if the edit did nothing. This |
| 1278 // prevents potential unintended side-effects of further event handling. |
| 1279 text_changed = true; |
| 1280 break; |
| 1281 case ui::VKEY_INSERT: |
| 1282 if (control && !shift && readable) |
| 1283 Copy(); |
| 1284 else if (shift && !control && editable) |
| 1285 cursor_changed = text_changed = Paste(); |
| 1286 break; |
| 1287 default: |
| 1288 break; |
| 1289 } |
| 1290 |
| 1291 // We must have input method in order to support text input. |
| 1292 DCHECK(textfield_->GetInputMethod()); |
| 1293 |
| 1294 UpdateAfterChange(text_changed, cursor_changed); |
| 1295 OnAfterUserAction(); |
| 1296 return (text_changed || cursor_changed); |
| 1297 } |
| 1298 return false; |
| 1299 } |
| 1300 |
| 1301 bool NativeTextfieldViews::MoveCursorTo(const gfx::Point& point, bool select) { |
| 1302 if (!model_->MoveCursorTo(point, select)) |
| 1303 return false; |
| 1304 OnCaretBoundsChanged(); |
| 1305 return true; |
| 1306 } |
| 1307 |
| 1308 void NativeTextfieldViews::PropagateTextChange() { |
| 1309 textfield_->SyncText(); |
| 1310 } |
| 1311 |
| 1312 void NativeTextfieldViews::UpdateAfterChange(bool text_changed, |
| 1313 bool cursor_changed) { |
| 1314 if (text_changed) { |
| 1315 PropagateTextChange(); |
| 1316 textfield_->NotifyAccessibilityEvent( |
| 1317 ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true); |
| 1318 } |
| 1319 if (cursor_changed) { |
| 1320 is_cursor_visible_ = true; |
| 1321 RepaintCursor(); |
| 1322 if (!text_changed) { |
| 1323 // TEXT_CHANGED implies SELECTION_CHANGED, so we only need to fire |
| 1324 // this if only the selection changed. |
| 1325 textfield_->NotifyAccessibilityEvent( |
| 1326 ui::AccessibilityTypes::EVENT_SELECTION_CHANGED, true); |
| 1327 } |
| 1328 } |
| 1329 if (text_changed || cursor_changed) { |
| 1330 OnCaretBoundsChanged(); |
| 1331 SchedulePaint(); |
| 1332 } |
| 1333 } |
| 1334 |
| 1335 void NativeTextfieldViews::UpdateContextMenu() { |
| 1336 if (!context_menu_contents_.get()) { |
| 1337 context_menu_contents_.reset(new ui::SimpleMenuModel(this)); |
| 1338 context_menu_contents_->AddItemWithStringId(IDS_APP_UNDO, IDS_APP_UNDO); |
| 1339 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); |
| 1340 context_menu_contents_->AddItemWithStringId(IDS_APP_CUT, IDS_APP_CUT); |
| 1341 context_menu_contents_->AddItemWithStringId(IDS_APP_COPY, IDS_APP_COPY); |
| 1342 context_menu_contents_->AddItemWithStringId(IDS_APP_PASTE, IDS_APP_PASTE); |
| 1343 context_menu_contents_->AddItemWithStringId(IDS_APP_DELETE, IDS_APP_DELETE); |
| 1344 context_menu_contents_->AddSeparator(ui::NORMAL_SEPARATOR); |
| 1345 context_menu_contents_->AddItemWithStringId(IDS_APP_SELECT_ALL, |
| 1346 IDS_APP_SELECT_ALL); |
| 1347 TextfieldController* controller = textfield_->GetController(); |
| 1348 if (controller) |
| 1349 controller->UpdateContextMenu(context_menu_contents_.get()); |
| 1350 |
| 1351 context_menu_delegate_.reset( |
| 1352 new views::MenuModelAdapter(context_menu_contents_.get())); |
| 1353 context_menu_runner_.reset( |
| 1354 new MenuRunner(new views::MenuItemView(context_menu_delegate_.get()))); |
| 1355 } |
| 1356 |
| 1357 context_menu_delegate_->BuildMenu(context_menu_runner_->GetMenu()); |
| 1358 } |
| 1359 |
| 1360 void NativeTextfieldViews::OnTextInputTypeChanged() { |
| 1361 // TODO(suzhe): changed from DCHECK. See http://crbug.com/81320. |
| 1362 if (textfield_->GetInputMethod()) |
| 1363 textfield_->GetInputMethod()->OnTextInputTypeChanged(textfield_); |
| 1364 } |
| 1365 |
| 1366 void NativeTextfieldViews::OnCaretBoundsChanged() { |
| 1367 // TODO(suzhe): changed from DCHECK. See http://crbug.com/81320. |
| 1368 if (textfield_->GetInputMethod()) |
| 1369 textfield_->GetInputMethod()->OnCaretBoundsChanged(textfield_); |
| 1370 |
| 1371 // Notify selection controller |
| 1372 if (touch_selection_controller_.get()) |
| 1373 touch_selection_controller_->SelectionChanged(); |
| 1374 } |
| 1375 |
| 1376 void NativeTextfieldViews::OnBeforeUserAction() { |
| 1377 TextfieldController* controller = textfield_->GetController(); |
| 1378 if (controller) |
| 1379 controller->OnBeforeUserAction(textfield_); |
| 1380 } |
| 1381 |
| 1382 void NativeTextfieldViews::OnAfterUserAction() { |
| 1383 TextfieldController* controller = textfield_->GetController(); |
| 1384 if (controller) |
| 1385 controller->OnAfterUserAction(textfield_); |
| 1386 } |
| 1387 |
| 1388 bool NativeTextfieldViews::Cut() { |
| 1389 if (!textfield_->read_only() && !textfield_->IsObscured() && model_->Cut()) { |
| 1390 TextfieldController* controller = textfield_->GetController(); |
| 1391 if (controller) |
| 1392 controller->OnAfterCutOrCopy(); |
| 1393 return true; |
| 1394 } |
| 1395 return false; |
| 1396 } |
| 1397 |
| 1398 bool NativeTextfieldViews::Copy() { |
| 1399 if (!textfield_->IsObscured() && model_->Copy()) { |
| 1400 TextfieldController* controller = textfield_->GetController(); |
| 1401 if (controller) |
| 1402 controller->OnAfterCutOrCopy(); |
| 1403 return true; |
| 1404 } |
| 1405 return false; |
| 1406 } |
| 1407 |
| 1408 bool NativeTextfieldViews::Paste() { |
| 1409 if (textfield_->read_only()) |
| 1410 return false; |
| 1411 |
| 1412 const base::string16 original_text = GetText(); |
| 1413 const bool success = model_->Paste(); |
| 1414 |
| 1415 if (success) { |
| 1416 // As Paste is handled in model_->Paste(), the RenderText may contain |
| 1417 // upper case characters. This is not consistent with other places |
| 1418 // which keeps RenderText only containing lower case characters. |
| 1419 base::string16 new_text = GetTextForDisplay(GetText()); |
| 1420 model_->SetText(new_text); |
| 1421 |
| 1422 TextfieldController* controller = textfield_->GetController(); |
| 1423 if (controller) |
| 1424 controller->OnAfterPaste(); |
| 1425 } |
| 1426 return success; |
| 1427 } |
| 1428 |
| 1429 void NativeTextfieldViews::TrackMouseClicks(const ui::MouseEvent& event) { |
| 1430 if (event.IsOnlyLeftMouseButton()) { |
| 1431 base::TimeDelta time_delta = event.time_stamp() - last_click_time_; |
| 1432 if (time_delta.InMilliseconds() <= GetDoubleClickInterval() && |
| 1433 !ExceededDragThresholdFromLastClickLocation(event)) { |
| 1434 // Upon clicking after a triple click, the count should go back to double |
| 1435 // click and alternate between double and triple. This assignment maps |
| 1436 // 0 to 1, 1 to 2, 2 to 1. |
| 1437 aggregated_clicks_ = (aggregated_clicks_ % 2) + 1; |
| 1438 } else { |
| 1439 aggregated_clicks_ = 0; |
| 1440 } |
| 1441 last_click_time_ = event.time_stamp(); |
| 1442 last_click_location_ = event.location(); |
| 1443 } |
| 1444 } |
| 1445 |
| 1446 void NativeTextfieldViews::HandleMousePressEvent(const ui::MouseEvent& event) { |
| 1447 if (event.IsOnlyLeftMouseButton() || event.IsOnlyRightMouseButton()) |
| 1448 textfield_->RequestFocus(); |
| 1449 |
| 1450 if (!event.IsOnlyLeftMouseButton()) |
| 1451 return; |
| 1452 |
| 1453 initiating_drag_ = false; |
| 1454 bool can_drag = true; |
| 1455 |
| 1456 switch (aggregated_clicks_) { |
| 1457 case 0: |
| 1458 if (can_drag && GetRenderText()->IsPointInSelection(event.location())) |
| 1459 initiating_drag_ = true; |
| 1460 else |
| 1461 MoveCursorTo(event.location(), event.IsShiftDown()); |
| 1462 break; |
| 1463 case 1: |
| 1464 MoveCursorTo(event.location(), false); |
| 1465 model_->SelectWord(); |
| 1466 double_click_word_ = GetRenderText()->selection(); |
| 1467 OnCaretBoundsChanged(); |
| 1468 break; |
| 1469 case 2: |
| 1470 model_->SelectAll(false); |
| 1471 OnCaretBoundsChanged(); |
| 1472 break; |
| 1473 default: |
| 1474 NOTREACHED(); |
| 1475 } |
| 1476 SchedulePaint(); |
| 1477 } |
| 1478 |
| 1479 bool NativeTextfieldViews::ImeEditingAllowed() const { |
| 1480 // We don't allow the input method to retrieve or delete content from a |
| 1481 // password field. |
| 1482 ui::TextInputType t = GetTextInputType(); |
| 1483 return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD); |
| 1484 } |
| 1485 |
| 1486 // static |
| 1487 bool NativeTextfieldViews::ShouldInsertChar(base::char16 ch, int flags) { |
| 1488 // Filter out all control characters, including tab and new line characters, |
| 1489 // and all characters with Alt modifier. But we need to allow characters with |
| 1490 // AltGr modifier. |
| 1491 // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different |
| 1492 // flag that we don't care about. |
| 1493 return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && |
| 1494 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN; |
| 1495 } |
| 1496 |
| 1497 void NativeTextfieldViews::CreateTouchSelectionControllerAndNotifyIt() { |
| 1498 if (!touch_selection_controller_) { |
| 1499 touch_selection_controller_.reset( |
| 1500 ui::TouchSelectionController::create(this)); |
| 1501 } |
| 1502 if (touch_selection_controller_) |
| 1503 touch_selection_controller_->SelectionChanged(); |
| 1504 } |
| 1505 |
| 1506 void NativeTextfieldViews::PlatformGestureEventHandling( |
| 1507 const ui::GestureEvent* event) { |
| 1508 #if defined(OS_WIN) && defined(USE_AURA) |
| 1509 if (event->type() == ui::ET_GESTURE_TAP && !textfield_->read_only()) |
| 1510 base::win::DisplayVirtualKeyboard(); |
| 1511 #endif |
| 1512 } |
| 1513 |
| 1514 void NativeTextfieldViews::RevealObscuredChar(int index, |
| 1515 const base::TimeDelta& duration) { |
| 1516 GetRenderText()->SetObscuredRevealIndex(index); |
| 1517 SchedulePaint(); |
| 1518 |
| 1519 if (index != -1) { |
| 1520 obscured_reveal_timer_.Start( |
| 1521 FROM_HERE, |
| 1522 duration, |
| 1523 base::Bind(&NativeTextfieldViews::RevealObscuredChar, |
| 1524 base::Unretained(this), -1, base::TimeDelta())); |
| 1525 } |
| 1526 } |
| 1527 |
| 1528 } // namespace views |
OLD | NEW |