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/trace_event/trace_event.h"
10 #include "ui/accessibility/ax_view_state.h"
11 #include "ui/base/clipboard/scoped_clipboard_writer.h"
12 #include "ui/base/cursor/cursor.h"
13 #include "ui/base/dragdrop/drag_drop_types.h"
14 #include "ui/base/dragdrop/drag_utils.h"
15 #include "ui/base/touch/selection_bound.h"
16 #include "ui/base/ui_base_switches_util.h"
17 #include "ui/compositor/paint_context.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/geometry/insets.h"
24 #include "ui/gfx/screen.h"
25 #include "ui/native_theme/native_theme.h"
26 #include "ui/strings/grit/ui_strings.h"
27 #include "ui/views/background.h"
28 #include "ui/views/controls/focusable_border.h"
29 #include "ui/views/controls/label.h"
30 #include "ui/views/controls/menu/menu_runner.h"
31 #include "ui/views/controls/native/native_view_host.h"
32 #include "ui/views/controls/textfield/textfield_controller.h"
33 #include "ui/views/drag_utils.h"
34 #include "ui/views/ime/input_method.h"
35 #include "ui/views/metrics.h"
36 #include "ui/views/native_cursor.h"
37 #include "ui/views/painter.h"
38 #include "ui/views/views_delegate.h"
39 #include "ui/views/widget/widget.h"
42 #include "base/win/win_util.h"
45 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
46 #include "base/strings/utf_string_conversions.h"
47 #include "ui/events/linux/text_edit_command_auralinux.h"
48 #include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h"
55 // Default placeholder text color.
56 const SkColor kDefaultPlaceholderTextColor
= SK_ColorLTGRAY
;
58 const int kNoCommand
= 0;
60 void ConvertRectToScreen(const View
* src
, gfx::Rect
* r
) {
63 gfx::Point new_origin
= r
->origin();
64 View::ConvertPointToScreen(src
, &new_origin
);
65 r
->set_origin(new_origin
);
68 // Get the drag selection timer delay, respecting animation scaling for testing.
69 int GetDragSelectionDelay() {
70 switch (ui::ScopedAnimationDurationScaleMode::duration_scale_mode()) {
71 case ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION
: return 100;
72 case ui::ScopedAnimationDurationScaleMode::FAST_DURATION
: return 25;
73 case ui::ScopedAnimationDurationScaleMode::SLOW_DURATION
: return 400;
74 case ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION
: return 1;
75 case ui::ScopedAnimationDurationScaleMode::ZERO_DURATION
: return 0;
80 // Get the default command for a given key |event| and selection state.
81 int GetCommandForKeyEvent(const ui::KeyEvent
& event
, bool has_selection
) {
82 if (event
.type() != ui::ET_KEY_PRESSED
|| event
.IsUnicodeKeyCode())
85 const bool shift
= event
.IsShiftDown();
86 const bool control
= event
.IsControlDown();
87 const bool alt
= event
.IsAltDown() || event
.IsAltGrDown();
88 switch (event
.key_code()) {
90 if (control
&& !shift
&& !alt
)
92 return (control
&& shift
&& !alt
) ? IDS_APP_REDO
: kNoCommand
;
94 return (control
&& !alt
) ? IDS_APP_REDO
: kNoCommand
;
96 return (control
&& !alt
) ? IDS_APP_SELECT_ALL
: kNoCommand
;
98 return (control
&& !alt
) ? IDS_APP_CUT
: kNoCommand
;
100 return (control
&& !alt
) ? IDS_APP_COPY
: kNoCommand
;
102 return (control
&& !alt
) ? IDS_APP_PASTE
: kNoCommand
;
104 // Ignore alt+right, which may be a browser navigation shortcut.
108 return control
? IDS_MOVE_WORD_RIGHT
: IDS_MOVE_RIGHT
;
109 return control
? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
110 IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
;
112 // Ignore alt+left, which may be a browser navigation shortcut.
116 return control
? IDS_MOVE_WORD_LEFT
: IDS_MOVE_LEFT
;
117 return control
? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
118 IDS_MOVE_LEFT_AND_MODIFY_SELECTION
;
120 return shift
? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
:
121 IDS_MOVE_TO_BEGINNING_OF_LINE
;
123 return shift
? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
:
124 IDS_MOVE_TO_END_OF_LINE
;
126 if (!control
|| has_selection
)
127 return IDS_DELETE_BACKWARD
;
128 #if defined(OS_LINUX)
129 // Only erase by line break on Linux and ChromeOS.
131 return IDS_DELETE_TO_BEGINNING_OF_LINE
;
133 return IDS_DELETE_WORD_BACKWARD
;
134 case ui::VKEY_DELETE
:
135 if (!control
|| has_selection
)
136 return (shift
&& has_selection
) ? IDS_APP_CUT
: IDS_DELETE_FORWARD
;
137 #if defined(OS_LINUX)
138 // Only erase by line break on Linux and ChromeOS.
140 return IDS_DELETE_TO_END_OF_LINE
;
142 return IDS_DELETE_WORD_FORWARD
;
143 case ui::VKEY_INSERT
:
144 if (control
&& !shift
)
146 return (shift
&& !control
) ? IDS_APP_PASTE
: kNoCommand
;
152 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
153 // Convert a custom text edit |command| to the equivalent views command ID.
154 int GetViewsCommand(const ui::TextEditCommandAuraLinux
& command
, bool rtl
) {
155 const bool select
= command
.extend_selection();
156 switch (command
.command_id()) {
157 case ui::TextEditCommandAuraLinux::COPY
:
159 case ui::TextEditCommandAuraLinux::CUT
:
161 case ui::TextEditCommandAuraLinux::DELETE_BACKWARD
:
162 return IDS_DELETE_BACKWARD
;
163 case ui::TextEditCommandAuraLinux::DELETE_FORWARD
:
164 return IDS_DELETE_FORWARD
;
165 case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_LINE
:
166 case ui::TextEditCommandAuraLinux::DELETE_TO_BEGINING_OF_PARAGRAPH
:
167 return IDS_DELETE_TO_BEGINNING_OF_LINE
;
168 case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_LINE
:
169 case ui::TextEditCommandAuraLinux::DELETE_TO_END_OF_PARAGRAPH
:
170 return IDS_DELETE_TO_END_OF_LINE
;
171 case ui::TextEditCommandAuraLinux::DELETE_WORD_BACKWARD
:
172 return IDS_DELETE_WORD_BACKWARD
;
173 case ui::TextEditCommandAuraLinux::DELETE_WORD_FORWARD
:
174 return IDS_DELETE_WORD_FORWARD
;
175 case ui::TextEditCommandAuraLinux::INSERT_TEXT
:
177 case ui::TextEditCommandAuraLinux::MOVE_BACKWARD
:
179 return select
? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
: IDS_MOVE_RIGHT
;
180 return select
? IDS_MOVE_LEFT_AND_MODIFY_SELECTION
: IDS_MOVE_LEFT
;
181 case ui::TextEditCommandAuraLinux::MOVE_DOWN
:
182 return IDS_MOVE_DOWN
;
183 case ui::TextEditCommandAuraLinux::MOVE_FORWARD
:
185 return select
? IDS_MOVE_LEFT_AND_MODIFY_SELECTION
: IDS_MOVE_LEFT
;
186 return select
? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
: IDS_MOVE_RIGHT
;
187 case ui::TextEditCommandAuraLinux::MOVE_LEFT
:
188 return select
? IDS_MOVE_LEFT_AND_MODIFY_SELECTION
: IDS_MOVE_LEFT
;
189 case ui::TextEditCommandAuraLinux::MOVE_PAGE_DOWN
:
190 case ui::TextEditCommandAuraLinux::MOVE_PAGE_UP
:
192 case ui::TextEditCommandAuraLinux::MOVE_RIGHT
:
193 return select
? IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
: IDS_MOVE_RIGHT
;
194 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_DOCUMENT
:
195 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_LINE
:
196 case ui::TextEditCommandAuraLinux::MOVE_TO_BEGINING_OF_PARAGRAPH
:
197 return select
? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
:
198 IDS_MOVE_TO_BEGINNING_OF_LINE
;
199 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_DOCUMENT
:
200 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_LINE
:
201 case ui::TextEditCommandAuraLinux::MOVE_TO_END_OF_PARAGRAPH
:
202 return select
? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
:
203 IDS_MOVE_TO_END_OF_LINE
;
204 case ui::TextEditCommandAuraLinux::MOVE_UP
:
206 case ui::TextEditCommandAuraLinux::MOVE_WORD_BACKWARD
:
208 return select
? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
211 return select
? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
213 case ui::TextEditCommandAuraLinux::MOVE_WORD_FORWARD
:
215 return select
? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
218 return select
? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
220 case ui::TextEditCommandAuraLinux::MOVE_WORD_LEFT
:
221 return select
? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
223 case ui::TextEditCommandAuraLinux::MOVE_WORD_RIGHT
:
224 return select
? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
226 case ui::TextEditCommandAuraLinux::PASTE
:
227 return IDS_APP_PASTE
;
228 case ui::TextEditCommandAuraLinux::SELECT_ALL
:
229 return IDS_APP_SELECT_ALL
;
230 case ui::TextEditCommandAuraLinux::SET_MARK
:
231 case ui::TextEditCommandAuraLinux::UNSELECT
:
232 case ui::TextEditCommandAuraLinux::INVALID_COMMAND
:
242 const char Textfield::kViewClassName
[] = "Textfield";
243 const int Textfield::kTextPadding
= 3;
246 size_t Textfield::GetCaretBlinkMs() {
247 static const size_t default_value
= 500;
249 static const size_t system_value
= ::GetCaretBlinkTime();
250 if (system_value
!= 0)
251 return (system_value
== INFINITE
) ? 0 : system_value
;
253 return default_value
;
256 Textfield::Textfield()
257 : model_(new TextfieldModel(this)),
259 scheduled_edit_command_(kNoCommand
),
261 default_width_in_chars_(0),
262 use_default_text_color_(true),
263 use_default_background_color_(true),
264 use_default_selection_text_color_(true),
265 use_default_selection_background_color_(true),
266 text_color_(SK_ColorBLACK
),
267 background_color_(SK_ColorWHITE
),
268 selection_text_color_(SK_ColorWHITE
),
269 selection_background_color_(SK_ColorBLUE
),
270 placeholder_text_color_(kDefaultPlaceholderTextColor
),
271 text_input_type_(ui::TEXT_INPUT_TYPE_TEXT
),
272 text_input_flags_(0),
273 performing_user_action_(false),
274 skip_input_method_cancel_composition_(false),
275 cursor_visible_(false),
276 drop_cursor_visible_(false),
277 initiating_drag_(false),
278 aggregated_clicks_(0),
279 drag_start_display_offset_(0),
280 touch_handles_hidden_due_to_scroll_(false),
281 weak_ptr_factory_(this) {
282 set_context_menu_controller(this);
283 set_drag_controller(this);
284 SetBorder(scoped_ptr
<Border
>(new FocusableBorder()));
287 if (ViewsDelegate::views_delegate
) {
288 password_reveal_duration_
= ViewsDelegate::views_delegate
->
289 GetDefaultTextfieldObscuredRevealDuration();
293 Textfield::~Textfield() {}
295 void Textfield::SetReadOnly(bool read_only
) {
296 // Update read-only without changing the focusable state (or active, etc.).
297 read_only_
= read_only
;
298 if (GetInputMethod())
299 GetInputMethod()->OnTextInputTypeChanged(this);
300 SetColor(GetTextColor());
301 UpdateBackgroundColor();
304 void Textfield::SetTextInputType(ui::TextInputType type
) {
305 GetRenderText()->SetObscured(type
== ui::TEXT_INPUT_TYPE_PASSWORD
);
306 text_input_type_
= type
;
307 OnCaretBoundsChanged();
308 if (GetInputMethod())
309 GetInputMethod()->OnTextInputTypeChanged(this);
313 void Textfield::SetTextInputFlags(int flags
) {
314 text_input_flags_
= flags
;
317 void Textfield::SetText(const base::string16
& new_text
) {
318 model_
->SetText(new_text
);
319 OnCaretBoundsChanged();
321 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED
, true);
324 void Textfield::AppendText(const base::string16
& new_text
) {
325 if (new_text
.empty())
327 model_
->Append(new_text
);
328 OnCaretBoundsChanged();
332 void Textfield::InsertOrReplaceText(const base::string16
& new_text
) {
333 if (new_text
.empty())
335 model_
->InsertText(new_text
);
336 OnCaretBoundsChanged();
340 base::i18n::TextDirection
Textfield::GetTextDirection() const {
341 return GetRenderText()->GetDisplayTextDirection();
344 base::string16
Textfield::GetSelectedText() const {
345 return model_
->GetSelectedText();
348 void Textfield::SelectAll(bool reversed
) {
349 model_
->SelectAll(reversed
);
350 UpdateSelectionClipboard();
351 UpdateAfterChange(false, true);
354 void Textfield::SelectWordAt(const gfx::Point
& point
) {
355 model_
->MoveCursorTo(point
, false);
356 model_
->SelectWord();
357 UpdateAfterChange(false, true);
360 void Textfield::ClearSelection() {
361 model_
->ClearSelection();
362 UpdateAfterChange(false, true);
365 bool Textfield::HasSelection() const {
366 return !GetSelectedRange().is_empty();
369 SkColor
Textfield::GetTextColor() const {
370 if (!use_default_text_color_
)
373 return GetNativeTheme()->GetSystemColor(read_only() ?
374 ui::NativeTheme::kColorId_TextfieldReadOnlyColor
:
375 ui::NativeTheme::kColorId_TextfieldDefaultColor
);
378 void Textfield::SetTextColor(SkColor color
) {
380 use_default_text_color_
= false;
384 void Textfield::UseDefaultTextColor() {
385 use_default_text_color_
= true;
386 SetColor(GetTextColor());
389 SkColor
Textfield::GetBackgroundColor() const {
390 if (!use_default_background_color_
)
391 return background_color_
;
393 return GetNativeTheme()->GetSystemColor(read_only() ?
394 ui::NativeTheme::kColorId_TextfieldReadOnlyBackground
:
395 ui::NativeTheme::kColorId_TextfieldDefaultBackground
);
398 void Textfield::SetBackgroundColor(SkColor color
) {
399 background_color_
= color
;
400 use_default_background_color_
= false;
401 UpdateBackgroundColor();
404 void Textfield::UseDefaultBackgroundColor() {
405 use_default_background_color_
= true;
406 UpdateBackgroundColor();
409 SkColor
Textfield::GetSelectionTextColor() const {
410 return use_default_selection_text_color_
?
411 GetNativeTheme()->GetSystemColor(
412 ui::NativeTheme::kColorId_TextfieldSelectionColor
) :
413 selection_text_color_
;
416 void Textfield::SetSelectionTextColor(SkColor color
) {
417 selection_text_color_
= color
;
418 use_default_selection_text_color_
= false;
419 GetRenderText()->set_selection_color(GetSelectionTextColor());
423 void Textfield::UseDefaultSelectionTextColor() {
424 use_default_selection_text_color_
= true;
425 GetRenderText()->set_selection_color(GetSelectionTextColor());
429 void Textfield::SetShadows(const gfx::ShadowValues
& shadows
) {
430 GetRenderText()->set_shadows(shadows
);
434 SkColor
Textfield::GetSelectionBackgroundColor() const {
435 return use_default_selection_background_color_
?
436 GetNativeTheme()->GetSystemColor(
437 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused
) :
438 selection_background_color_
;
441 void Textfield::SetSelectionBackgroundColor(SkColor color
) {
442 selection_background_color_
= color
;
443 use_default_selection_background_color_
= false;
444 GetRenderText()->set_selection_background_focused_color(
445 GetSelectionBackgroundColor());
449 void Textfield::UseDefaultSelectionBackgroundColor() {
450 use_default_selection_background_color_
= true;
451 GetRenderText()->set_selection_background_focused_color(
452 GetSelectionBackgroundColor());
456 bool Textfield::GetCursorEnabled() const {
457 return GetRenderText()->cursor_enabled();
460 void Textfield::SetCursorEnabled(bool enabled
) {
461 GetRenderText()->SetCursorEnabled(enabled
);
464 const gfx::FontList
& Textfield::GetFontList() const {
465 return GetRenderText()->font_list();
468 void Textfield::SetFontList(const gfx::FontList
& font_list
) {
469 GetRenderText()->SetFontList(font_list
);
470 OnCaretBoundsChanged();
471 PreferredSizeChanged();
474 base::string16
Textfield::GetPlaceholderText() const {
475 return placeholder_text_
;
478 gfx::HorizontalAlignment
Textfield::GetHorizontalAlignment() const {
479 return GetRenderText()->horizontal_alignment();
482 void Textfield::SetHorizontalAlignment(gfx::HorizontalAlignment alignment
) {
483 GetRenderText()->SetHorizontalAlignment(alignment
);
486 void Textfield::ShowImeIfNeeded() {
487 if (enabled() && !read_only())
488 GetInputMethod()->ShowImeIfNeeded();
491 bool Textfield::IsIMEComposing() const {
492 return model_
->HasCompositionText();
495 const gfx::Range
& Textfield::GetSelectedRange() const {
496 return GetRenderText()->selection();
499 void Textfield::SelectRange(const gfx::Range
& range
) {
500 model_
->SelectRange(range
);
501 UpdateAfterChange(false, true);
504 const gfx::SelectionModel
& Textfield::GetSelectionModel() const {
505 return GetRenderText()->selection_model();
508 void Textfield::SelectSelectionModel(const gfx::SelectionModel
& sel
) {
509 model_
->SelectSelectionModel(sel
);
510 UpdateAfterChange(false, true);
513 size_t Textfield::GetCursorPosition() const {
514 return model_
->GetCursorPosition();
517 void Textfield::SetColor(SkColor value
) {
518 GetRenderText()->SetColor(value
);
522 void Textfield::ApplyColor(SkColor value
, const gfx::Range
& range
) {
523 GetRenderText()->ApplyColor(value
, range
);
527 void Textfield::SetStyle(gfx::TextStyle style
, bool value
) {
528 GetRenderText()->SetStyle(style
, value
);
532 void Textfield::ApplyStyle(gfx::TextStyle style
,
534 const gfx::Range
& range
) {
535 GetRenderText()->ApplyStyle(style
, value
, range
);
539 void Textfield::ClearEditHistory() {
540 model_
->ClearEditHistory();
543 void Textfield::SetAccessibleName(const base::string16
& name
) {
544 accessible_name_
= name
;
547 void Textfield::ExecuteCommand(int command_id
) {
548 ExecuteCommand(command_id
, ui::EF_NONE
);
551 void Textfield::SetFocusPainter(scoped_ptr
<Painter
> focus_painter
) {
552 focus_painter_
= focus_painter
.Pass();
555 bool Textfield::HasTextBeingDragged() {
556 return initiating_drag_
;
559 ////////////////////////////////////////////////////////////////////////////////
560 // Textfield, View overrides:
562 gfx::Insets
Textfield::GetInsets() const {
563 gfx::Insets insets
= View::GetInsets();
564 insets
+= gfx::Insets(kTextPadding
, kTextPadding
, kTextPadding
, kTextPadding
);
568 int Textfield::GetBaseline() const {
569 return GetInsets().top() + GetRenderText()->GetBaseline();
572 gfx::Size
Textfield::GetPreferredSize() const {
573 const gfx::Insets
& insets
= GetInsets();
574 return gfx::Size(GetFontList().GetExpectedTextWidth(default_width_in_chars_
) +
575 insets
.width(), GetFontList().GetHeight() + insets
.height());
578 const char* Textfield::GetClassName() const {
579 return kViewClassName
;
582 gfx::NativeCursor
Textfield::GetCursor(const ui::MouseEvent
& event
) {
583 bool in_selection
= GetRenderText()->IsPointInSelection(event
.location());
584 bool drag_event
= event
.type() == ui::ET_MOUSE_DRAGGED
;
585 bool text_cursor
= !initiating_drag_
&& (drag_event
|| !in_selection
);
586 return text_cursor
? GetNativeIBeamCursor() : gfx::kNullCursor
;
589 bool Textfield::OnMousePressed(const ui::MouseEvent
& event
) {
590 TrackMouseClicks(event
);
592 if (!controller_
|| !controller_
->HandleMouseEvent(this, event
)) {
593 if (event
.IsOnlyLeftMouseButton() || event
.IsOnlyRightMouseButton()) {
598 if (event
.IsOnlyLeftMouseButton()) {
599 OnBeforeUserAction();
600 initiating_drag_
= false;
601 switch (aggregated_clicks_
) {
603 if (GetRenderText()->IsPointInSelection(event
.location()))
604 initiating_drag_
= true;
606 MoveCursorTo(event
.location(), event
.IsShiftDown());
609 SelectWordAt(event
.location());
610 double_click_word_
= GetRenderText()->selection();
621 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
622 if (event
.IsOnlyMiddleMouseButton()) {
623 if (GetRenderText()->IsPointInSelection(event
.location())) {
624 OnBeforeUserAction();
626 ui::ScopedClipboardWriter(
627 ui::CLIPBOARD_TYPE_SELECTION
).WriteText(base::string16());
629 } else if (!read_only()) {
630 PasteSelectionClipboard(event
);
639 bool Textfield::OnMouseDragged(const ui::MouseEvent
& event
) {
640 last_drag_location_
= event
.location();
642 // Don't adjust the cursor on a potential drag and drop, or if the mouse
643 // movement from the last mouse click does not exceed the drag threshold.
644 if (initiating_drag_
|| !event
.IsOnlyLeftMouseButton() ||
645 !ExceededDragThreshold(last_drag_location_
- last_click_location_
)) {
649 // A timer is used to continuously scroll while selecting beyond side edges.
650 const int x
= event
.location().x();
651 if ((x
>= 0 && x
<= width()) || GetDragSelectionDelay() == 0) {
652 drag_selection_timer_
.Stop();
653 SelectThroughLastDragLocation();
654 } else if (!drag_selection_timer_
.IsRunning()) {
655 // Select through the edge of the visible text, then start the scroll timer.
656 last_drag_location_
.set_x(std::min(std::max(0, x
), width()));
657 SelectThroughLastDragLocation();
658 drag_selection_timer_
.Start(
659 FROM_HERE
, base::TimeDelta::FromMilliseconds(GetDragSelectionDelay()),
660 this, &Textfield::SelectThroughLastDragLocation
);
666 void Textfield::OnMouseReleased(const ui::MouseEvent
& event
) {
667 OnBeforeUserAction();
668 drag_selection_timer_
.Stop();
669 // Cancel suspected drag initiations, the user was clicking in the selection.
670 if (initiating_drag_
)
671 MoveCursorTo(event
.location(), false);
672 initiating_drag_
= false;
673 UpdateSelectionClipboard();
677 bool Textfield::OnKeyPressed(const ui::KeyEvent
& event
) {
678 int edit_command
= scheduled_edit_command_
;
679 scheduled_edit_command_
= kNoCommand
;
681 // Since HandleKeyEvent() might destroy |this|, get a weak pointer and verify
682 // it isn't null before proceeding.
683 base::WeakPtr
<Textfield
> textfield(weak_ptr_factory_
.GetWeakPtr());
685 bool handled
= controller_
&& controller_
->HandleKeyEvent(this, event
);
690 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
691 ui::TextEditKeyBindingsDelegateAuraLinux
* delegate
=
692 ui::GetTextEditKeyBindingsDelegate();
693 std::vector
<ui::TextEditCommandAuraLinux
> commands
;
694 if (!handled
&& delegate
&& delegate
->MatchEvent(event
, &commands
)) {
695 const bool rtl
= GetTextDirection() == base::i18n::RIGHT_TO_LEFT
;
696 for (size_t i
= 0; i
< commands
.size(); ++i
) {
697 const int command
= GetViewsCommand(commands
[i
], rtl
);
698 if (IsCommandIdEnabled(command
)) {
699 ExecuteCommand(command
);
707 if (edit_command
== kNoCommand
)
708 edit_command
= GetCommandForKeyEvent(event
, HasSelection());
710 if (!handled
&& IsCommandIdEnabled(edit_command
)) {
711 ExecuteCommand(edit_command
);
717 ui::TextInputClient
* Textfield::GetTextInputClient() {
721 void Textfield::OnGestureEvent(ui::GestureEvent
* event
) {
722 switch (event
->type()) {
723 case ui::ET_GESTURE_TAP_DOWN
:
728 case ui::ET_GESTURE_TAP
:
729 if (event
->details().tap_count() == 1) {
730 // If tap is on the selection and touch handles are not present, handles
731 // should be shown without changing selection. Otherwise, cursor should
732 // be moved to the tap location.
733 if (touch_selection_controller_
||
734 !GetRenderText()->IsPointInSelection(event
->location())) {
735 OnBeforeUserAction();
736 MoveCursorTo(event
->location(), false);
739 } else if (event
->details().tap_count() == 2) {
740 OnBeforeUserAction();
741 SelectWordAt(event
->location());
744 OnBeforeUserAction();
748 CreateTouchSelectionControllerAndNotifyIt();
751 base::win::DisplayVirtualKeyboard();
755 case ui::ET_GESTURE_LONG_PRESS
:
756 if (!GetRenderText()->IsPointInSelection(event
->location())) {
757 // If long-press happens outside selection, select word and try to
758 // activate touch selection.
759 OnBeforeUserAction();
760 SelectWordAt(event
->location());
762 CreateTouchSelectionControllerAndNotifyIt();
763 // If touch selection activated successfully, mark event as handled so
764 // that the regular context menu is not shown.
765 if (touch_selection_controller_
)
768 // If long-press happens on the selection, deactivate touch selection
769 // and try to initiate drag-drop. If drag-drop is not enabled, context
770 // menu will be shown. Event is not marked as handled to let Views
771 // handle drag-drop or context menu.
772 DestroyTouchSelection();
773 initiating_drag_
= switches::IsTouchDragDropEnabled();
776 case ui::ET_GESTURE_LONG_TAP
:
777 // If touch selection is enabled, the context menu on long tap will be
778 // shown by the |touch_selection_controller_|, hence we mark the event
779 // handled so Views does not try to show context menu on it.
780 if (touch_selection_controller_
)
783 case ui::ET_GESTURE_SCROLL_BEGIN
:
784 touch_handles_hidden_due_to_scroll_
= touch_selection_controller_
!= NULL
;
785 DestroyTouchSelection();
786 drag_start_location_
= event
->location();
787 drag_start_display_offset_
=
788 GetRenderText()->GetUpdatedDisplayOffset().x();
791 case ui::ET_GESTURE_SCROLL_UPDATE
: {
792 int new_offset
= drag_start_display_offset_
+ event
->location().x() -
793 drag_start_location_
.x();
794 GetRenderText()->SetDisplayOffset(new_offset
);
799 case ui::ET_GESTURE_SCROLL_END
:
800 case ui::ET_SCROLL_FLING_START
:
801 if (touch_handles_hidden_due_to_scroll_
) {
802 CreateTouchSelectionControllerAndNotifyIt();
803 touch_handles_hidden_due_to_scroll_
= false;
812 // This function is called by BrowserView to execute clipboard commands.
813 bool Textfield::AcceleratorPressed(const ui::Accelerator
& accelerator
) {
814 ui::KeyEvent
event(accelerator
.type(), accelerator
.key_code(),
815 accelerator
.modifiers());
816 ExecuteCommand(GetCommandForKeyEvent(event
, HasSelection()));
820 void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse
) {
824 bool Textfield::SkipDefaultKeyEventProcessing(const ui::KeyEvent
& event
) {
825 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
826 // Skip any accelerator handling that conflicts with custom keybindings.
827 ui::TextEditKeyBindingsDelegateAuraLinux
* delegate
=
828 ui::GetTextEditKeyBindingsDelegate();
829 std::vector
<ui::TextEditCommandAuraLinux
> commands
;
830 if (delegate
&& delegate
->MatchEvent(event
, &commands
)) {
831 const bool rtl
= GetTextDirection() == base::i18n::RIGHT_TO_LEFT
;
832 for (size_t i
= 0; i
< commands
.size(); ++i
)
833 if (IsCommandIdEnabled(GetViewsCommand(commands
[i
], rtl
)))
838 // Skip backspace accelerator handling; editable textfields handle this key.
839 // Also skip processing Windows [Alt]+<num-pad digit> Unicode alt-codes.
840 const bool is_backspace
= event
.key_code() == ui::VKEY_BACK
;
841 return (is_backspace
&& !read_only()) || event
.IsUnicodeKeyCode();
844 bool Textfield::GetDropFormats(
846 std::set
<OSExchangeData::CustomFormat
>* custom_formats
) {
847 if (!enabled() || read_only())
849 // TODO(msw): Can we support URL, FILENAME, etc.?
850 *formats
= ui::OSExchangeData::STRING
;
852 controller_
->AppendDropFormats(formats
, custom_formats
);
856 bool Textfield::CanDrop(const OSExchangeData
& data
) {
858 std::set
<OSExchangeData::CustomFormat
> custom_formats
;
859 GetDropFormats(&formats
, &custom_formats
);
860 return enabled() && !read_only() &&
861 data
.HasAnyFormat(formats
, custom_formats
);
864 int Textfield::OnDragUpdated(const ui::DropTargetEvent
& event
) {
865 DCHECK(CanDrop(event
.data()));
866 gfx::RenderText
* render_text
= GetRenderText();
867 const gfx::Range
& selection
= render_text
->selection();
868 drop_cursor_position_
= render_text
->FindCursorPosition(event
.location());
869 bool in_selection
= !selection
.is_empty() &&
870 selection
.Contains(gfx::Range(drop_cursor_position_
.caret_pos()));
871 drop_cursor_visible_
= !in_selection
;
872 // TODO(msw): Pan over text when the user drags to the visible text edge.
873 OnCaretBoundsChanged();
876 if (initiating_drag_
) {
878 return ui::DragDropTypes::DRAG_NONE
;
879 return event
.IsControlDown() ? ui::DragDropTypes::DRAG_COPY
:
880 ui::DragDropTypes::DRAG_MOVE
;
882 return ui::DragDropTypes::DRAG_COPY
| ui::DragDropTypes::DRAG_MOVE
;
885 void Textfield::OnDragExited() {
886 drop_cursor_visible_
= false;
890 int Textfield::OnPerformDrop(const ui::DropTargetEvent
& event
) {
891 DCHECK(CanDrop(event
.data()));
892 drop_cursor_visible_
= false;
895 int drag_operation
= controller_
->OnDrop(event
.data());
896 if (drag_operation
!= ui::DragDropTypes::DRAG_NONE
)
897 return drag_operation
;
900 gfx::RenderText
* render_text
= GetRenderText();
901 DCHECK(!initiating_drag_
||
902 !render_text
->IsPointInSelection(event
.location()));
903 OnBeforeUserAction();
904 skip_input_method_cancel_composition_
= true;
906 gfx::SelectionModel drop_destination_model
=
907 render_text
->FindCursorPosition(event
.location());
908 base::string16 new_text
;
909 event
.data().GetString(&new_text
);
911 // Delete the current selection for a drag and drop within this view.
912 const bool move
= initiating_drag_
&& !event
.IsControlDown() &&
913 event
.source_operations() & ui::DragDropTypes::DRAG_MOVE
;
915 // Adjust the drop destination if it is on or after the current selection.
916 size_t pos
= drop_destination_model
.caret_pos();
917 pos
-= render_text
->selection().Intersect(gfx::Range(0, pos
)).length();
918 model_
->DeleteSelectionAndInsertTextAt(new_text
, pos
);
920 model_
->MoveCursorTo(drop_destination_model
);
921 // Drop always inserts text even if the textfield is not in insert mode.
922 model_
->InsertText(new_text
);
924 skip_input_method_cancel_composition_
= false;
925 UpdateAfterChange(true, true);
927 return move
? ui::DragDropTypes::DRAG_MOVE
: ui::DragDropTypes::DRAG_COPY
;
930 void Textfield::OnDragDone() {
931 initiating_drag_
= false;
932 drop_cursor_visible_
= false;
935 void Textfield::GetAccessibleState(ui::AXViewState
* state
) {
936 state
->role
= ui::AX_ROLE_TEXT_FIELD
;
937 state
->name
= accessible_name_
;
939 state
->AddStateFlag(ui::AX_STATE_READ_ONLY
);
940 if (text_input_type_
== ui::TEXT_INPUT_TYPE_PASSWORD
) {
941 state
->AddStateFlag(ui::AX_STATE_PROTECTED
);
942 state
->value
= base::string16(text().size(), '*');
944 state
->value
= text();
946 const gfx::Range range
= GetSelectedRange();
947 state
->selection_start
= range
.start();
948 state
->selection_end
= range
.end();
951 state
->set_value_callback
=
952 base::Bind(&Textfield::AccessibilitySetValue
,
953 weak_ptr_factory_
.GetWeakPtr());
957 void Textfield::OnBoundsChanged(const gfx::Rect
& previous_bounds
) {
958 // Textfield insets include a reasonable amount of whitespace on all sides of
959 // the default font list. Fallback fonts with larger heights may paint over
960 // the vertical whitespace as needed. Alternate solutions involve undesirable
961 // behavior like changing the default font size, shrinking some fallback fonts
962 // beyond their legibility, or enlarging controls dynamically with content.
963 gfx::Rect bounds
= GetContentsBounds();
964 // GetContentsBounds() does not actually use the local GetInsets() override.
965 bounds
.Inset(gfx::Insets(0, kTextPadding
, 0, kTextPadding
));
966 GetRenderText()->SetDisplayRect(bounds
);
967 OnCaretBoundsChanged();
970 bool Textfield::GetNeedsNotificationWhenVisibleBoundsChange() const {
974 void Textfield::OnVisibleBoundsChanged() {
975 if (touch_selection_controller_
)
976 touch_selection_controller_
->SelectionChanged();
979 void Textfield::OnEnabledChanged() {
980 View::OnEnabledChanged();
981 if (GetInputMethod())
982 GetInputMethod()->OnTextInputTypeChanged(this);
986 void Textfield::OnPaint(gfx::Canvas
* canvas
) {
987 OnPaintBackground(canvas
);
988 PaintTextAndCursor(canvas
);
989 OnPaintBorder(canvas
);
992 void Textfield::OnFocus() {
993 GetRenderText()->set_focused(true);
994 cursor_visible_
= true;
996 GetInputMethod()->OnFocus();
997 OnCaretBoundsChanged();
999 const size_t caret_blink_ms
= Textfield::GetCaretBlinkMs();
1000 if (caret_blink_ms
!= 0) {
1001 cursor_repaint_timer_
.Start(FROM_HERE
,
1002 base::TimeDelta::FromMilliseconds(caret_blink_ms
), this,
1003 &Textfield::UpdateCursor
);
1010 void Textfield::OnBlur() {
1011 GetRenderText()->set_focused(false);
1012 GetInputMethod()->OnBlur();
1013 cursor_repaint_timer_
.Stop();
1014 if (cursor_visible_
) {
1015 cursor_visible_
= false;
1019 DestroyTouchSelection();
1021 // Border typically draws focus indicator.
1025 gfx::Point
Textfield::GetKeyboardContextMenuLocation() {
1026 return GetCaretBounds().bottom_right();
1029 void Textfield::OnNativeThemeChanged(const ui::NativeTheme
* theme
) {
1030 gfx::RenderText
* render_text
= GetRenderText();
1031 render_text
->SetColor(GetTextColor());
1032 UpdateBackgroundColor();
1033 render_text
->set_cursor_color(GetTextColor());
1034 render_text
->set_selection_color(GetSelectionTextColor());
1035 render_text
->set_selection_background_focused_color(
1036 GetSelectionBackgroundColor());
1039 ////////////////////////////////////////////////////////////////////////////////
1040 // Textfield, TextfieldModel::Delegate overrides:
1042 void Textfield::OnCompositionTextConfirmedOrCleared() {
1043 if (!skip_input_method_cancel_composition_
)
1044 GetInputMethod()->CancelComposition(this);
1047 ////////////////////////////////////////////////////////////////////////////////
1048 // Textfield, ContextMenuController overrides:
1050 void Textfield::ShowContextMenuForView(View
* source
,
1051 const gfx::Point
& point
,
1052 ui::MenuSourceType source_type
) {
1053 UpdateContextMenu();
1054 ignore_result(context_menu_runner_
->RunMenuAt(GetWidget(),
1056 gfx::Rect(point
, gfx::Size()),
1057 MENU_ANCHOR_TOPLEFT
,
1061 ////////////////////////////////////////////////////////////////////////////////
1062 // Textfield, DragController overrides:
1064 void Textfield::WriteDragDataForView(View
* sender
,
1065 const gfx::Point
& press_pt
,
1066 OSExchangeData
* data
) {
1067 const base::string16
& selected_text(GetSelectedText());
1068 data
->SetString(selected_text
);
1069 Label
label(selected_text
, GetFontList());
1070 label
.SetBackgroundColor(GetBackgroundColor());
1071 label
.SetSubpixelRenderingEnabled(false);
1072 gfx::Size
size(label
.GetPreferredSize());
1073 gfx::NativeView native_view
= GetWidget()->GetNativeView();
1074 gfx::Display display
= gfx::Screen::GetScreenFor(native_view
)->
1075 GetDisplayNearestWindow(native_view
);
1076 size
.SetToMin(gfx::Size(display
.size().width(), height()));
1077 label
.SetBoundsRect(gfx::Rect(size
));
1078 scoped_ptr
<gfx::Canvas
> canvas(
1079 GetCanvasForDragImage(GetWidget(), label
.size()));
1080 label
.SetEnabledColor(GetTextColor());
1081 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1082 // Desktop Linux Aura does not yet support transparency in drag images.
1083 canvas
->DrawColor(GetBackgroundColor());
1085 label
.Paint(ui::PaintContext(canvas
.get()));
1086 const gfx::Vector2d
kOffset(-15, 0);
1087 drag_utils::SetDragImageOnDataObject(*canvas
, kOffset
, data
);
1089 controller_
->OnWriteDragData(data
);
1092 int Textfield::GetDragOperationsForView(View
* sender
, const gfx::Point
& p
) {
1093 int drag_operations
= ui::DragDropTypes::DRAG_COPY
;
1094 if (!enabled() || text_input_type_
== ui::TEXT_INPUT_TYPE_PASSWORD
||
1095 !GetRenderText()->IsPointInSelection(p
)) {
1096 drag_operations
= ui::DragDropTypes::DRAG_NONE
;
1097 } else if (sender
== this && !read_only()) {
1099 ui::DragDropTypes::DRAG_MOVE
| ui::DragDropTypes::DRAG_COPY
;
1102 controller_
->OnGetDragOperationsForTextfield(&drag_operations
);
1103 return drag_operations
;
1106 bool Textfield::CanStartDragForView(View
* sender
,
1107 const gfx::Point
& press_pt
,
1108 const gfx::Point
& p
) {
1109 return initiating_drag_
&& GetRenderText()->IsPointInSelection(press_pt
);
1112 ////////////////////////////////////////////////////////////////////////////////
1113 // Textfield, ui::TouchEditable overrides:
1115 void Textfield::SelectRect(const gfx::Point
& start
, const gfx::Point
& end
) {
1116 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE
)
1119 gfx::SelectionModel start_caret
= GetRenderText()->FindCursorPosition(start
);
1120 gfx::SelectionModel end_caret
= GetRenderText()->FindCursorPosition(end
);
1121 gfx::SelectionModel
selection(
1122 gfx::Range(start_caret
.caret_pos(), end_caret
.caret_pos()),
1123 end_caret
.caret_affinity());
1125 OnBeforeUserAction();
1126 SelectSelectionModel(selection
);
1127 OnAfterUserAction();
1130 void Textfield::MoveCaretTo(const gfx::Point
& point
) {
1131 SelectRect(point
, point
);
1134 void Textfield::GetSelectionEndPoints(ui::SelectionBound
* anchor
,
1135 ui::SelectionBound
* focus
) {
1136 gfx::RenderText
* render_text
= GetRenderText();
1137 const gfx::SelectionModel
& sel
= render_text
->selection_model();
1138 gfx::SelectionModel start_sel
=
1139 render_text
->GetSelectionModelForSelectionStart();
1140 gfx::Rect r1
= render_text
->GetCursorBounds(start_sel
, true);
1141 gfx::Rect r2
= render_text
->GetCursorBounds(sel
, true);
1143 anchor
->SetEdge(r1
.origin(), r1
.bottom_left());
1144 focus
->SetEdge(r2
.origin(), r2
.bottom_left());
1146 // Determine the SelectionBound's type for focus and anchor.
1147 // TODO(mfomitchev): Ideally we should have different logical directions for
1148 // start and end to support proper handle direction for mixed LTR/RTL text.
1149 const bool ltr
= GetTextDirection() != base::i18n::RIGHT_TO_LEFT
;
1150 size_t anchor_position_index
= sel
.selection().start();
1151 size_t focus_position_index
= sel
.selection().end();
1153 if (anchor_position_index
== focus_position_index
) {
1154 anchor
->set_type(ui::SelectionBound::CENTER
);
1155 focus
->set_type(ui::SelectionBound::CENTER
);
1156 } else if ((ltr
&& anchor_position_index
< focus_position_index
) ||
1157 (!ltr
&& anchor_position_index
> focus_position_index
)) {
1158 anchor
->set_type(ui::SelectionBound::LEFT
);
1159 focus
->set_type(ui::SelectionBound::RIGHT
);
1161 anchor
->set_type(ui::SelectionBound::RIGHT
);
1162 focus
->set_type(ui::SelectionBound::LEFT
);
1166 gfx::Rect
Textfield::GetBounds() {
1167 return GetLocalBounds();
1170 gfx::NativeView
Textfield::GetNativeView() const {
1171 return GetWidget()->GetNativeView();
1174 void Textfield::ConvertPointToScreen(gfx::Point
* point
) {
1175 View::ConvertPointToScreen(this, point
);
1178 void Textfield::ConvertPointFromScreen(gfx::Point
* point
) {
1179 View::ConvertPointFromScreen(this, point
);
1182 bool Textfield::DrawsHandles() {
1186 void Textfield::OpenContextMenu(const gfx::Point
& anchor
) {
1187 DestroyTouchSelection();
1188 ShowContextMenu(anchor
, ui::MENU_SOURCE_TOUCH_EDIT_MENU
);
1191 void Textfield::DestroyTouchSelection() {
1192 touch_selection_controller_
.reset();
1195 ////////////////////////////////////////////////////////////////////////////////
1196 // Textfield, ui::SimpleMenuModel::Delegate overrides:
1198 bool Textfield::IsCommandIdChecked(int command_id
) const {
1202 bool Textfield::IsCommandIdEnabled(int command_id
) const {
1203 base::string16 result
;
1204 bool editable
= !read_only();
1205 bool readable
= text_input_type_
!= ui::TEXT_INPUT_TYPE_PASSWORD
;
1206 switch (command_id
) {
1208 return editable
&& model_
->CanUndo();
1210 return editable
&& model_
->CanRedo();
1212 return editable
&& readable
&& model_
->HasSelection();
1214 return readable
&& model_
->HasSelection();
1216 ui::Clipboard::GetForCurrentThread()->ReadText(
1217 ui::CLIPBOARD_TYPE_COPY_PASTE
, &result
);
1218 return editable
&& !result
.empty();
1219 case IDS_APP_DELETE
:
1220 return editable
&& model_
->HasSelection();
1221 case IDS_APP_SELECT_ALL
:
1222 return !text().empty();
1223 case IDS_DELETE_FORWARD
:
1224 case IDS_DELETE_BACKWARD
:
1225 case IDS_DELETE_TO_BEGINNING_OF_LINE
:
1226 case IDS_DELETE_TO_END_OF_LINE
:
1227 case IDS_DELETE_WORD_BACKWARD
:
1228 case IDS_DELETE_WORD_FORWARD
:
1231 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION
:
1232 case IDS_MOVE_RIGHT
:
1233 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
:
1234 case IDS_MOVE_WORD_LEFT
:
1235 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
1236 case IDS_MOVE_WORD_RIGHT
:
1237 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
1238 case IDS_MOVE_TO_BEGINNING_OF_LINE
:
1239 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
:
1240 case IDS_MOVE_TO_END_OF_LINE
:
1241 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
:
1248 bool Textfield::GetAcceleratorForCommandId(int command_id
,
1249 ui::Accelerator
* accelerator
) {
1250 switch (command_id
) {
1252 *accelerator
= ui::Accelerator(ui::VKEY_Z
, ui::EF_CONTROL_DOWN
);
1256 *accelerator
= ui::Accelerator(ui::VKEY_X
, ui::EF_CONTROL_DOWN
);
1260 *accelerator
= ui::Accelerator(ui::VKEY_C
, ui::EF_CONTROL_DOWN
);
1264 *accelerator
= ui::Accelerator(ui::VKEY_V
, ui::EF_CONTROL_DOWN
);
1267 case IDS_APP_SELECT_ALL
:
1268 *accelerator
= ui::Accelerator(ui::VKEY_A
, ui::EF_CONTROL_DOWN
);
1276 void Textfield::ExecuteCommand(int command_id
, int event_flags
) {
1277 DestroyTouchSelection();
1278 if (!IsCommandIdEnabled(command_id
))
1281 bool text_changed
= false;
1282 bool cursor_changed
= false;
1283 bool rtl
= GetTextDirection() == base::i18n::RIGHT_TO_LEFT
;
1284 gfx::VisualCursorDirection begin
= rtl
? gfx::CURSOR_RIGHT
: gfx::CURSOR_LEFT
;
1285 gfx::VisualCursorDirection end
= rtl
? gfx::CURSOR_LEFT
: gfx::CURSOR_RIGHT
;
1286 gfx::SelectionModel selection_model
= GetSelectionModel();
1288 OnBeforeUserAction();
1289 switch (command_id
) {
1291 text_changed
= cursor_changed
= model_
->Undo();
1294 text_changed
= cursor_changed
= model_
->Redo();
1297 text_changed
= cursor_changed
= Cut();
1303 text_changed
= cursor_changed
= Paste();
1305 case IDS_APP_DELETE
:
1306 text_changed
= cursor_changed
= model_
->Delete();
1308 case IDS_APP_SELECT_ALL
:
1311 case IDS_DELETE_BACKWARD
:
1312 text_changed
= cursor_changed
= model_
->Backspace();
1314 case IDS_DELETE_FORWARD
:
1315 text_changed
= cursor_changed
= model_
->Delete();
1317 case IDS_DELETE_TO_END_OF_LINE
:
1318 model_
->MoveCursor(gfx::LINE_BREAK
, end
, true);
1319 text_changed
= cursor_changed
= model_
->Delete();
1321 case IDS_DELETE_TO_BEGINNING_OF_LINE
:
1322 model_
->MoveCursor(gfx::LINE_BREAK
, begin
, true);
1323 text_changed
= cursor_changed
= model_
->Backspace();
1325 case IDS_DELETE_WORD_BACKWARD
:
1326 model_
->MoveCursor(gfx::WORD_BREAK
, begin
, true);
1327 text_changed
= cursor_changed
= model_
->Backspace();
1329 case IDS_DELETE_WORD_FORWARD
:
1330 model_
->MoveCursor(gfx::WORD_BREAK
, end
, true);
1331 text_changed
= cursor_changed
= model_
->Delete();
1334 model_
->MoveCursor(gfx::CHARACTER_BREAK
, gfx::CURSOR_LEFT
, false);
1336 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION
:
1337 model_
->MoveCursor(gfx::CHARACTER_BREAK
, gfx::CURSOR_LEFT
, true);
1339 case IDS_MOVE_RIGHT
:
1340 model_
->MoveCursor(gfx::CHARACTER_BREAK
, gfx::CURSOR_RIGHT
, false);
1342 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
:
1343 model_
->MoveCursor(gfx::CHARACTER_BREAK
, gfx::CURSOR_RIGHT
, true);
1345 case IDS_MOVE_WORD_LEFT
:
1346 model_
->MoveCursor(gfx::WORD_BREAK
, gfx::CURSOR_LEFT
, false);
1348 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
:
1349 model_
->MoveCursor(gfx::WORD_BREAK
, gfx::CURSOR_LEFT
, true);
1351 case IDS_MOVE_WORD_RIGHT
:
1352 model_
->MoveCursor(gfx::WORD_BREAK
, gfx::CURSOR_RIGHT
, false);
1354 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
:
1355 model_
->MoveCursor(gfx::WORD_BREAK
, gfx::CURSOR_RIGHT
, true);
1357 case IDS_MOVE_TO_BEGINNING_OF_LINE
:
1358 model_
->MoveCursor(gfx::LINE_BREAK
, begin
, false);
1360 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
:
1361 model_
->MoveCursor(gfx::LINE_BREAK
, begin
, true);
1363 case IDS_MOVE_TO_END_OF_LINE
:
1364 model_
->MoveCursor(gfx::LINE_BREAK
, end
, false);
1366 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
:
1367 model_
->MoveCursor(gfx::LINE_BREAK
, end
, true);
1374 cursor_changed
|= GetSelectionModel() != selection_model
;
1376 UpdateSelectionClipboard();
1377 UpdateAfterChange(text_changed
, cursor_changed
);
1378 OnAfterUserAction();
1381 ////////////////////////////////////////////////////////////////////////////////
1382 // Textfield, ui::TextInputClient overrides:
1384 void Textfield::SetCompositionText(const ui::CompositionText
& composition
) {
1385 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE
)
1388 OnBeforeUserAction();
1389 skip_input_method_cancel_composition_
= true;
1390 model_
->SetCompositionText(composition
);
1391 skip_input_method_cancel_composition_
= false;
1392 UpdateAfterChange(true, true);
1393 OnAfterUserAction();
1396 void Textfield::ConfirmCompositionText() {
1397 if (!model_
->HasCompositionText())
1400 OnBeforeUserAction();
1401 skip_input_method_cancel_composition_
= true;
1402 model_
->ConfirmCompositionText();
1403 skip_input_method_cancel_composition_
= false;
1404 UpdateAfterChange(true, true);
1405 OnAfterUserAction();
1408 void Textfield::ClearCompositionText() {
1409 if (!model_
->HasCompositionText())
1412 OnBeforeUserAction();
1413 skip_input_method_cancel_composition_
= true;
1414 model_
->CancelCompositionText();
1415 skip_input_method_cancel_composition_
= false;
1416 UpdateAfterChange(true, true);
1417 OnAfterUserAction();
1420 void Textfield::InsertText(const base::string16
& new_text
) {
1421 // TODO(suzhe): Filter invalid characters.
1422 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE
|| new_text
.empty())
1425 OnBeforeUserAction();
1426 skip_input_method_cancel_composition_
= true;
1427 if (GetRenderText()->insert_mode())
1428 model_
->InsertText(new_text
);
1430 model_
->ReplaceText(new_text
);
1431 skip_input_method_cancel_composition_
= false;
1432 UpdateAfterChange(true, true);
1433 OnAfterUserAction();
1436 void Textfield::InsertChar(base::char16 ch
, int flags
) {
1437 const int kControlModifierMask
= ui::EF_CONTROL_DOWN
| ui::EF_ALT_DOWN
|
1438 ui::EF_COMMAND_DOWN
| ui::EF_ALTGR_DOWN
|
1441 // Filter out all control characters, including tab and new line characters,
1442 // and all characters with Alt modifier. But allow characters with the AltGr
1443 // modifier. On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a
1444 // different flag that we don't care about.
1445 const bool should_insert_char
=
1446 ((ch
>= 0x20 && ch
< 0x7F) || ch
> 0x9F) &&
1447 (flags
& kControlModifierMask
) != ui::EF_ALT_DOWN
;
1448 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE
|| !should_insert_char
)
1453 if (text_input_type_
== ui::TEXT_INPUT_TYPE_PASSWORD
&&
1454 password_reveal_duration_
!= base::TimeDelta()) {
1455 const size_t change_offset
= model_
->GetCursorPosition();
1456 DCHECK_GT(change_offset
, 0u);
1457 RevealPasswordChar(change_offset
- 1);
1461 gfx::NativeWindow
Textfield::GetAttachedWindow() const {
1462 // Imagine the following hierarchy.
1463 // [NativeWidget A] - FocusManager
1468 // An important thing is that [NativeWidget A] owns Win32 input focus even
1469 // when [View X] is logically focused by FocusManager. As a result, an Win32
1470 // IME may want to interact with the native view of [NativeWidget A] rather
1471 // than that of [NativeWidget B]. This is why we need to call
1472 // GetTopLevelWidget() here.
1473 return GetWidget()->GetTopLevelWidget()->GetNativeWindow();
1476 ui::TextInputType
Textfield::GetTextInputType() const {
1477 if (read_only() || !enabled())
1478 return ui::TEXT_INPUT_TYPE_NONE
;
1479 return text_input_type_
;
1482 ui::TextInputMode
Textfield::GetTextInputMode() const {
1483 return ui::TEXT_INPUT_MODE_DEFAULT
;
1486 int Textfield::GetTextInputFlags() const {
1487 return text_input_flags_
;
1490 bool Textfield::CanComposeInline() const {
1494 gfx::Rect
Textfield::GetCaretBounds() const {
1495 gfx::Rect rect
= GetRenderText()->GetUpdatedCursorBounds();
1496 ConvertRectToScreen(this, &rect
);
1500 bool Textfield::GetCompositionCharacterBounds(uint32 index
,
1501 gfx::Rect
* rect
) const {
1503 if (!HasCompositionText())
1505 gfx::Range composition_range
;
1506 model_
->GetCompositionTextRange(&composition_range
);
1507 DCHECK(!composition_range
.is_empty());
1509 size_t text_index
= composition_range
.start() + index
;
1510 if (composition_range
.end() <= text_index
)
1512 gfx::RenderText
* render_text
= GetRenderText();
1513 if (!render_text
->IsValidCursorIndex(text_index
)) {
1514 text_index
= render_text
->IndexOfAdjacentGrapheme(
1515 text_index
, gfx::CURSOR_BACKWARD
);
1517 if (text_index
< composition_range
.start())
1519 const gfx::SelectionModel
caret(text_index
, gfx::CURSOR_BACKWARD
);
1520 *rect
= render_text
->GetCursorBounds(caret
, false);
1521 ConvertRectToScreen(this, rect
);
1525 bool Textfield::HasCompositionText() const {
1526 return model_
->HasCompositionText();
1529 bool Textfield::GetTextRange(gfx::Range
* range
) const {
1530 if (!ImeEditingAllowed())
1533 model_
->GetTextRange(range
);
1537 bool Textfield::GetCompositionTextRange(gfx::Range
* range
) const {
1538 if (!ImeEditingAllowed())
1541 model_
->GetCompositionTextRange(range
);
1545 bool Textfield::GetSelectionRange(gfx::Range
* range
) const {
1546 if (!ImeEditingAllowed())
1548 *range
= GetRenderText()->selection();
1552 bool Textfield::SetSelectionRange(const gfx::Range
& range
) {
1553 if (!ImeEditingAllowed() || !range
.IsValid())
1555 OnBeforeUserAction();
1557 OnAfterUserAction();
1561 bool Textfield::DeleteRange(const gfx::Range
& range
) {
1562 if (!ImeEditingAllowed() || range
.is_empty())
1565 OnBeforeUserAction();
1566 model_
->SelectRange(range
);
1567 if (model_
->HasSelection()) {
1568 model_
->DeleteSelection();
1569 UpdateAfterChange(true, true);
1571 OnAfterUserAction();
1575 bool Textfield::GetTextFromRange(const gfx::Range
& range
,
1576 base::string16
* range_text
) const {
1577 if (!ImeEditingAllowed() || !range
.IsValid())
1580 gfx::Range text_range
;
1581 if (!GetTextRange(&text_range
) || !text_range
.Contains(range
))
1584 *range_text
= model_
->GetTextFromRange(range
);
1588 void Textfield::OnInputMethodChanged() {}
1590 bool Textfield::ChangeTextDirectionAndLayoutAlignment(
1591 base::i18n::TextDirection direction
) {
1592 // Restore text directionality mode when the indicated direction matches the
1593 // current forced mode; otherwise, force the mode indicated. This helps users
1594 // manage BiDi text layout without getting stuck in forced LTR or RTL modes.
1595 const gfx::DirectionalityMode mode
= direction
== base::i18n::RIGHT_TO_LEFT
?
1596 gfx::DIRECTIONALITY_FORCE_RTL
: gfx::DIRECTIONALITY_FORCE_LTR
;
1597 if (mode
== GetRenderText()->directionality_mode())
1598 GetRenderText()->SetDirectionalityMode(gfx::DIRECTIONALITY_FROM_TEXT
);
1600 GetRenderText()->SetDirectionalityMode(mode
);
1605 void Textfield::ExtendSelectionAndDelete(size_t before
, size_t after
) {
1606 gfx::Range range
= GetRenderText()->selection();
1607 DCHECK_GE(range
.start(), before
);
1609 range
.set_start(range
.start() - before
);
1610 range
.set_end(range
.end() + after
);
1611 gfx::Range text_range
;
1612 if (GetTextRange(&text_range
) && text_range
.Contains(range
))
1616 void Textfield::EnsureCaretInRect(const gfx::Rect
& rect
) {}
1618 void Textfield::OnCandidateWindowShown() {}
1620 void Textfield::OnCandidateWindowUpdated() {}
1622 void Textfield::OnCandidateWindowHidden() {}
1624 bool Textfield::IsEditCommandEnabled(int command_id
) {
1625 return IsCommandIdEnabled(command_id
);
1628 void Textfield::SetEditCommandForNextKeyEvent(int command_id
) {
1629 DCHECK_EQ(kNoCommand
, scheduled_edit_command_
);
1630 scheduled_edit_command_
= command_id
;
1633 ////////////////////////////////////////////////////////////////////////////////
1634 // Textfield, protected:
1636 void Textfield::DoInsertChar(base::char16 ch
) {
1637 OnBeforeUserAction();
1638 skip_input_method_cancel_composition_
= true;
1639 if (GetRenderText()->insert_mode())
1640 model_
->InsertChar(ch
);
1642 model_
->ReplaceChar(ch
);
1643 skip_input_method_cancel_composition_
= false;
1645 UpdateAfterChange(true, true);
1646 OnAfterUserAction();
1649 gfx::RenderText
* Textfield::GetRenderText() const {
1650 return model_
->render_text();
1653 base::string16
Textfield::GetSelectionClipboardText() const {
1654 base::string16 selection_clipboard_text
;
1655 ui::Clipboard::GetForCurrentThread()->ReadText(
1656 ui::CLIPBOARD_TYPE_SELECTION
, &selection_clipboard_text
);
1657 return selection_clipboard_text
;
1660 ////////////////////////////////////////////////////////////////////////////////
1661 // Textfield, private:
1663 void Textfield::AccessibilitySetValue(const base::string16
& new_value
) {
1670 void Textfield::UpdateBackgroundColor() {
1671 const SkColor color
= GetBackgroundColor();
1672 set_background(Background::CreateSolidBackground(color
));
1673 // Disable subpixel rendering when the background color is transparent
1674 // because it draws incorrect colors around the glyphs in that case.
1675 // See crbug.com/115198
1676 GetRenderText()->set_subpixel_rendering_suppressed(
1677 SkColorGetA(color
) != 0xFF);
1681 void Textfield::UpdateAfterChange(bool text_changed
, bool cursor_changed
) {
1684 controller_
->ContentsChanged(this, text());
1685 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_CHANGED
, true);
1687 if (cursor_changed
) {
1688 cursor_visible_
= true;
1690 if (cursor_repaint_timer_
.IsRunning())
1691 cursor_repaint_timer_
.Reset();
1692 if (!text_changed
) {
1693 // TEXT_CHANGED implies TEXT_SELECTION_CHANGED, so we only need to fire
1694 // this if only the selection changed.
1695 NotifyAccessibilityEvent(ui::AX_EVENT_TEXT_SELECTION_CHANGED
, true);
1698 if (text_changed
|| cursor_changed
) {
1699 OnCaretBoundsChanged();
1704 void Textfield::UpdateCursor() {
1705 const size_t caret_blink_ms
= Textfield::GetCaretBlinkMs();
1706 cursor_visible_
= !cursor_visible_
|| (caret_blink_ms
== 0);
1710 void Textfield::RepaintCursor() {
1711 gfx::Rect
r(GetRenderText()->GetUpdatedCursorBounds());
1712 r
.Inset(-1, -1, -1, -1);
1713 SchedulePaintInRect(r
);
1716 void Textfield::PaintTextAndCursor(gfx::Canvas
* canvas
) {
1717 TRACE_EVENT0("views", "Textfield::PaintTextAndCursor");
1720 // Draw placeholder text if needed.
1721 gfx::RenderText
* render_text
= GetRenderText();
1722 if (text().empty() && !GetPlaceholderText().empty()) {
1723 canvas
->DrawStringRect(GetPlaceholderText(), GetFontList(),
1724 placeholder_text_color(), render_text
->display_rect());
1727 // Draw the text, cursor, and selection.
1728 render_text
->set_cursor_visible(cursor_visible_
&& !drop_cursor_visible_
&&
1730 render_text
->Draw(canvas
);
1732 // Draw the detached drop cursor that marks where the text will be dropped.
1733 if (drop_cursor_visible_
)
1734 render_text
->DrawCursor(canvas
, drop_cursor_position_
);
1739 void Textfield::MoveCursorTo(const gfx::Point
& point
, bool select
) {
1740 if (model_
->MoveCursorTo(point
, select
))
1741 UpdateAfterChange(false, true);
1744 void Textfield::SelectThroughLastDragLocation() {
1745 OnBeforeUserAction();
1746 model_
->MoveCursorTo(last_drag_location_
, true);
1747 if (aggregated_clicks_
== 1) {
1748 model_
->SelectWord();
1749 // Expand the selection so the initially selected word remains selected.
1750 gfx::Range selection
= GetRenderText()->selection();
1751 const size_t min
= std::min(selection
.GetMin(),
1752 double_click_word_
.GetMin());
1753 const size_t max
= std::max(selection
.GetMax(),
1754 double_click_word_
.GetMax());
1755 const bool reversed
= selection
.is_reversed();
1756 selection
.set_start(reversed
? max
: min
);
1757 selection
.set_end(reversed
? min
: max
);
1758 model_
->SelectRange(selection
);
1760 UpdateAfterChange(false, true);
1761 OnAfterUserAction();
1764 void Textfield::OnCaretBoundsChanged() {
1765 if (GetInputMethod())
1766 GetInputMethod()->OnCaretBoundsChanged(this);
1767 if (touch_selection_controller_
)
1768 touch_selection_controller_
->SelectionChanged();
1771 void Textfield::OnBeforeUserAction() {
1772 DCHECK(!performing_user_action_
);
1773 performing_user_action_
= true;
1775 controller_
->OnBeforeUserAction(this);
1778 void Textfield::OnAfterUserAction() {
1780 controller_
->OnAfterUserAction(this);
1781 DCHECK(performing_user_action_
);
1782 performing_user_action_
= false;
1785 bool Textfield::Cut() {
1786 if (!read_only() && text_input_type_
!= ui::TEXT_INPUT_TYPE_PASSWORD
&&
1789 controller_
->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE
);
1795 bool Textfield::Copy() {
1796 if (text_input_type_
!= ui::TEXT_INPUT_TYPE_PASSWORD
&& model_
->Copy()) {
1798 controller_
->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_COPY_PASTE
);
1804 bool Textfield::Paste() {
1805 if (!read_only() && model_
->Paste()) {
1807 controller_
->OnAfterPaste();
1813 void Textfield::UpdateContextMenu() {
1814 if (!context_menu_contents_
.get()) {
1815 context_menu_contents_
.reset(new ui::SimpleMenuModel(this));
1816 context_menu_contents_
->AddItemWithStringId(IDS_APP_UNDO
, IDS_APP_UNDO
);
1817 context_menu_contents_
->AddSeparator(ui::NORMAL_SEPARATOR
);
1818 context_menu_contents_
->AddItemWithStringId(IDS_APP_CUT
, IDS_APP_CUT
);
1819 context_menu_contents_
->AddItemWithStringId(IDS_APP_COPY
, IDS_APP_COPY
);
1820 context_menu_contents_
->AddItemWithStringId(IDS_APP_PASTE
, IDS_APP_PASTE
);
1821 context_menu_contents_
->AddItemWithStringId(IDS_APP_DELETE
, IDS_APP_DELETE
);
1822 context_menu_contents_
->AddSeparator(ui::NORMAL_SEPARATOR
);
1823 context_menu_contents_
->AddItemWithStringId(IDS_APP_SELECT_ALL
,
1824 IDS_APP_SELECT_ALL
);
1826 controller_
->UpdateContextMenu(context_menu_contents_
.get());
1828 context_menu_runner_
.reset(
1829 new MenuRunner(context_menu_contents_
.get(),
1830 MenuRunner::HAS_MNEMONICS
| MenuRunner::CONTEXT_MENU
));
1833 void Textfield::TrackMouseClicks(const ui::MouseEvent
& event
) {
1834 if (event
.IsOnlyLeftMouseButton()) {
1835 base::TimeDelta time_delta
= event
.time_stamp() - last_click_time_
;
1836 if (time_delta
.InMilliseconds() <= GetDoubleClickInterval() &&
1837 !ExceededDragThreshold(event
.location() - last_click_location_
)) {
1838 // Upon clicking after a triple click, the count should go back to double
1839 // click and alternate between double and triple. This assignment maps
1840 // 0 to 1, 1 to 2, 2 to 1.
1841 aggregated_clicks_
= (aggregated_clicks_
% 2) + 1;
1843 aggregated_clicks_
= 0;
1845 last_click_time_
= event
.time_stamp();
1846 last_click_location_
= event
.location();
1850 bool Textfield::ImeEditingAllowed() const {
1851 // Disallow input method editing of password fields.
1852 ui::TextInputType t
= GetTextInputType();
1853 return (t
!= ui::TEXT_INPUT_TYPE_NONE
&& t
!= ui::TEXT_INPUT_TYPE_PASSWORD
);
1856 void Textfield::RevealPasswordChar(int index
) {
1857 GetRenderText()->SetObscuredRevealIndex(index
);
1861 password_reveal_timer_
.Start(FROM_HERE
, password_reveal_duration_
,
1862 base::Bind(&Textfield::RevealPasswordChar
,
1863 weak_ptr_factory_
.GetWeakPtr(), -1));
1867 void Textfield::CreateTouchSelectionControllerAndNotifyIt() {
1871 if (!touch_selection_controller_
) {
1872 touch_selection_controller_
.reset(
1873 ui::TouchEditingControllerDeprecated::Create(this));
1875 if (touch_selection_controller_
)
1876 touch_selection_controller_
->SelectionChanged();
1879 void Textfield::UpdateSelectionClipboard() const {
1880 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1881 if (performing_user_action_
&& HasSelection()) {
1882 ui::ScopedClipboardWriter(
1883 ui::CLIPBOARD_TYPE_SELECTION
).WriteText(GetSelectedText());
1885 controller_
->OnAfterCutOrCopy(ui::CLIPBOARD_TYPE_SELECTION
);
1890 void Textfield::PasteSelectionClipboard(const ui::MouseEvent
& event
) {
1891 DCHECK(event
.IsOnlyMiddleMouseButton());
1892 DCHECK(!read_only());
1893 base::string16 selection_clipboard_text
= GetSelectionClipboardText();
1894 OnBeforeUserAction();
1895 const gfx::SelectionModel mouse
=
1896 GetRenderText()->FindCursorPosition(event
.location());
1899 model_
->MoveCursorTo(mouse
);
1900 if (!selection_clipboard_text
.empty()) {
1901 model_
->InsertText(selection_clipboard_text
);
1902 UpdateAfterChange(true, true);
1904 OnAfterUserAction();
1907 } // namespace views