Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(571)

Side by Side Diff: ui/views/controls/textfield/native_textfield_views.cc

Issue 9358049: Implement STYLE_LOWERCASE style for Aura NativeTextfield. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add MaybeLowerCase(), test descriptions and cursor position assertion. Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/views/controls/textfield/native_textfield_views.h" 5 #include "ui/views/controls/textfield/native_textfield_views.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/debug/trace_event.h" 11 #include "base/debug/trace_event.h"
12 #include "base/i18n/case_conversion.h"
12 #include "base/logging.h" 13 #include "base/logging.h"
13 #include "base/message_loop.h" 14 #include "base/message_loop.h"
14 #include "base/utf_string_conversions.h" 15 #include "base/utf_string_conversions.h"
15 #include "grit/app_locale_settings.h" 16 #include "grit/app_locale_settings.h"
16 #include "grit/ui_strings.h" 17 #include "grit/ui_strings.h"
17 #include "third_party/skia/include/core/SkColor.h" 18 #include "third_party/skia/include/core/SkColor.h"
18 #include "ui/base/clipboard/clipboard.h" 19 #include "ui/base/clipboard/clipboard.h"
19 #include "ui/base/dragdrop/drag_drop_types.h" 20 #include "ui/base/dragdrop/drag_drop_types.h"
20 #include "ui/base/l10n/l10n_util.h" 21 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/range/range.h" 22 #include "ui/base/range/range.h"
22 #include "ui/gfx/canvas.h" 23 #include "ui/gfx/canvas.h"
23 #include "ui/gfx/compositor/layer.h" 24 #include "ui/gfx/compositor/layer.h"
24 #include "ui/gfx/insets.h" 25 #include "ui/gfx/insets.h"
25 #include "ui/gfx/render_text.h" 26 #include "ui/gfx/render_text.h"
26 #include "ui/views/background.h" 27 #include "ui/views/background.h"
27 #include "ui/views/border.h" 28 #include "ui/views/border.h"
28 #include "ui/views/controls/focusable_border.h" 29 #include "ui/views/controls/focusable_border.h"
29 #include "ui/views/controls/menu/menu_item_view.h" 30 #include "ui/views/controls/menu/menu_item_view.h"
30 #include "ui/views/controls/menu/menu_model_adapter.h" 31 #include "ui/views/controls/menu/menu_model_adapter.h"
31 #include "ui/views/controls/menu/menu_runner.h" 32 #include "ui/views/controls/menu/menu_runner.h"
32 #include "ui/views/controls/textfield/textfield.h" 33 #include "ui/views/controls/textfield/textfield.h"
33 #include "ui/views/controls/textfield/textfield_controller.h" 34 #include "ui/views/controls/textfield/textfield_controller.h"
34 #include "ui/views/controls/textfield/textfield_views_model.h" 35 #include "ui/views/controls/textfield/textfield_views_model.h"
35 #include "ui/views/events/event.h" 36 #include "ui/views/events/event.h"
36 #include "ui/views/ime/input_method.h" 37 #include "ui/views/ime/input_method.h"
37 #include "ui/views/metrics.h" 38 #include "ui/views/metrics.h"
38 #include "ui/views/views_delegate.h" 39 #include "ui/views/views_delegate.h"
39 #include "ui/views/widget/widget.h" 40 #include "ui/views/widget/widget.h"
41 #include "unicode/uchar.h"
40 42
41 #if defined(USE_AURA) 43 #if defined(USE_AURA)
42 #include "ui/base/cursor/cursor.h" 44 #include "ui/base/cursor/cursor.h"
43 #endif 45 #endif
44 46
45 namespace { 47 namespace {
46 48
47 // Text color for read only. 49 // Text color for read only.
48 const SkColor kReadonlyTextColor = SK_ColorDKGRAY; 50 const SkColor kReadonlyTextColor = SK_ColorDKGRAY;
49 51
(...skipping 20 matching lines...) Expand all
70 skip_input_method_cancel_composition_(false), 72 skip_input_method_cancel_composition_(false),
71 initiating_drag_(false), 73 initiating_drag_(false),
72 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)), 74 ALLOW_THIS_IN_INITIALIZER_LIST(cursor_timer_(this)),
73 aggregated_clicks_(0), 75 aggregated_clicks_(0),
74 last_click_time_(), 76 last_click_time_(),
75 last_click_location_(), 77 last_click_location_(),
76 ALLOW_THIS_IN_INITIALIZER_LIST(touch_selection_controller_( 78 ALLOW_THIS_IN_INITIALIZER_LIST(touch_selection_controller_(
77 TouchSelectionController::create(this))) { 79 TouchSelectionController::create(this))) {
78 set_border(text_border_); 80 set_border(text_border_);
79 81
80 // Lowercase is not supported.
81 DCHECK_NE(parent->style(), Textfield::STYLE_LOWERCASE);
82
83 #if defined(OS_CHROMEOS) 82 #if defined(OS_CHROMEOS)
84 GetRenderText()->SetFontList(gfx::FontList(l10n_util::GetStringUTF8( 83 GetRenderText()->SetFontList(gfx::FontList(l10n_util::GetStringUTF8(
85 IDS_UI_FONT_FAMILY_CROS))); 84 IDS_UI_FONT_FAMILY_CROS)));
86 #else 85 #else
87 GetRenderText()->SetFontList(gfx::FontList(textfield_->font())); 86 GetRenderText()->SetFontList(gfx::FontList(textfield_->font()));
88 #endif 87 #endif
89 // Set the default text style. 88 // Set the default text style.
90 gfx::StyleRange default_style; 89 gfx::StyleRange default_style;
91 default_style.foreground = textfield_->text_color(); 90 default_style.foreground = textfield_->text_color();
92 GetRenderText()->set_default_style(default_style); 91 GetRenderText()->set_default_style(default_style);
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
191 DCHECK(CanDrop(event.data())); 190 DCHECK(CanDrop(event.data()));
192 DCHECK(!initiating_drag_ || 191 DCHECK(!initiating_drag_ ||
193 !GetRenderText()->IsPointInSelection(event.location())); 192 !GetRenderText()->IsPointInSelection(event.location()));
194 OnBeforeUserAction(); 193 OnBeforeUserAction();
195 skip_input_method_cancel_composition_ = true; 194 skip_input_method_cancel_composition_ = true;
196 195
197 gfx::SelectionModel drop_destination_model = 196 gfx::SelectionModel drop_destination_model =
198 GetRenderText()->FindCursorPosition(event.location()); 197 GetRenderText()->FindCursorPosition(event.location());
199 string16 text; 198 string16 text;
200 event.data().GetString(&text); 199 event.data().GetString(&text);
200 MaybeLowerCase(&text);
201 201
202 // We'll delete the current selection for a drag and drop within this view. 202 // We'll delete the current selection for a drag and drop within this view.
203 bool move = initiating_drag_ && !event.IsControlDown() && 203 bool move = initiating_drag_ && !event.IsControlDown() &&
204 event.source_operations() & ui::DragDropTypes::DRAG_MOVE; 204 event.source_operations() & ui::DragDropTypes::DRAG_MOVE;
205 if (move) { 205 if (move) {
206 gfx::SelectionModel selected; 206 gfx::SelectionModel selected;
207 model_->GetSelectionModel(&selected); 207 model_->GetSelectionModel(&selected);
208 // Adjust the drop destination if it is on or after the current selection. 208 // Adjust the drop destination if it is on or after the current selection.
209 size_t drop_destination = drop_destination_model.caret_pos(); 209 size_t drop_destination = drop_destination_model.caret_pos();
210 drop_destination -= 210 drop_destination -=
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
314 } 314 }
315 315
316 ///////////////////////////////////////////////////////////////// 316 /////////////////////////////////////////////////////////////////
317 // NativeTextfieldViews, NativeTextifieldWrapper overrides: 317 // NativeTextfieldViews, NativeTextifieldWrapper overrides:
318 318
319 string16 NativeTextfieldViews::GetText() const { 319 string16 NativeTextfieldViews::GetText() const {
320 return model_->GetText(); 320 return model_->GetText();
321 } 321 }
322 322
323 void NativeTextfieldViews::UpdateText() { 323 void NativeTextfieldViews::UpdateText() {
324 model_->SetText(textfield_->text()); 324 string16 text = textfield_->text();
325 MaybeLowerCase(&text);
326 model_->SetText(text);
325 OnCaretBoundsChanged(); 327 OnCaretBoundsChanged();
326 SchedulePaint(); 328 SchedulePaint();
327 textfield_->GetWidget()->NotifyAccessibilityEvent( 329 textfield_->GetWidget()->NotifyAccessibilityEvent(
328 textfield_, ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true); 330 textfield_, ui::AccessibilityTypes::EVENT_TEXT_CHANGED, true);
329 } 331 }
330 332
331 void NativeTextfieldViews::AppendText(const string16& text) { 333 void NativeTextfieldViews::AppendText(const string16& text) {
332 if (text.empty()) 334 if (text.empty())
333 return; 335 return;
334 model_->Append(text); 336 model_->Append(textfield_->style() & Textfield::STYLE_LOWERCASE ?
337 base::i18n::ToLower(text) : text);
335 OnCaretBoundsChanged(); 338 OnCaretBoundsChanged();
336 SchedulePaint(); 339 SchedulePaint();
337 } 340 }
338 341
339 string16 NativeTextfieldViews::GetSelectedText() const { 342 string16 NativeTextfieldViews::GetSelectedText() const {
340 return model_->GetSelectedText(); 343 return model_->GetSelectedText();
341 } 344 }
342 345
343 void NativeTextfieldViews::SelectAll() { 346 void NativeTextfieldViews::SelectAll() {
344 OnBeforeUserAction(); 347 OnBeforeUserAction();
(...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after
661 OnAfterUserAction(); 664 OnAfterUserAction();
662 } 665 }
663 666
664 void NativeTextfieldViews::InsertText(const string16& text) { 667 void NativeTextfieldViews::InsertText(const string16& text) {
665 // TODO(suzhe): Filter invalid characters. 668 // TODO(suzhe): Filter invalid characters.
666 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty()) 669 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || text.empty())
667 return; 670 return;
668 671
669 OnBeforeUserAction(); 672 OnBeforeUserAction();
670 skip_input_method_cancel_composition_ = true; 673 skip_input_method_cancel_composition_ = true;
674
675 string16 new_text = text;
676 MaybeLowerCase(&new_text);
677
671 if (GetRenderText()->insert_mode()) 678 if (GetRenderText()->insert_mode())
672 model_->InsertText(text); 679 model_->InsertText(new_text);
673 else 680 else
674 model_->ReplaceText(text); 681 model_->ReplaceText(new_text);
675 skip_input_method_cancel_composition_ = false; 682 skip_input_method_cancel_composition_ = false;
676 UpdateAfterChange(true, true); 683 UpdateAfterChange(true, true);
677 OnAfterUserAction(); 684 OnAfterUserAction();
678 } 685 }
679 686
680 void NativeTextfieldViews::InsertChar(char16 ch, int flags) { 687 void NativeTextfieldViews::InsertChar(char16 ch, int flags) {
681 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE || 688 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE ||
682 !ShouldInsertChar(ch, flags)) { 689 !ShouldInsertChar(ch, flags)) {
683 return; 690 return;
684 } 691 }
685 692
686 OnBeforeUserAction(); 693 OnBeforeUserAction();
687 skip_input_method_cancel_composition_ = true; 694 skip_input_method_cancel_composition_ = true;
688 if (GetRenderText()->insert_mode()) 695 if (GetRenderText()->insert_mode())
689 model_->InsertChar(ch); 696 model_->InsertChar(ch);
690 else 697 else
691 model_->ReplaceChar(ch); 698 model_->ReplaceChar(ch);
692 skip_input_method_cancel_composition_ = false; 699 skip_input_method_cancel_composition_ = false;
700
701 if (textfield_->style() & Textfield::STYLE_LOWERCASE)
702 model_->SetText(base::i18n::ToLower(GetText()));
703
693 UpdateAfterChange(true, true); 704 UpdateAfterChange(true, true);
694 OnAfterUserAction(); 705 OnAfterUserAction();
695 } 706 }
696 707
697 ui::TextInputType NativeTextfieldViews::GetTextInputType() const { 708 ui::TextInputType NativeTextfieldViews::GetTextInputType() const {
698 return textfield_->GetTextInputType(); 709 return textfield_->GetTextInputType();
699 } 710 }
700 711
701 bool NativeTextfieldViews::CanComposeInline() const { 712 bool NativeTextfieldViews::CanComposeInline() const {
702 return true; 713 return true;
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after
1042 if (controller) 1053 if (controller)
1043 controller->OnAfterCutOrCopy(); 1054 controller->OnAfterCutOrCopy();
1044 return true; 1055 return true;
1045 } 1056 }
1046 return false; 1057 return false;
1047 } 1058 }
1048 1059
1049 bool NativeTextfieldViews::Paste() { 1060 bool NativeTextfieldViews::Paste() {
1050 const bool success = model_->Paste(); 1061 const bool success = model_->Paste();
1051 1062
1052 // Calls TextfieldController::ContentsChanged() explicitly if the paste action 1063 if (success) {
1053 // did not change the content at all. See http://crbug.com/79002 1064 string16 textfield_text = textfield_->text();
1054 if (success && GetText() == textfield_->text()) { 1065 // As Paste is handled in model_->Paste(), the RenderText may contain
msw 2012/04/12 16:15:29 Every call to MaybeLowerCase is sent to the model.
kochi 2012/04/13 09:44:09 Sorry, I'm not clear about your comment. If the co
msw 2012/04/13 18:13:25 Yeah, it's not necessarily the right approach; jus
1055 TextfieldController* controller = textfield_->GetController(); 1066 // upper case characters. This is not consistent with other places
1056 if (controller) 1067 // which keeps RenderText only containing lower case characters.
1057 controller->ContentsChanged(textfield_, textfield_->text()); 1068 if (textfield_->style() & Textfield::STYLE_LOWERCASE) {
1069 model_->SetText(base::i18n::ToLower(GetText()));
1070 textfield_text = base::i18n::ToLower(textfield_text);
1071 }
1072 // Calls TextfieldController::ContentsChanged() explicitly if the paste
1073 // action did not change the content at all. See http://crbug.com/79002
1074 if (GetText() == textfield_text) {
1075 TextfieldController* controller = textfield_->GetController();
1076 if (controller)
1077 controller->ContentsChanged(textfield_, textfield_->text());
1078 }
1058 } 1079 }
1059 return success; 1080 return success;
1060 } 1081 }
1061 1082
1062 void NativeTextfieldViews::TrackMouseClicks(const MouseEvent& event) { 1083 void NativeTextfieldViews::TrackMouseClicks(const MouseEvent& event) {
1063 if (event.IsOnlyLeftMouseButton()) { 1084 if (event.IsOnlyLeftMouseButton()) {
1064 base::TimeDelta time_delta = event.time_stamp() - last_click_time_; 1085 base::TimeDelta time_delta = event.time_stamp() - last_click_time_;
1065 if (time_delta.InMilliseconds() <= GetDoubleClickInterval() && 1086 if (time_delta.InMilliseconds() <= GetDoubleClickInterval() &&
1066 !ExceededDragThresholdFromLastClickLocation(event)) { 1087 !ExceededDragThresholdFromLastClickLocation(event)) {
1067 aggregated_clicks_ = (aggregated_clicks_ + 1) % 3; 1088 aggregated_clicks_ = (aggregated_clicks_ + 1) % 3;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
1102 } 1123 }
1103 } 1124 }
1104 1125
1105 bool NativeTextfieldViews::ImeEditingAllowed() const { 1126 bool NativeTextfieldViews::ImeEditingAllowed() const {
1106 // We don't allow the input method to retrieve or delete content from a 1127 // We don't allow the input method to retrieve or delete content from a
1107 // password field. 1128 // password field.
1108 ui::TextInputType t = GetTextInputType(); 1129 ui::TextInputType t = GetTextInputType();
1109 return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD); 1130 return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD);
1110 } 1131 }
1111 1132
1133 void NativeTextfieldViews::MaybeLowerCase(string16* text) {
sky 2012/04/12 15:28:56 I think making this return the string would make f
msw 2012/04/12 16:15:29 Ditto to Sky's comment: Each MaybeLowerCase string
kochi 2012/04/13 09:44:09 Done.
1134 if (textfield_->style() & Textfield::STYLE_LOWERCASE)
1135 *text = base::i18n::ToLower(*text);
1136 }
1137
1112 // static 1138 // static
1113 bool NativeTextfieldViews::ShouldInsertChar(char16 ch, int flags) { 1139 bool NativeTextfieldViews::ShouldInsertChar(char16 ch, int flags) {
1114 // Filter out all control characters, including tab and new line characters, 1140 // Filter out all control characters, including tab and new line characters,
1115 // and all characters with Alt modifier. But we need to allow characters with 1141 // and all characters with Alt modifier. But we need to allow characters with
1116 // AltGr modifier. 1142 // AltGr modifier.
1117 // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different 1143 // On Windows AltGr is represented by Alt+Ctrl, and on Linux it's a different
1118 // flag that we don't care about. 1144 // flag that we don't care about.
1119 return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) && 1145 return ((ch >= 0x20 && ch < 0x7F) || ch > 0x9F) &&
1120 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN; 1146 (flags & ~(ui::EF_SHIFT_DOWN | ui::EF_CAPS_LOCK_DOWN)) != ui::EF_ALT_DOWN;
1121 } 1147 }
1122 1148
1123 #if defined(USE_AURA) 1149 #if defined(USE_AURA)
1124 // static 1150 // static
1125 NativeTextfieldWrapper* NativeTextfieldWrapper::CreateWrapper( 1151 NativeTextfieldWrapper* NativeTextfieldWrapper::CreateWrapper(
1126 Textfield* field) { 1152 Textfield* field) {
1127 return new NativeTextfieldViews(field); 1153 return new NativeTextfieldViews(field);
1128 } 1154 }
1129 #endif 1155 #endif
1130 1156
1131 } // namespace views 1157 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698