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