1 // Copyright (c) 2012 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.
5 #include "ui/views/controls/textfield/textfield.h"
9 #include "base/debug/trace_event.h"
10 #include "grit/ui_strings.h"
11 #include "ui/accessibility/ax_view_state.h"
12 #include "ui/base/clipboard/scoped_clipboard_writer.h"
13 #include "ui/base/cursor/cursor.h"
14 #include "ui/base/dragdrop/drag_drop_types.h"
15 #include "ui/base/dragdrop/drag_utils.h"
16 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/base/ui_base_switches_util.h"
18 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
19 #include "ui/events/event.h"
20 #include "ui/events/keycodes/keyboard_codes.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/gfx/display.h"
23 #include "ui/gfx/insets.h"
24 #include "ui/gfx/screen.h"
25 #include "ui/native_theme/native_theme.h"
26 #include "ui/views/background.h"
27 #include "ui/views/controls/focusable_border.h"
28 #include "ui/views/controls/label.h"
29 #include "ui/views/controls/menu/menu_runner.h"
30 #include "ui/views/controls/native/native_view_host.h"
31 #include "ui/views/controls/textfield/textfield_controller.h"
32 #include "ui/views/drag_utils.h"
33 #include "ui/views/ime/input_method.h"
34 #include "ui/views/metrics.h"
35 #include "ui/views/native_cursor.h"
36 #include "ui/views/painter.h"
37 #include "ui/views/views_delegate.h"
38 #include "ui/views/widget/widget.h"
41 #include "base/win/win_util.h"
44 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
45 #include "base/strings/utf_string_conversions.h"
46 #include "ui/events/linux/text_edit_command_auralinux.h"
47 #include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h"
54 // Default placeholder text color.
55 const SkColor kDefaultPlaceholderTextColor
= SK_ColorLTGRAY
;
57 const int kNoCommand
= 0;
59 void ConvertRectToScreen(const View
* src
, gfx::Rect
* r
) {
62 gfx::Point new_origin
= r
->origin();
63 View::ConvertPointToScreen(src
, &new_origin
);
64 r
->set_origin(new_origin
);
67 // Get the drag selection timer delay, respecting animation scaling for testing.
68 int GetDragSelectionDelay() {
69 switch (ui::ScopedAnimationDurationScaleMode::duration_scale_mode()) {
70 case ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION
: return 100;
71 case ui::ScopedAnimationDurationScaleMode::FAST_DURATION
: return 25;
72 case ui::ScopedAnimationDurationScaleMode::SLOW_DURATION
: return 400;
73 case ui::ScopedAnimationDurationScaleMode::ZERO_DURATION
: return 0;
78 // Get the default command for a given key |event| and selection state.
79 int GetCommandForKeyEvent(const ui::KeyEvent
& event
, bool has_selection
) {
80 if (event
.type() != ui::ET_KEY_PRESSED
|| event
.IsUnicodeKeyCode())
83 const bool shift
= event
.IsShiftDown();
84 const bool control
= event
.IsControlDown();
85 const bool alt
= event
.IsAltDown() || event
.IsAltGrDown();
86 switch (event
.key_code()) {
88 if (control
&& !shift
&& !alt
)
90 return (control
&& shift
&& !alt
) ? IDS_APP_REDO
: kNoCommand
;
92 return (control
&& !alt
) ? IDS_APP_REDO
: kNoCommand
;
94 return (control
&& !alt
) ? IDS_APP_SELECT_ALL
: kNoCommand
;
96 return (control
&& !alt
) ? IDS_APP_CUT
: kNoCommand
;
98 return (control
&& !alt
) ? IDS_APP_COPY
: kNoCommand
;
100 return (control
&& !alt
) ? IDS_APP_PASTE
: kNoCommand
;
102 // Ignore alt+right, which may be a browser navigation shortcut.
106 return control
? IDS_MOVE_WORD_RIGHT
: IDS_MOVE_RIGHT
;
107 return control
? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
108 IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
;
110 // Ignore alt+left, which may be a browser navigation shortcut.
114 return control
? IDS_MOVE_WORD_LEFT
: IDS_MOVE_LEFT
;
115 return control
? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
116 IDS_MOVE_LEFT_AND_MODIFY_SELECTION
;
118 return shift
? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
:
119 IDS_MOVE_TO_BEGINNING_OF_LINE
;
121 return shift
? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
:
122 IDS_MOVE_TO_END_OF_LINE
;
124 if (!control
|| has_selection
)
125 return IDS_DELETE_BACKWARD
;
126 #if defined(OS_LINUX)
127 // Only erase by line break on Linux and ChromeOS.
129 return IDS_DELETE_TO_BEGINNING_OF_LINE
;
131 return IDS_DELETE_WORD_BACKWARD
;
132 case ui::VKEY_DELETE
:
133 if (!control
|| has_selection
)
134 return (shift
&& has_selection
) ? IDS_APP_CUT
: IDS_DELETE_FORWARD
;
135 #if defined(OS_LINUX)
136 // Only erase by line break on Linux and ChromeOS.
138 return IDS_DELETE_TO_END_OF_LINE
;
140 return IDS_DELETE_WORD_FORWARD
;
141 case ui::VKEY_INSERT
:
142 if (control
&& !shift
)
144 return (shift
&& !control
) ? IDS_APP_PASTE
: kNoCommand
;
150 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
151 // Convert a custom text edit |command| to the equivalent views command ID.
152 int GetViewsCommand(const ui::TextEditCommandAuraLinux
& command
, bool rtl
) {
153 const bool select
= command
.extend_selection();
154 switch (command
.command_id()) {
155 case ui::TextEditCommandAuraLinux::COPY
:
157 case ui::TextEditCommandAuraLinux::CUT
:
159 case ui::TextEditCommandAuraLinux::DELETE_BACKWARD
:
160 return IDS_DELETE_BACKWARD
;
161 case ui::TextEditCommandAuraLinux::DELETE_FORWARD
:
162 return IDS_DELETE_FORWARD
;
163 case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_LINE
:
164 case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_PARAGRAPH
:
165 return IDS_DELETE_TO_BEGINNING_OF_LINE
;
166 case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_LINE
:
167 case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_PARAGRAPH
:
168 return IDS_DELETE_TO_END_OF_LINE
;
169 case ui::TextEditCommandAuraLinux::DELETE_WORD_BACKWARD
:
170 return IDS_DELETE_WORD_BACKWARD
;
171 case ui::TextEditCommandAuraLinux::DELETE_WORD_FORWARD
:
172 return IDS_DELETE_WORD_FORWARD
;
173 case ui::TextEditCommandAuraLinux::INSERT_TEXT
:
175 case ui::TextEditCommandAuraLinux::MOVE_BACKWARD
:
177 return select
? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
: IDS_MOVE_RIGHT
;
178 return select
? IDS_MOVE_LEFT_AND_MODIFY_SELECTION
: IDS_MOVE_LEFT
;
179 case ui::TextEditCommandAuraLinux::MOVE_DOWN
:
180 return IDS_MOVE_DOWN
;
181 case ui::TextEditCommandAuraLinux::MOVE_FORWARD
:
183 return select
? IDS_MOVE_LEFT_AND_MODIFY_SELECTION
: IDS_MOVE_LEFT
;
184 return select
? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
: IDS_MOVE_RIGHT
;
185 case ui::TextEditCommandAuraLinux::MOVE_LEFT
:
186 return select
? IDS_MOVE_LEFT_AND_MODIFY_SELECTION
: IDS_MOVE_LEFT
;
187 case ui::TextEditCommandAuraLinux::MOVE_PAGE_DOWN
:
188 case ui::TextEditCommandAuraLinux::MOVE_PAGE_UP
:
190 case ui::TextEditCommandAuraLinux::MOVE_RIGHT
:
191 return select
? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
: IDS_MOVE_RIGHT
;
192 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_DOCUMENT
:
193 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_LINE
:
194 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_PARAGRAPH
:
195 return select
? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
:
196 IDS_MOVE_TO_BEGINNING_OF_LINE
;
197 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_DOCUMENT
:
198 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_LINE
:
199 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_PARAGRAPH
:
200 return select
? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
:
201 IDS_MOVE_TO_END_OF_LINE
;
202 case ui::TextEditCommandAuraLinux::MOVE_UP
:
204 case ui::TextEditCommandAuraLinux::MOVE_WORD_BACKWARD
:
206 return select
? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
209 return select
? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
211 case ui::TextEditCommandAuraLinux::MOVE_WORD_FORWARD
:
213 return select
? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
216 return select
? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
218 case ui::TextEditCommandAuraLinux::MOVE_WORD_LEFT
:
219 return select
? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
221 case ui::TextEditCommandAuraLinux::MOVE_WORD_RIGHT
:
222 return select
? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
224 case ui::TextEditCommandAuraLinux::PASTE
:
225 return IDS_APP_PASTE
;
226 case ui::TextEditCommandAuraLinux::SELECT_ALL
:
227 return IDS_APP_SELECT_ALL
;
228 case ui::TextEditCommandAuraLinux::SET_MARK
:
229 case ui::TextEditCommandAuraLinux::UNSELECT
:
230 case ui::TextEditCommandAuraLinux::INVALID_COMMAND
:
240 const char Textfield::kViewClassName
[] = "Textfield";
243 size_t Textfield::GetCaretBlinkMs() {
244 static const size_t default_value
= 500;
246 static const size_t system_value
= ::GetCaretBlinkTime();
247 if (system_value
!= 0)
248 return (system_value
== INFINITE
) ? 0 : system_value
;
250 return default_value
;
253 Textfield::Textfield()
254 : model_(new TextfieldModel(this)),
257 default_width_in_chars_(0),
258 use_default_text_color_(true),
259 use_default_background_color_(true),
260 use_default_selection_text_color_(true),
261 use_default_selection_background_color_(true),
262 text_color_(SK_ColorBLACK
),
263 background_color_(SK_ColorWHITE
),
264 selection_text_color_(SK_ColorWHITE
),
265 selection_background_color_(SK_ColorBLUE
),
266 placeholder_text_color_(kDefaultPlaceholderTextColor
),
267 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT
),
268 performing_user_action_(false),
269 skip_input_method_cancel_composition_(false),
270 cursor_visible_(false),
271 drop_cursor_visible_(false),
272 initiating_drag_(false),
273 aggregated_clicks_(0),
274 weak_ptr_factory_(this) {
275 set_context_menu_controller(this);
276 set_drag_controller(this);
277 SetBorder(scoped_ptr
<Border
>(new FocusableBorder()));
280 if (ViewsDelegate::views_delegate
) {
281 password_reveal_duration_
= ViewsDelegate::views_delegate
->
282 GetDefaultTextfieldObscuredRevealDuration();
286 Textfield::~Textfield() {}
288 void Textfield::SetReadOnly(bool read_only
) {
289 // Update read-only without changing the focusable state (or active, etc.).
290 read_only_
= read_only
;
291 if (GetInputMethod())
292 GetInputMethod()->OnTextInputTypeChanged(this);
293 SetColor(GetTextColor());
294 UpdateBackgroundColor();
297 void Textfield::SetTextInputType(ui::TextInputType type
) {
298 GetRenderText()->SetObscured(type
== ui::TEXT_INPUT_TYPE_PASSWORD
);
299 text_input_type_
= type
;
300 OnCaretBoundsChanged();
301 if (GetInputMethod())
302 GetInputMethod()->OnTextInputTypeChanged(this);
306 void Textfield::SetText(const base::string16
& new_text
) {
307 model_
->SetText(new_text
);
308 OnCaretBoundsChanged();
310 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED
, true);
313 void Textfield::AppendText(const base::string16
& new_text
) {
314 if (new_text
.empty())
316 model_
->Append(new_text
);
317 OnCaretBoundsChanged();
321 void Textfield::InsertOrReplaceText(const base::string16
& new_text
) {
322 if (new_text
.empty())
324 model_
->InsertText(new_text
);
325 OnCaretBoundsChanged();
329 base::i18n::TextDirection
Textfield::GetTextDirection() const {
330 return GetRenderText()->GetTextDirection();
333 base::string16
Textfield::GetSelectedText() const {
334 return model_
->GetSelectedText();
337 void Textfield::SelectAll(bool reversed
) {
338 model_
->SelectAll(reversed
);
339 UpdateSelectionClipboard();
340 UpdateAfterChange(false, true);
343 void Textfield::ClearSelection() {
344 model_
->ClearSelection();
345 UpdateAfterChange(false, true);
348 bool Textfield::HasSelection() const {
349 return !GetSelectedRange().is_empty();
352 SkColor
Textfield::GetTextColor() const {
353 if (!use_default_text_color_
)
356 return GetNativeTheme()->GetSystemColor(read_only() ?
357 ui::NativeTheme::kColorId_TextfieldReadOnlyColor
:
358 ui::NativeTheme::kColorId_TextfieldDefaultColor
);
361 void Textfield::SetTextColor(SkColor color
) {
363 use_default_text_color_
= false;
367 void Textfield::UseDefaultTextColor() {
368 use_default_text_color_
= true;
369 SetColor(GetTextColor());
372 SkColor
Textfield::GetBackgroundColor() const {
373 if (!use_default_background_color_
)
374 return background_color_
;
376 return GetNativeTheme()->GetSystemColor(read_only() ?
377 ui::NativeTheme::kColorId_TextfieldReadOnlyBackground
:
378 ui::NativeTheme::kColorId_TextfieldDefaultBackground
);
381 void Textfield::SetBackgroundColor(SkColor color
) {
382 background_color_
= color
;
383 use_default_background_color_
= false;
384 UpdateBackgroundColor();
387 void Textfield::UseDefaultBackgroundColor() {
388 use_default_background_color_
= true;
389 UpdateBackgroundColor();
392 SkColor
Textfield::GetSelectionTextColor() const {
393 return use_default_selection_text_color_
?
394 GetNativeTheme()->GetSystemColor(
395 ui::NativeTheme::kColorId_TextfieldSelectionColor
) :
396 selection_text_color_
;
399 void Textfield::SetSelectionTextColor(SkColor color
) {
400 selection_text_color_
= color
;
401 use_default_selection_text_color_
= false;
402 GetRenderText()->set_selection_color(GetSelectionTextColor());
406 void Textfield::UseDefaultSelectionTextColor() {
407 use_default_selection_text_color_
= true;
408 GetRenderText()->set_selection_color(GetSelectionTextColor());
412 void Textfield::SetShadows(const gfx::ShadowValues
& shadows
) {
413 GetRenderText()->set_shadows(shadows
);
417 SkColor
Textfield::GetSelectionBackgroundColor() const {
418 return use_default_selection_background_color_
?
419 GetNativeTheme()->GetSystemColor(
420 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused
) :
421 selection_background_color_
;
424 void Textfield::SetSelectionBackgroundColor(SkColor color
) {
425 selection_background_color_
= color
;
426 use_default_selection_background_color_
= false;
427 GetRenderText()->set_selection_background_focused_color(
428 GetSelectionBackgroundColor());
432 void Textfield::UseDefaultSelectionBackgroundColor() {
433 use_default_selection_background_color_
= true;
434 GetRenderText()->set_selection_background_focused_color(
435 GetSelectionBackgroundColor());
439 bool Textfield::GetCursorEnabled() const {
440 return GetRenderText()->cursor_enabled();
443 void Textfield::SetCursorEnabled(bool enabled
) {
444 GetRenderText()->SetCursorEnabled(enabled
);
447 const gfx::FontList
& Textfield::GetFontList() const {
448 return GetRenderText()->font_list();
451 void Textfield::SetFontList(const gfx::FontList
& font_list
) {
452 GetRenderText()->SetFontList(font_list
);
453 OnCaretBoundsChanged();
454 PreferredSizeChanged();
457 base::string16
Textfield::GetPlaceholderText() const {
458 return placeholder_text_
;
461 gfx::HorizontalAlignment
Textfield::GetHorizontalAlignment() const {
462 return GetRenderText()->horizontal_alignment();
465 void Textfield::SetHorizontalAlignment(gfx::HorizontalAlignment alignment
) {
466 GetRenderText()->SetHorizontalAlignment(alignment
);
469 void Textfield::ShowImeIfNeeded() {
470 if (enabled() && !read_only())
471 GetInputMethod()->ShowImeIfNeeded();
474 bool Textfield::IsIMEComposing() const {
475 return model_
->HasCompositionText();
478 const gfx::Range
& Textfield::GetSelectedRange() const {
479 return GetRenderText()->selection();
482 void Textfield::SelectRange(const gfx::Range
& range
) {
483 model_
->SelectRange(range
);
484 UpdateAfterChange(false, true);
487 const gfx::SelectionModel
& Textfield::GetSelectionModel() const {
488 return GetRenderText()->selection_model();
491 void Textfield::SelectSelectionModel(const gfx::SelectionModel
& sel
) {
492 model_
->SelectSelectionModel(sel
);
493 UpdateAfterChange(false, true);
496 size_t Textfield::GetCursorPosition() const {
497 return model_
->GetCursorPosition();
500 void Textfield::SetColor(SkColor value
) {
501 GetRenderText()->SetColor(value
);
505 void Textfield::ApplyColor(SkColor value
, const gfx::Range
& range
) {
506 GetRenderText()->ApplyColor(value
, range
);
510 void Textfield::SetStyle(gfx::TextStyle style
, bool value
) {
511 GetRenderText()->SetStyle(style
, value
);
515 void Textfield::ApplyStyle(gfx::TextStyle style
,
517 const gfx::Range
& range
) {
518 GetRenderText()->ApplyStyle(style
, value
, range
);
522 void Textfield::ClearEditHistory() {
523 model_
->ClearEditHistory();
526 void Textfield::SetAccessibleName(const base::string16
& name
) {
527 accessible_name_
= name
;
530 void Textfield::ExecuteCommand(int command_id
) {
531 ExecuteCommand(command_id
, ui::EF_NONE
);
534 void Textfield::SetFocusPainter(scoped_ptr
<Painter
> focus_painter
) {
535 focus_painter_
= focus_painter
.Pass();
538 bool Textfield::HasTextBeingDragged() {
539 return initiating_drag_
;
542 ////////////////////////////////////////////////////////////////////////////////
543 // Textfield, View overrides:
545 int Textfield::GetBaseline() const {
546 return GetInsets().top() + GetRenderText()->GetBaseline();
549 gfx::Size
Textfield::GetPreferredSize() const {
550 const gfx::Insets
& insets
= GetInsets();
551 return gfx::Size(GetFontList().GetExpectedTextWidth(default_width_in_chars_
) +
552 insets
.width(), GetFontList().GetHeight() + insets
.height());
555 const char* Textfield::GetClassName() const {
556 return kViewClassName
;
559 gfx::NativeCursor
Textfield::GetCursor(const ui::MouseEvent
& event
) {
560 bool in_selection
= GetRenderText()->IsPointInSelection(event
.location());
561 bool drag_event
= event
.type() == ui::ET_MOUSE_DRAGGED
;
562 bool text_cursor
= !initiating_drag_
&& (drag_event
|| !in_selection
);
563 return text_cursor
? GetNativeIBeamCursor() : gfx::kNullCursor
;
566 bool Textfield::OnMousePressed(const ui::MouseEvent
& event
) {
567 TrackMouseClicks(event
);
569 if (!controller_
|| !controller_
->HandleMouseEvent(this, event
)) {
570 if (event
.IsOnlyLeftMouseButton() || event
.IsOnlyRightMouseButton()) {
575 if (event
.IsOnlyLeftMouseButton()) {
576 OnBeforeUserAction();
577 initiating_drag_
= false;
578 switch (aggregated_clicks_
) {
580 if (GetRenderText()->IsPointInSelection(event
.location()))
581 initiating_drag_
= true;
583 MoveCursorTo(event
.location(), event
.IsShiftDown());
586 model_
->MoveCursorTo(event
.location(), false);
587 model_
->SelectWord();
588 UpdateAfterChange(false, true);
589 double_click_word_
= GetRenderText()->selection();
600 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
601 if (event
.IsOnlyMiddleMouseButton()) {
602 if (GetRenderText()->IsPointInSelection(event
.location())) {
603 OnBeforeUserAction();
605 ui::ScopedClipboardWriter(
606 ui::Clipboard::GetForCurrentThread(),
607 ui::CLIPBOARD_TYPE_SELECTION
).WriteText(base::string16());
609 } else if (!read_only()) {
610 PasteSelectionClipboard(event
);
619 bool Textfield::OnMouseDragged(const ui::MouseEvent
& event
) {
620 last_drag_location_
= event
.location();
622 // Don't adjust the cursor on a potential drag and drop, or if the mouse
623 // movement from the last mouse click does not exceed the drag threshold.
624 if (initiating_drag_
|| !event
.IsOnlyLeftMouseButton() ||
625 !ExceededDragThreshold(last_drag_location_
- last_click_location_
)) {
629 // A timer is used to continuously scroll while selecting beyond side edges.
630 if ((event
.location().x() > 0 && event
.location().x() < size().width()) ||
631 GetDragSelectionDelay() == 0) {
632 drag_selection_timer_
.Stop();
633 SelectThroughLastDragLocation();
634 } else if (!drag_selection_timer_
.IsRunning()) {
635 drag_selection_timer_
.Start(
636 FROM_HERE
, base::TimeDelta::FromMilliseconds(GetDragSelectionDelay()),
637 this, &Textfield::SelectThroughLastDragLocation
);
643 void Textfield::OnMouseReleased(const ui::MouseEvent
& event
) {
644 OnBeforeUserAction();
645 drag_selection_timer_
.Stop();
646 // Cancel suspected drag initiations, the user was clicking in the selection.
647 if (initiating_drag_
)
648 MoveCursorTo(event
.location(), false);
649 initiating_drag_
= false;
650 UpdateSelectionClipboard();
654 bool Textfield::OnKeyPressed(const ui::KeyEvent
& event
) {
655 // Since HandleKeyEvent() might destroy |this|, get a weak pointer and verify
656 // it isn't null before proceeding.
657 base::WeakPtr
<Textfield
> textfield(weak_ptr_factory_
.GetWeakPtr());
659 bool handled
= controller_
&& controller_
->HandleKeyEvent(this, event
);
664 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
665 ui::TextEditKeyBindingsDelegateAuraLinux
* delegate
=
666 ui::GetTextEditKeyBindingsDelegate();
667 std::vector
<ui::TextEditCommandAuraLinux
> commands
;
668 if (!handled
&& delegate
&& delegate
->MatchEvent(event
, &commands
)) {
669 const bool rtl
= GetTextDirection() == base::i18n::RIGHT_TO_LEFT
;
670 for (size_t i
= 0; i
< commands
.size(); ++i
) {
671 const int command
= GetViewsCommand(commands
[i
], rtl
);
672 if (IsCommandIdEnabled(command
)) {
673 ExecuteCommand(command
);
681 const int command
= GetCommandForKeyEvent(event
, HasSelection());
682 if (!handled
&& IsCommandIdEnabled(command
)) {
683 ExecuteCommand(command
);
689 ui::TextInputClient
* Textfield::GetTextInputClient() {
690 return read_only_
? NULL
: this;
693 void Textfield::OnGestureEvent(ui::GestureEvent
* event
) {
694 switch (event
->type()) {
695 case ui::ET_GESTURE_TAP_DOWN
:
696 OnBeforeUserAction();
700 // We don't deselect if the point is in the selection
701 // because TAP_DOWN may turn into a LONG_PRESS.
702 if (!GetRenderText()->IsPointInSelection(event
->location()))
703 MoveCursorTo(event
->location(), false);
707 case ui::ET_GESTURE_SCROLL_UPDATE
:
708 OnBeforeUserAction();
709 MoveCursorTo(event
->location(), true);
713 case ui::ET_GESTURE_SCROLL_END
:
714 case ui::ET_SCROLL_FLING_START
:
715 CreateTouchSelectionControllerAndNotifyIt();
718 case ui::ET_GESTURE_TAP
:
719 if (event
->details().tap_count() == 1) {
720 CreateTouchSelectionControllerAndNotifyIt();
722 DestroyTouchSelection();
723 OnBeforeUserAction();
730 base::win::DisplayVirtualKeyboard();
733 case ui::ET_GESTURE_LONG_PRESS
:
734 // If long press happens outside selection, select word and show context
735 // menu (If touch selection is enabled, context menu is shown by the
736 // |touch_selection_controller_|, hence we mark the event handled.
737 // Otherwise, the regular context menu will be shown by views).
738 // If long press happens in selected text and touch drag drop is enabled,
739 // we will turn off touch selection (if one exists) and let views do drag
741 if (!GetRenderText()->IsPointInSelection(event
->location())) {
742 OnBeforeUserAction();
743 model_
->SelectWord();
744 touch_selection_controller_
.reset(
745 ui::TouchSelectionController::create(this));
746 UpdateAfterChange(false, true);
748 if (touch_selection_controller_
)
750 } else if (switches::IsTouchDragDropEnabled()) {
751 initiating_drag_
= true;
752 DestroyTouchSelection();
754 if (!touch_selection_controller_
)
755 CreateTouchSelectionControllerAndNotifyIt();
756 if (touch_selection_controller_
)
760 case ui::ET_GESTURE_LONG_TAP
:
761 if (!touch_selection_controller_
)
762 CreateTouchSelectionControllerAndNotifyIt();
764 // If touch selection is enabled, the context menu on long tap will be
765 // shown by the |touch_selection_controller_|, hence we mark the event
766 // handled so views does not try to show context menu on it.
767 if (touch_selection_controller_
)
775 void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse
) {
779 bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent
& event
) {
780 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
781 // Skip any accelerator handling that conflicts with custom keybindings.
782 ui::TextEditKeyBindingsDelegateAuraLinux
* delegate
=
783 ui::GetTextEditKeyBindingsDelegate();
784 std::vector
<ui::TextEditCommandAuraLinux
> commands
;
785 if (delegate
&& delegate
->MatchEvent(event
, &commands
)) {
786 const bool rtl
= GetTextDirection() == base::i18n::RIGHT_TO_LEFT
;
787 for (size_t i
= 0; i
< commands
.size(); ++i
)
788 if (IsCommandIdEnabled(GetViewsCommand(commands
[i
], rtl
)))
793 // Skip backspace accelerator handling; editable textfields handle this key.
794 // Also skip processing Windows [Alt]+<num-pad digit> Unicode alt-codes.
795 const bool is_backspace
= event
.key_code() == ui::VKEY_BACK
;
796 return (is_backspace
&& !read_only()) || event
.IsUnicodeKeyCode();
799 bool Textfield::GetDropFormats(
801 std::set
<OSExchangeData::CustomFormat
>* custom_formats
) {
802 if (!enabled() || read_only())
804 // TODO(msw): Can we support URL, FILENAME, etc.?
805 *formats
= ui::OSExchangeData::STRING
;
807 controller_
->AppendDropFormats(formats
, custom_formats
);
811 bool Textfield::CanDrop(const OSExchangeData
& data
) {
813 std::set
<OSExchangeData::CustomFormat
> custom_formats
;
814 GetDropFormats(&formats
, &custom_formats
);
815 return enabled() && !read_only() &&
816 data
.HasAnyFormat(formats
, custom_formats
);
819 int Textfield::OnDragUpdated(const ui::DropTargetEvent
& event
) {
820 DCHECK(CanDrop(event
.data()));
821 gfx::RenderText
* render_text
= GetRenderText();
822 const gfx::Range
& selection
= render_text
->selection();
823 drop_cursor_position_
= render_text
->FindCursorPosition(event
.location());
824 bool in_selection
= !selection
.is_empty() &&
825 selection
.Contains(gfx::Range(drop_cursor_position_
.caret_pos()));
826 drop_cursor_visible_
= !in_selection
;
827 // TODO(msw): Pan over text when the user drags to the visible text edge.
828 OnCaretBoundsChanged();
831 if (initiating_drag_
) {
833 return ui::DragDropTypes::DRAG_NONE
;
834 return event
.IsControlDown() ? ui::DragDropTypes::DRAG_COPY
:
835 ui::DragDropTypes::DRAG_MOVE
;
837 return ui::DragDropTypes::DRAG_COPY
| ui::DragDropTypes::DRAG_MOVE
;
840 void Textfield::OnDragExited() {
841 drop_cursor_visible_
= false;
845 int Textfield::OnPerformDrop(const ui::DropTargetEvent
& event
) {
846 DCHECK(CanDrop(event
.data()));
847 drop_cursor_visible_
= false;
850 int drag_operation
= controller_
->OnDrop(event
.data());
851 if (drag_operation
!= ui::DragDropTypes::DRAG_NONE
)
852 return drag_operation
;
855 gfx::RenderText
* render_text
= GetRenderText();
856 DCHECK(!initiating_drag_
||
857 !render_text
->IsPointInSelection(event
.location()));
858 OnBeforeUserAction();
859 skip_input_method_cancel_composition_
= true;
861 gfx::SelectionModel drop_destination_model
=
862 render_text
->FindCursorPosition(event
.location());
863 base::string16 new_text
;
864 event
.data().GetString(&new_text
);
866 // Delete the current selection for a drag and drop within this view.
867 const bool move
= initiating_drag_
&& !event
.IsControlDown() &&
868 event
.source_operations() & ui::DragDropTypes::DRAG_MOVE
;
870 // Adjust the drop destination if it is on or after the current selection.
871 size_t pos
= drop_destination_model
.caret_pos();
872 pos
-= render_text
->selection().Intersect(gfx::Range(0, pos
)).length();
873 model_
->DeleteSelectionAndInsertTextAt(new_text
, pos
);
875 model_
->MoveCursorTo(drop_destination_model
);
876 // Drop always inserts text even if the textfield is not in insert mode.
877 model_
->InsertText(new_text
);
879 skip_input_method_cancel_composition_
= false;
880 UpdateAfterChange(true, true);
882 return move
? ui::DragDropTypes::DRAG_MOVE
: ui::DragDropTypes::DRAG_COPY
;
885 void Textfield::OnDragDone() {
886 initiating_drag_
= false;
887 drop_cursor_visible_
= false;
890 void Textfield::GetAccessibleState(ui::AXViewState
* state
) {
891 state
->role
= ui::AX_ROLE_TEXT_FIELD
;
892 state
->name
= accessible_name_
;
894 state
->AddStateFlag(ui::AX_STATE_READ_ONLY
);
895 if (text_input_type_
== ui::TEXT_INPUT_TYPE_PASSWORD
)
896 state
->AddStateFlag(ui::AX_STATE_PROTECTED
);
897 state
->value
= text();
899 const gfx::Range range
= GetSelectedRange();
900 state
->selection_start
= range
.start();
901 state
->selection_end
= range
.end();
904 state
->set_value_callback
=
905 base::Bind(&Textfield::AccessibilitySetValue
,
906 weak_ptr_factory_
.GetWeakPtr());
910 void Textfield::OnBoundsChanged(const gfx::Rect
& previous_bounds
) {
911 GetRenderText()->SetDisplayRect(GetContentsBounds());
912 OnCaretBoundsChanged();
915 void Textfield::OnEnabledChanged() {
916 View::OnEnabledChanged();
917 if (GetInputMethod())
918 GetInputMethod()->OnTextInputTypeChanged(this);
922 void Textfield::OnPaint(gfx::Canvas
* canvas
) {
923 OnPaintBackground(canvas
);
924 PaintTextAndCursor(canvas
);
925 OnPaintBorder(canvas
);
928 void Textfield::OnFocus() {
929 GetRenderText()->set_focused(true);
930 cursor_visible_
= true;
932 GetInputMethod()->OnFocus();
933 OnCaretBoundsChanged();
935 const size_t caret_blink_ms
= Textfield::GetCaretBlinkMs();
936 if (caret_blink_ms
!= 0) {
937 cursor_repaint_timer_
.Start(FROM_HERE
,
938 base::TimeDelta::FromMilliseconds(caret_blink_ms
), this,
939 &Textfield::UpdateCursor
);
946 void Textfield::OnBlur() {
947 GetRenderText()->set_focused(false);
948 GetInputMethod()->OnBlur();
949 cursor_repaint_timer_
.Stop();
950 if (cursor_visible_
) {
951 cursor_visible_
= false;
955 DestroyTouchSelection();
957 // Border typically draws focus indicator.
961 gfx::Point
Textfield::GetKeyboardContextMenuLocation() {
962 return GetCaretBounds().bottom_right();
965 void Textfield::OnNativeThemeChanged(const ui::NativeTheme
* theme
) {
966 gfx::RenderText
* render_text
= GetRenderText();
967 render_text
->SetColor(GetTextColor());
968 UpdateBackgroundColor();
969 render_text
->set_cursor_color(GetTextColor());
970 render_text
->set_selection_color(GetSelectionTextColor());
971 render_text
->set_selection_background_focused_color(
972 GetSelectionBackgroundColor());
975 ////////////////////////////////////////////////////////////////////////////////
976 // Textfield, TextfieldModel::Delegate overrides:
978 void Textfield::OnCompositionTextConfirmedOrCleared() {
979 if (!skip_input_method_cancel_composition_
)
980 GetInputMethod()->CancelComposition(this);
983 ////////////////////////////////////////////////////////////////////////////////
984 // Textfield, ContextMenuController overrides:
986 void Textfield::ShowContextMenuForView(View
* source
,
987 const gfx::Point
& point
,
988 ui::MenuSourceType source_type
) {
990 ignore_result(context_menu_runner_
->RunMenuAt(GetWidget(),
992 gfx::Rect(point
, gfx::Size()),
997 ////////////////////////////////////////////////////////////////////////////////
998 // Textfield, DragController overrides:
1000 void Textfield::WriteDragDataForView(View
* sender
,
1001 const gfx::Point
& press_pt
,
1002 OSExchangeData
* data
) {
1003 const base::string16
& selected_text(GetSelectedText());
1004 data
->SetString(selected_text
);
1005 Label
label(selected_text
, GetFontList());
1006 label
.SetBackgroundColor(GetBackgroundColor());
1007 label
.set_subpixel_rendering_enabled(false);
1008 gfx::Size
size(label
.GetPreferredSize());
1009 gfx::NativeView native_view
= GetWidget()->GetNativeView();
1010 gfx::Display display
= gfx::Screen::GetScreenFor(native_view
)->
1011 GetDisplayNearestWindow(native_view
);
1012 size
.SetToMin(gfx::Size(display
.size().width(), height()));
1013 label
.SetBoundsRect(gfx::Rect(size
));
1014 scoped_ptr
<gfx::Canvas
> canvas(
1015 GetCanvasForDragImage(GetWidget(), label
.size()));
1016 label
.SetEnabledColor(GetTextColor());
1017 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1018 // Desktop Linux Aura does not yet support transparency in drag images.
1019 canvas
->DrawColor(GetBackgroundColor());
1021 label
.Paint(canvas
.get(), views::CullSet());
1022 const gfx::Vector2d
kOffset(-15, 0);
1023 drag_utils::SetDragImageOnDataObject(*canvas
, kOffset
, data
);
1025 controller_
->OnWriteDragData(data
);
1028 int Textfield::GetDragOperationsForView(View
* sender
, const gfx::Point
& p
) {
1029 int drag_operations
= ui::DragDropTypes::DRAG_COPY
;
1030 if (!enabled() || text_input_type_
== ui::TEXT_INPUT_TYPE_PASSWORD
||
1031 !GetRenderText()->IsPointInSelection(p
)) {
1032 drag_operations
= ui::DragDropTypes::DRAG_NONE
;
1033 } else if (sender
== this && !read_only()) {
1035 ui::DragDropTypes::DRAG_MOVE
| ui::DragDropTypes::DRAG_COPY
;
1038 controller_
->OnGetDragOperationsForTextfield(&drag_operations
);
1039 return drag_operations
;
1042 bool Textfield::CanStartDragForView(View
* sender
,
1043 const gfx::Point
& press_pt
,
1044 const gfx::Point
& p
) {
1045 return initiating_drag_
&& GetRenderText()->IsPointInSelection(press_pt
);
1048 ////////////////////////////////////////////////////////////////////////////////
1049 // Textfield, ui::TouchEditable overrides:
1051 void Textfield::SelectRect(const gfx::Point
& start
, const gfx::Point
& end
) {
1052 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE
)
1055 gfx::SelectionModel start_caret
= GetRenderText()->FindCursorPosition(start
);
1056 gfx::SelectionModel end_caret
= GetRenderText()->FindCursorPosition(end
);
1057 gfx::SelectionModel
selection(
1058 gfx::Range(start_caret
.caret_pos(), end_caret
.caret_pos()),
1059 end_caret
.caret_affinity());
1061 OnBeforeUserAction();
1062 SelectSelectionModel(selection
);
1063 OnAfterUserAction();
1066 void Textfield::MoveCaretTo(const gfx::Point
& point
) {
1067 SelectRect(point
, point
);
1070 void Textfield::GetSelectionEndPoints(gfx::Rect
* p1
, gfx::Rect
* p2
) {
1071 gfx::RenderText
* render_text
= GetRenderText();
1072 const gfx::SelectionModel
& sel
= render_text
->selection_model();
1073 gfx::SelectionModel start_sel
=
1074 render_text
->GetSelectionModelForSelectionStart();
1075 *p1
= render_text
->GetCursorBounds(start_sel
, true);
1076 *p2
= render_text
->GetCursorBounds(sel
, true);
1079 gfx::Rect
Textfield::GetBounds() {
1080 return GetLocalBounds();
1083 gfx::NativeView
Textfield::GetNativeView() const {
1084 return GetWidget()->GetNativeView();
1087 void Textfield::ConvertPointToScreen(gfx::Point
* point
) {
1088 View::ConvertPointToScreen(this, point
);
1091 void Textfield::ConvertPointFromScreen(gfx::Point
* point
) {
1092 View::ConvertPointFromScreen(this, point
);
1095 bool Textfield::DrawsHandles() {
1099 void Textfield::OpenContextMenu(const gfx::Point
& anchor
) {
1100 DestroyTouchSelection();
1101 ShowContextMenu(anchor
, ui::MENU_SOURCE_TOUCH_EDIT_MENU
);
1104 void Textfield::DestroyTouchSelection() {
1105 touch_selection_controller_
.reset();
1108 ////////////////////////////////////////////////////////////////////////////////
1109 // Textfield, ui::SimpleMenuModel::Delegate overrides:
1111 bool Textfield::IsCommandIdChecked(int command_id
) const {
1115 bool Textfield::IsCommandIdEnabled(int command_id
) const {
1116 base::string16 result
;
1117 bool editable
= !read_only();
1118 bool readable
= text_input_type_
!= ui::TEXT_INPUT_TYPE_PASSWORD
;
1119 switch (command_id
) {
1121 return editable
&& model_
->CanUndo();
1123 return editable
&& model_
->CanRedo();
1125 return editable
&& readable
&& model_
->HasSelection();
1127 return readable
&& model_
->HasSelection();
1129 ui::Clipboard::GetForCurrentThread()->ReadText(
1130 ui::CLIPBOARD_TYPE_COPY_PASTE
, &result
);
1131 return editable
&& !result
.empty();
1132 case IDS_APP_DELETE
:
1133 return editable
&& model_
->HasSelection();
1134 case IDS_APP_SELECT_ALL
:
1135 return !text().empty();
1136 case IDS_DELETE_FORWARD
:
1137 case IDS_DELETE_BACKWARD
:
1138 case IDS_DELETE_TO_BEGINNING_OF_LINE
:
1139 case IDS_DELETE_TO_END_OF_LINE
:
1140 case IDS_DELETE_WORD_BACKWARD
:
1141 case IDS_DELETE_WORD_FORWARD
:
1144 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION
:
1145 case IDS_MOVE_RIGHT
:
1146 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
:
1147 case IDS_MOVE_WORD_LEFT
:
1148 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
1149 case IDS_MOVE_WORD_RIGHT
:
1150 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
1151 case IDS_MOVE_TO_BEGINNING_OF_LINE
:
1152 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
:
1153 case IDS_MOVE_TO_END_OF_LINE
:
1154 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
:
1161 bool Textfield::GetAcceleratorForCommandId(int command_id
,
1162 ui::Accelerator
* accelerator
) {
1166 void Textfield::ExecuteCommand(int command_id
, int event_flags
) {
1167 DestroyTouchSelection();
1168 if (!IsCommandIdEnabled(command_id
))
1171 bool text_changed
= false;
1172 bool cursor_changed
= false;
1173 bool rtl
= GetTextDirection() == base::i18n::RIGHT_TO_LEFT
;
1174 gfx::VisualCursorDirection begin
= rtl
? gfx::CURSOR_RIGHT
: gfx::CURSOR_LEFT
;
1175 gfx::VisualCursorDirection end
= rtl
? gfx::CURSOR_LEFT
: gfx::CURSOR_RIGHT
;
1176 gfx::SelectionModel selection_model
= GetSelectionModel();
1178 OnBeforeUserAction();
1179 switch (command_id
) {
1181 text_changed
= cursor_changed
= model_
->Undo();
1184 text_changed
= cursor_changed
= model_
->Redo();
1187 text_changed
= cursor_changed
= Cut();
1193 text_changed
= cursor_changed
= Paste();
1195 case IDS_APP_DELETE
:
1196 text_changed
= cursor_changed
= model_
->Delete();
1198 case IDS_APP_SELECT_ALL
:
1201 case IDS_DELETE_BACKWARD
:
1202 text_changed
= cursor_changed
= model_
->Backspace();
1204 case IDS_DELETE_FORWARD
:
1205 text_changed
= cursor_changed
= model_
->Delete();
1207 case IDS_DELETE_TO_END_OF_LINE
:
1208 model_
->MoveCursor(gfx::LINE_BREAK
, end
, true);
1209 text_changed
= cursor_changed
= model_
->Delete();
1211 case IDS_DELETE_TO_BEGINNING_OF_LINE
:
1212 model_
->MoveCursor(gfx::LINE_BREAK
, begin
, true);
1213 text_changed
= cursor_changed
= model_
->Backspace();
1215 case IDS_DELETE_WORD_BACKWARD
:
1216 model_
->MoveCursor(gfx::WORD_BREAK
, begin
, true);
1217 text_changed
= cursor_changed
= model_
->Backspace();
1219 case IDS_DELETE_WORD_FORWARD
:
1220 model_
->MoveCursor(gfx::WORD_BREAK
, end
, true);
1221 text_changed
= cursor_changed
= model_
->Delete();
1224 model_
->MoveCursor(gfx::CHARACTER_BREAK
, gfx::CURSOR_LEFT
, false);
1226 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION
:
1227 model_
->MoveCursor(gfx::CHARACTER_BREAK
, gfx::CURSOR_LEFT
, true);
1229 case IDS_MOVE_RIGHT
:
1230 model_
->MoveCursor(gfx::CHARACTER_BREAK
, gfx::CURSOR_RIGHT
, false);
1232 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
:
1233 model_
->MoveCursor(gfx::CHARACTER_BREAK
, gfx::CURSOR_RIGHT
, true);
1235 case IDS_MOVE_WORD_LEFT
:
1236 model_
->MoveCursor(gfx::WORD_BREAK
, gfx::CURSOR_LEFT
, false);
1238 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
1239 model_
->MoveCursor(gfx::WORD_BREAK
, gfx::CURSOR_LEFT
, true);
1241 case IDS_MOVE_WORD_RIGHT
:
1242 model_
->MoveCursor(gfx::WORD_BREAK
, gfx::CURSOR_RIGHT
, false);
1244 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
1245 model_
->MoveCursor(gfx::WORD_BREAK
, gfx::CURSOR_RIGHT
, true);
1247 case IDS_MOVE_TO_BEGINNING_OF_LINE
:
1248 model_
->MoveCursor(gfx::LINE_BREAK
, begin
, false);
1250 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
:
1251 model_
->MoveCursor(gfx::LINE_BREAK
, begin
, true);
1253 case IDS_MOVE_TO_END_OF_LINE
:
1254 model_
->MoveCursor(gfx::LINE_BREAK
, end
, false);
1256 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
:
1257 model_
->MoveCursor(gfx::LINE_BREAK
, end
, true);
1264 cursor_changed
|= GetSelectionModel() != selection_model
;
1266 UpdateSelectionClipboard();
1267 UpdateAfterChange(text_changed
, cursor_changed
);
1268 OnAfterUserAction();
1271 ////////////////////////////////////////////////////////////////////////////////
1272 // Textfield, ui::TextInputClient overrides:
1274 void Textfield::SetCompositionText(const ui::CompositionText
& composition
) {
1275 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE
)
1278 OnBeforeUserAction();
1279 skip_input_method_cancel_composition_
= true;
1280 model_
->SetCompositionText(composition
);
1281 skip_input_method_cancel_composition_
= false;
1282 UpdateAfterChange(true, true);
1283 OnAfterUserAction();
1286 void Textfield::ConfirmCompositionText() {
1287 if (!model_
->HasCompositionText())
1290 OnBeforeUserAction();
1291 skip_input_method_cancel_composition_
= true;
1292 model_
->ConfirmCompositionText();
1293 skip_input_method_cancel_composition_
= false;
1294 UpdateAfterChange(true, true);
1295 OnAfterUserAction();
1298 void Textfield::ClearCompositionText() {
1299 if (!model_
->HasCompositionText())
1302 OnBeforeUserAction();
1303 skip_input_method_cancel_composition_
= true;
1304 model_
->CancelCompositionText();
1305 skip_input_method_cancel_composition_
= false;
1306 UpdateAfterChange(true, true);
1307 OnAfterUserAction();
1310 void Textfield::InsertText(const base::string16
& new_text
) {
1311 // TODO(suzhe): Filter invalid characters.
1312 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE
|| new_text
.empty())
1315 OnBeforeUserAction();
1316 skip_input_method_cancel_composition_
= true;
1317 if (GetRenderText()->insert_mode())
1318 model_
->InsertText(new_text
);
1320 model_
->ReplaceText(new_text
);
1321 skip_input_method_cancel_composition_
= false;
1322 UpdateAfterChange(true, true);
1323 OnAfterUserAction();
1326 void Textfield::InsertChar(base::char16 ch
, int flags
) {
1327 const int kControlModifierMask
= ui::EF_CONTROL_DOWN
| ui::EF_ALT_DOWN
|
1328 ui::EF_COMMAND_DOWN
| ui::EF_ALTGR_DOWN
|
1331 // Filter out all control characters, including tab and new line characters,
1332 // and all characters with Alt modifier. But allow characters with the AltGr
1333 // modifier. On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a
1334 // different flag that we don't care about.
1335 const bool should_insert_char
=
1336 ((ch
>= 0x20 && ch
< 0x7F) || ch
> 0x9F) &&
1337 (flags
& kControlModifierMask
) != ui::EF_ALT_DOWN
;
1338 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE
|| !should_insert_char
)
1341 OnBeforeUserAction();
1342 skip_input_method_cancel_composition_
= true;
1343 if (GetRenderText()->insert_mode())
1344 model_
->InsertChar(ch
);
1346 model_
->ReplaceChar(ch
);
1347 skip_input_method_cancel_composition_
= false;
1349 UpdateAfterChange(true, true);
1350 OnAfterUserAction();
1352 if (text_input_type_
== ui::TEXT_INPUT_TYPE_PASSWORD
&&
1353 password_reveal_duration_
!= base::TimeDelta()) {
1354 const size_t change_offset
= model_
->GetCursorPosition();
1355 DCHECK_GT(change_offset
, 0u);
1356 RevealPasswordChar(change_offset
- 1);
1360 gfx::NativeWindow
Textfield::GetAttachedWindow() const {
1361 // Imagine the following hierarchy.
1362 // [NativeWidget A] - FocusManager
1367 // An important thing is that [NativeWidget A] owns Win32 input focus even
1368 // when [View X] is logically focused by FocusManager. As a result, an Win32
1369 // IME may want to interact with the native view of [NativeWidget A] rather
1370 // than that of [NativeWidget B]. This is why we need to call
1371 // GetTopLevelWidget() here.
1372 return GetWidget()->GetTopLevelWidget()->GetNativeWindow();
1375 ui::TextInputType
Textfield::GetTextInputType() const {
1376 if (read_only() || !enabled())
1377 return ui::TEXT_INPUT_TYPE_NONE
;
1378 return text_input_type_
;
1381 ui::TextInputMode
Textfield::GetTextInputMode() const {
1382 return ui::TEXT_INPUT_MODE_DEFAULT
;
1385 bool Textfield::CanComposeInline() const {
1389 gfx::Rect
Textfield::GetCaretBounds() const {
1390 gfx::Rect rect
= GetRenderText()->GetUpdatedCursorBounds();
1391 ConvertRectToScreen(this, &rect
);
1395 bool Textfield::GetCompositionCharacterBounds(uint32 index
,
1396 gfx::Rect
* rect
) const {
1398 if (!HasCompositionText())
1400 gfx::RenderText
* render_text
= GetRenderText();
1401 const gfx::Range
& composition_range
= render_text
->GetCompositionRange();
1402 DCHECK(!composition_range
.is_empty());
1404 size_t text_index
= composition_range
.start() + index
;
1405 if (composition_range
.end() <= text_index
)
1407 if (!render_text
->IsValidCursorIndex(text_index
)) {
1408 text_index
= render_text
->IndexOfAdjacentGrapheme(
1409 text_index
, gfx::CURSOR_BACKWARD
);
1411 if (text_index
< composition_range
.start())
1413 const gfx::SelectionModel
caret(text_index
, gfx::CURSOR_BACKWARD
);
1414 *rect
= render_text
->GetCursorBounds(caret
, false);
1415 ConvertRectToScreen(this, rect
);
1419 bool Textfield::HasCompositionText() const {
1420 return model_
->HasCompositionText();
1423 bool Textfield::GetTextRange(gfx::Range
* range
) const {
1424 if (!ImeEditingAllowed())
1427 model_
->GetTextRange(range
);
1431 bool Textfield::GetCompositionTextRange(gfx::Range
* range
) const {
1432 if (!ImeEditingAllowed())
1435 model_
->GetCompositionTextRange(range
);
1439 bool Textfield::GetSelectionRange(gfx::Range
* range
) const {
1440 if (!ImeEditingAllowed())
1442 *range
= GetRenderText()->selection();
1446 bool Textfield::SetSelectionRange(const gfx::Range
& range
) {
1447 if (!ImeEditingAllowed() || !range
.IsValid())
1449 OnBeforeUserAction();
1451 OnAfterUserAction();
1455 bool Textfield::DeleteRange(const gfx::Range
& range
) {
1456 if (!ImeEditingAllowed() || range
.is_empty())
1459 OnBeforeUserAction();
1460 model_
->SelectRange(range
);
1461 if (model_
->HasSelection()) {
1462 model_
->DeleteSelection();
1463 UpdateAfterChange(true, true);
1465 OnAfterUserAction();
1469 bool Textfield::GetTextFromRange(const gfx::Range
& range
,
1470 base::string16
* range_text
) const {
1471 if (!ImeEditingAllowed() || !range
.IsValid())
1474 gfx::Range text_range
;
1475 if (!GetTextRange(&text_range
) || !text_range
.Contains(range
))
1478 *range_text
= model_
->GetTextFromRange(range
);
1482 void Textfield::OnInputMethodChanged() {}
1484 bool Textfield::ChangeTextDirectionAndLayoutAlignment(
1485 base::i18n::TextDirection direction
) {
1486 // Restore text directionality mode when the indicated direction matches the
1487 // current forced mode; otherwise, force the mode indicated. This helps users
1488 // manage BiDi text layout without getting stuck in forced LTR or RTL modes.
1489 const gfx::DirectionalityMode mode
= direction
== base::i18n::RIGHT_TO_LEFT
?
1490 gfx::DIRECTIONALITY_FORCE_RTL
: gfx::DIRECTIONALITY_FORCE_LTR
;
1491 if (mode
== GetRenderText()->directionality_mode())
1492 GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT
);
1494 GetRenderText()->SetDirectionalityMode(mode
);
1499 void Textfield::ExtendSelectionAndDelete(size_t before
, size_t after
) {
1500 gfx::Range range
= GetRenderText()->selection();
1501 DCHECK_GE(range
.start(), before
);
1503 range
.set_start(range
.start() - before
);
1504 range
.set_end(range
.end() + after
);
1505 gfx::Range text_range
;
1506 if (GetTextRange(&text_range
) && text_range
.Contains(range
))
1510 void Textfield::EnsureCaretInRect(const gfx::Rect
& rect
) {}
1512 void Textfield::OnCandidateWindowShown() {}
1514 void Textfield::OnCandidateWindowUpdated() {}
1516 void Textfield::OnCandidateWindowHidden() {}
1518 bool Textfield::IsEditingCommandEnabled(int command_id
) {
1519 return IsCommandIdEnabled(command_id
);
1522 void Textfield::ExecuteEditingCommand(int command_id
) {
1523 ExecuteCommand(command_id
);
1526 ////////////////////////////////////////////////////////////////////////////////
1527 // Textfield, protected:
1529 gfx::RenderText
* Textfield::GetRenderText() const {
1530 return model_
->render_text();
1533 base::string16
Textfield::GetSelectionClipboardText() const {
1534 base::string16 selection_clipboard_text
;
1535 ui::Clipboard::GetForCurrentThread()->ReadText(
1536 ui::CLIPBOARD_TYPE_SELECTION
, &selection_clipboard_text
);
1537 return selection_clipboard_text
;
1540 ////////////////////////////////////////////////////////////////////////////////
1541 // Textfield, private:
1543 void Textfield::AccessibilitySetValue(const base::string16
& new_value
) {
1550 void Textfield::UpdateBackgroundColor() {
1551 const SkColor color
= GetBackgroundColor();
1552 set_background(Background::CreateSolidBackground(color
));
1553 GetRenderText()->set_background_is_transparent(SkColorGetA(color
) != 0xFF);
1557 void Textfield::UpdateAfterChange(bool text_changed
, bool cursor_changed
) {
1560 controller_
->ContentsChanged(this, text());
1561 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED
, true);
1563 if (cursor_changed
) {
1564 cursor_visible_
= true;
1566 if (cursor_repaint_timer_
.IsRunning())
1567 cursor_repaint_timer_
.Reset();
1568 if (!text_changed
) {
1569 // TEXT_CHANGED implies TEXT_SELECTION_CHANGED, so we only need to fire
1570 // this if only the selection changed.
1571 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_SELECTION_CHANGED
, true);
1574 if (text_changed
|| cursor_changed
) {
1575 OnCaretBoundsChanged();
1580 void Textfield::UpdateCursor() {
1581 const size_t caret_blink_ms
= Textfield::GetCaretBlinkMs();
1582 cursor_visible_
= !cursor_visible_
|| (caret_blink_ms
== 0);
1586 void Textfield::RepaintCursor() {
1587 gfx::Rect
r(GetRenderText()->GetUpdatedCursorBounds());
1588 r
.Inset(-1, -1, -1, -1);
1589 SchedulePaintInRect(r
);
1592 void Textfield::PaintTextAndCursor(gfx::Canvas
* canvas
) {
1593 TRACE_EVENT0("views", "Textfield::PaintTextAndCursor");
1596 // Draw placeholder text if needed.
1597 gfx::RenderText
* render_text
= GetRenderText();
1598 if (text().empty() && !GetPlaceholderText().empty()) {
1599 canvas
->DrawStringRect(GetPlaceholderText(), GetFontList(),
1600 placeholder_text_color(), render_text
->display_rect());
1603 // Draw the text, cursor, and selection.
1604 render_text
->set_cursor_visible(cursor_visible_
&& !drop_cursor_visible_
&&
1606 render_text
->Draw(canvas
);
1608 // Draw the detached drop cursor that marks where the text will be dropped.
1609 if (drop_cursor_visible_
)
1610 render_text
->DrawCursor(canvas
, drop_cursor_position_
);
1615 void Textfield::MoveCursorTo(const gfx::Point
& point
, bool select
) {
1616 if (model_
->MoveCursorTo(point
, select
))
1617 UpdateAfterChange(false, true);
1620 void Textfield::SelectThroughLastDragLocation() {
1621 OnBeforeUserAction();
1622 model_
->MoveCursorTo(last_drag_location_
, true);
1623 if (aggregated_clicks_
== 1) {
1624 model_
->SelectWord();
1625 // Expand the selection so the initially selected word remains selected.
1626 gfx::Range selection
= GetRenderText()->selection();
1627 const size_t min
= std::min(selection
.GetMin(),
1628 double_click_word_
.GetMin());
1629 const size_t max
= std::max(selection
.GetMax(),
1630 double_click_word_
.GetMax());
1631 const bool reversed
= selection
.is_reversed();
1632 selection
.set_start(reversed
? max
: min
);
1633 selection
.set_end(reversed
? min
: max
);
1634 model_
->SelectRange(selection
);
1636 UpdateAfterChange(false, true);
1637 OnAfterUserAction();
1640 void Textfield::OnCaretBoundsChanged() {
1641 if (GetInputMethod())
1642 GetInputMethod()->OnCaretBoundsChanged(this);
1643 if (touch_selection_controller_
)
1644 touch_selection_controller_
->SelectionChanged();
1647 void Textfield::OnBeforeUserAction() {
1648 DCHECK(!performing_user_action_
);
1649 performing_user_action_
= true;
1651 controller_
->OnBeforeUserAction(this);
1654 void Textfield::OnAfterUserAction() {
1656 controller_
->OnAfterUserAction(this);
1657 DCHECK(performing_user_action_
);
1658 performing_user_action_
= false;
1661 bool Textfield::Cut() {
1662 if (!read_only() && text_input_type_
!= ui::TEXT_INPUT_TYPE_PASSWORD
&&
1665 controller_
->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE
);
1671 bool Textfield::Copy() {
1672 if (text_input_type_
!= ui::TEXT_INPUT_TYPE_PASSWORD
&& model_
->Copy()) {
1674 controller_
->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE
);
1680 bool Textfield::Paste() {
1681 if (!read_only() && model_
->Paste()) {
1683 controller_
->OnAfterPaste();
1689 void Textfield::UpdateContextMenu() {
1690 if (!context_menu_contents_
.get()) {
1691 context_menu_contents_
.reset(new ui::SimpleMenuModel(this));
1692 context_menu_contents_
->AddItemWithStringId(IDS_APP_UNDO
, IDS_APP_UNDO
);
1693 context_menu_contents_
->AddSeparator(ui::NORMAL_SEPARATOR
);
1694 context_menu_contents_
->AddItemWithStringId(IDS_APP_CUT
, IDS_APP_CUT
);
1695 context_menu_contents_
->AddItemWithStringId(IDS_APP_COPY
, IDS_APP_COPY
);
1696 context_menu_contents_
->AddItemWithStringId(IDS_APP_PASTE
, IDS_APP_PASTE
);
1697 context_menu_contents_
->AddItemWithStringId(IDS_APP_DELETE
, IDS_APP_DELETE
);
1698 context_menu_contents_
->AddSeparator(ui::NORMAL_SEPARATOR
);
1699 context_menu_contents_
->AddItemWithStringId(IDS_APP_SELECT_ALL
,
1700 IDS_APP_SELECT_ALL
);
1702 controller_
->UpdateContextMenu(context_menu_contents_
.get());
1704 context_menu_runner_
.reset(
1705 new MenuRunner(context_menu_contents_
.get(),
1706 MenuRunner::HAS_MNEMONICS
| MenuRunner::CONTEXT_MENU
));
1709 void Textfield::TrackMouseClicks(const ui::MouseEvent
& event
) {
1710 if (event
.IsOnlyLeftMouseButton()) {
1711 base::TimeDelta time_delta
= event
.time_stamp() - last_click_time_
;
1712 if (time_delta
.InMilliseconds() <= GetDoubleClickInterval() &&
1713 !ExceededDragThreshold(event
.location() - last_click_location_
)) {
1714 // Upon clicking after a triple click, the count should go back to double
1715 // click and alternate between double and triple. This assignment maps
1716 // 0 to 1, 1 to 2, 2 to 1.
1717 aggregated_clicks_
= (aggregated_clicks_
% 2) + 1;
1719 aggregated_clicks_
= 0;
1721 last_click_time_
= event
.time_stamp();
1722 last_click_location_
= event
.location();
1726 bool Textfield::ImeEditingAllowed() const {
1727 // Disallow input method editing of password fields.
1728 ui::TextInputType t
= GetTextInputType();
1729 return (t
!= ui::TEXT_INPUT_TYPE_NONE
&& t
!= ui::TEXT_INPUT_TYPE_PASSWORD
);
1732 void Textfield::RevealPasswordChar(int index
) {
1733 GetRenderText()->SetObscuredRevealIndex(index
);
1737 password_reveal_timer_
.Start(FROM_HERE
, password_reveal_duration_
,
1738 base::Bind(&Textfield::RevealPasswordChar
,
1739 weak_ptr_factory_
.GetWeakPtr(), -1));
1743 void Textfield::CreateTouchSelectionControllerAndNotifyIt() {
1744 if (!touch_selection_controller_
) {
1745 touch_selection_controller_
.reset(
1746 ui::TouchSelectionController::create(this));
1748 if (touch_selection_controller_
)
1749 touch_selection_controller_
->SelectionChanged();
1752 void Textfield::UpdateSelectionClipboard() const {
1753 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1754 if (performing_user_action_
&& HasSelection()) {
1755 ui::ScopedClipboardWriter(
1756 ui::Clipboard::GetForCurrentThread(),
1757 ui::CLIPBOARD_TYPE_SELECTION
).WriteText(GetSelectedText());
1759 controller_
->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_SELECTION
);
1764 void Textfield::PasteSelectionClipboard(const ui::MouseEvent
& event
) {
1765 DCHECK(event
.IsOnlyMiddleMouseButton());
1766 DCHECK(!read_only());
1767 base::string16 selection_clipboard_text
= GetSelectionClipboardText();
1768 if (!selection_clipboard_text
.empty()) {
1769 OnBeforeUserAction();
1770 gfx::Range range
= GetSelectionModel().selection();
1771 gfx::LogicalCursorDirection affinity
= GetSelectionModel().caret_affinity();
1772 const gfx::SelectionModel mouse
=
1773 GetRenderText()->FindCursorPosition(event
.location());
1774 model_
->MoveCursorTo(mouse
);
1775 model_
->InsertText(selection_clipboard_text
);
1776 // Update the new selection range as needed.
1777 if (range
.GetMin() >= mouse
.caret_pos()) {
1778 const size_t length
= selection_clipboard_text
.length();
1779 range
= gfx::Range(range
.start() + length
, range
.end() + length
);
1781 model_
->MoveCursorTo(gfx::SelectionModel(range
, affinity
));
1782 UpdateAfterChange(true, true);
1783 OnAfterUserAction();
1787 } // namespace views