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

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

Issue 211593006: Make Views Textfield key handling execute commands. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add kNoCommand constant; update comments. Created 6 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
« no previous file with comments | « ui/base/strings/ui_strings.grd ('k') | ui/views/controls/textfield/textfield_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/textfield.h" 5 #include "ui/views/controls/textfield/textfield.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/debug/trace_event.h" 9 #include "base/debug/trace_event.h"
10 #include "grit/ui_strings.h" 10 #include "grit/ui_strings.h"
(...skipping 29 matching lines...) Expand all
40 #include "base/win/win_util.h" 40 #include "base/win/win_util.h"
41 #endif 41 #endif
42 42
43 namespace views { 43 namespace views {
44 44
45 namespace { 45 namespace {
46 46
47 // Default placeholder text color. 47 // Default placeholder text color.
48 const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY; 48 const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY;
49 49
50 const int kNoCommand = 0;
51
50 void ConvertRectToScreen(const View* src, gfx::Rect* r) { 52 void ConvertRectToScreen(const View* src, gfx::Rect* r) {
51 DCHECK(src); 53 DCHECK(src);
52 54
53 gfx::Point new_origin = r->origin(); 55 gfx::Point new_origin = r->origin();
54 View::ConvertPointToScreen(src, &new_origin); 56 View::ConvertPointToScreen(src, &new_origin);
55 r->set_origin(new_origin); 57 r->set_origin(new_origin);
56 } 58 }
57 59
60 int GetCommandForKeyEvent(const ui::KeyEvent& event, bool has_selection) {
61 if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode())
62 return kNoCommand;
63
64 const bool shift = event.IsShiftDown();
65 const bool control = event.IsControlDown();
66 const bool alt = event.IsAltDown() || event.IsAltGrDown();
67 switch (event.key_code()) {
68 case ui::VKEY_Z:
69 if (control && !shift && !alt)
70 return IDS_APP_UNDO;
71 return (control && shift && !alt) ? IDS_APP_REDO : kNoCommand;
72 case ui::VKEY_Y:
73 return (control && !alt) ? IDS_APP_REDO : kNoCommand;
74 case ui::VKEY_A:
75 return (control && !alt) ? IDS_APP_SELECT_ALL : kNoCommand;
76 case ui::VKEY_X:
77 return (control && !alt) ? IDS_APP_CUT : kNoCommand;
78 case ui::VKEY_C:
79 return (control && !alt) ? IDS_APP_COPY : kNoCommand;
80 case ui::VKEY_V:
81 return (control && !alt) ? IDS_APP_PASTE : kNoCommand;
82 case ui::VKEY_RIGHT:
83 // Ignore alt+right, which may be a browser navigation shortcut.
84 if (alt)
85 return kNoCommand;
86 if (!shift)
87 return control ? IDS_MOVE_WORD_RIGHT : IDS_MOVE_RIGHT;
88 return control ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
89 IDS_MOVE_RIGHT_AND_MODIFY_SELECTION;
90 case ui::VKEY_LEFT:
91 // Ignore alt+left, which may be a browser navigation shortcut.
92 if (alt)
93 return kNoCommand;
94 if (!shift)
95 return control ? IDS_MOVE_WORD_LEFT : IDS_MOVE_LEFT;
96 return control ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
97 IDS_MOVE_LEFT_AND_MODIFY_SELECTION;
98 case ui::VKEY_HOME:
99 return shift ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION :
100 IDS_MOVE_TO_BEGINNING_OF_LINE;
101 case ui::VKEY_END:
102 return shift ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION :
103 IDS_MOVE_TO_END_OF_LINE;
104 case ui::VKEY_BACK:
105 if (!control || has_selection)
106 return IDS_DELETE_BACKWARD;
107 #if defined(OS_LINUX)
108 // Only erase by line break on Linux and ChromeOS.
109 if (shift)
110 return IDS_DELETE_TO_BEGINNING_OF_LINE;
111 #endif
112 return IDS_DELETE_WORD_BACKWARD;
113 case ui::VKEY_DELETE:
114 if (!control || has_selection)
115 return (shift && has_selection) ? IDS_APP_CUT : IDS_DELETE_FORWARD;
116 #if defined(OS_LINUX)
117 // Only erase by line break on Linux and ChromeOS.
118 if (shift)
119 return IDS_DELETE_TO_END_OF_LINE;
120 #endif
121 return IDS_DELETE_WORD_FORWARD;
122 case ui::VKEY_INSERT:
123 if (control && !shift)
124 return IDS_APP_COPY;
125 return (shift && !control) ? IDS_APP_PASTE : kNoCommand;
126 default:
127 return kNoCommand;
128 }
129 return kNoCommand;
130 }
131
58 } // namespace 132 } // namespace
59 133
60 // static 134 // static
61 const char Textfield::kViewClassName[] = "Textfield"; 135 const char Textfield::kViewClassName[] = "Textfield";
62 136
63 // static 137 // static
64 size_t Textfield::GetCaretBlinkMs() { 138 size_t Textfield::GetCaretBlinkMs() {
65 static const size_t default_value = 500; 139 static const size_t default_value = 500;
66 #if defined(OS_WIN) 140 #if defined(OS_WIN)
67 static const size_t system_value = ::GetCaretBlinkTime(); 141 static const size_t system_value = ::GetCaretBlinkTime();
(...skipping 349 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 UpdateSelectionClipboard(); 491 UpdateSelectionClipboard();
418 OnAfterUserAction(); 492 OnAfterUserAction();
419 } 493 }
420 494
421 bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { 495 bool Textfield::OnKeyPressed(const ui::KeyEvent& event) {
422 bool handled = controller_ && controller_->HandleKeyEvent(this, event); 496 bool handled = controller_ && controller_->HandleKeyEvent(this, event);
423 touch_selection_controller_.reset(); 497 touch_selection_controller_.reset();
424 if (handled) 498 if (handled)
425 return true; 499 return true;
426 500
427 // TODO(oshima): Refactor and consolidate with ExecuteCommand. 501 int command = GetCommandForKeyEvent(event, HasSelection());
428 if (event.type() == ui::ET_KEY_PRESSED) { 502 if (command != kNoCommand) {
oshima 2014/03/28 23:53:26 discussed offline and lgtm if this is changed to u
msw 2014/03/28 23:56:28 Done.
429 ui::KeyboardCode key_code = event.key_code(); 503 ExecuteCommand(command);
430 if (key_code == ui::VKEY_TAB || event.IsUnicodeKeyCode()) 504 return true;
431 return false;
432
433 gfx::RenderText* render_text = GetRenderText();
434 const bool editable = !read_only();
435 const bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD;
436 const bool shift = event.IsShiftDown();
437 const bool control = event.IsControlDown();
438 const bool alt = event.IsAltDown() || event.IsAltGrDown();
439 bool text_changed = false;
440 bool cursor_changed = false;
441
442 OnBeforeUserAction();
443 switch (key_code) {
444 case ui::VKEY_Z:
445 if (control && !shift && !alt && editable)
446 cursor_changed = text_changed = model_->Undo();
447 else if (control && shift && !alt && editable)
448 cursor_changed = text_changed = model_->Redo();
449 break;
450 case ui::VKEY_Y:
451 if (control && !alt && editable)
452 cursor_changed = text_changed = model_->Redo();
453 break;
454 case ui::VKEY_A:
455 if (control && !alt) {
456 model_->SelectAll(false);
457 UpdateSelectionClipboard();
458 cursor_changed = true;
459 }
460 break;
461 case ui::VKEY_X:
462 if (control && !alt && editable && readable)
463 cursor_changed = text_changed = Cut();
464 break;
465 case ui::VKEY_C:
466 if (control && !alt && readable)
467 Copy();
468 break;
469 case ui::VKEY_V:
470 if (control && !alt && editable)
471 cursor_changed = text_changed = Paste();
472 break;
473 case ui::VKEY_RIGHT:
474 case ui::VKEY_LEFT: {
475 // We should ignore the alt-left/right keys because alt key doesn't make
476 // any special effects for them and they can be shortcut keys such like
477 // forward/back of the browser history.
478 if (alt)
479 break;
480 const gfx::Range selection_range = render_text->selection();
481 model_->MoveCursor(
482 control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK,
483 (key_code == ui::VKEY_RIGHT) ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT,
484 shift);
485 UpdateSelectionClipboard();
486 cursor_changed = render_text->selection() != selection_range;
487 break;
488 }
489 case ui::VKEY_END:
490 case ui::VKEY_HOME:
491 if ((key_code == ui::VKEY_HOME) ==
492 (render_text->GetTextDirection() == base::i18n::RIGHT_TO_LEFT))
493 model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, shift);
494 else
495 model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, shift);
496 UpdateSelectionClipboard();
497 cursor_changed = true;
498 break;
499 case ui::VKEY_BACK:
500 case ui::VKEY_DELETE:
501 if (!editable)
502 break;
503 if (!model_->HasSelection()) {
504 gfx::VisualCursorDirection direction = (key_code == ui::VKEY_DELETE) ?
505 gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
506 if (shift && control) {
507 // If shift and control are pressed, erase up to the next line break
508 // on Linux and ChromeOS. Otherwise, do nothing.
509 #if defined(OS_LINUX)
510 model_->MoveCursor(gfx::LINE_BREAK, direction, true);
511 #else
512 break;
513 #endif
514 } else if (control) {
515 // If only control is pressed, then erase the previous/next word.
516 model_->MoveCursor(gfx::WORD_BREAK, direction, true);
517 }
518 }
519 if (key_code == ui::VKEY_BACK)
520 model_->Backspace();
521 else if (shift && model_->HasSelection() && readable)
522 Cut();
523 else
524 model_->Delete();
525
526 // Consume backspace and delete keys even if the edit did nothing. This
527 // prevents potential unintended side-effects of further event handling.
528 text_changed = true;
529 break;
530 case ui::VKEY_INSERT:
531 if (control && !shift && readable)
532 Copy();
533 else if (shift && !control && editable)
534 cursor_changed = text_changed = Paste();
535 break;
536 default:
537 break;
538 }
539
540 // We must have input method in order to support text input.
541 DCHECK(GetInputMethod());
542 UpdateAfterChange(text_changed, cursor_changed);
543 OnAfterUserAction();
544 return (text_changed || cursor_changed);
545 } 505 }
546 return false; 506 return false;
547 } 507 }
548 508
549 ui::TextInputClient* Textfield::GetTextInputClient() { 509 ui::TextInputClient* Textfield::GetTextInputClient() {
550 return read_only_ ? NULL : this; 510 return read_only_ ? NULL : this;
551 } 511 }
552 512
553 void Textfield::OnGestureEvent(ui::GestureEvent* event) { 513 void Textfield::OnGestureEvent(ui::GestureEvent* event) {
554 switch (event->type()) { 514 switch (event->type()) {
(...skipping 398 matching lines...) Expand 10 before | Expand all | Expand 10 after
953 return true; 913 return true;
954 } 914 }
955 915
956 bool Textfield::IsCommandIdEnabled(int command_id) const { 916 bool Textfield::IsCommandIdEnabled(int command_id) const {
957 base::string16 result; 917 base::string16 result;
958 bool editable = !read_only(); 918 bool editable = !read_only();
959 bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD; 919 bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD;
960 switch (command_id) { 920 switch (command_id) {
961 case IDS_APP_UNDO: 921 case IDS_APP_UNDO:
962 return editable && model_->CanUndo(); 922 return editable && model_->CanUndo();
923 case IDS_APP_REDO:
924 return editable && model_->CanRedo();
963 case IDS_APP_CUT: 925 case IDS_APP_CUT:
964 return editable && readable && model_->HasSelection(); 926 return editable && readable && model_->HasSelection();
965 case IDS_APP_COPY: 927 case IDS_APP_COPY:
966 return readable && model_->HasSelection(); 928 return readable && model_->HasSelection();
967 case IDS_APP_PASTE: 929 case IDS_APP_PASTE:
968 ui::Clipboard::GetForCurrentThread()->ReadText( 930 ui::Clipboard::GetForCurrentThread()->ReadText(
969 ui::CLIPBOARD_TYPE_COPY_PASTE, &result); 931 ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
970 return editable && !result.empty(); 932 return editable && !result.empty();
971 case IDS_APP_DELETE: 933 case IDS_APP_DELETE:
972 return editable && model_->HasSelection(); 934 return editable && model_->HasSelection();
973 case IDS_APP_SELECT_ALL: 935 case IDS_APP_SELECT_ALL:
974 return !text().empty(); 936 return !text().empty();
937 case IDS_DELETE_FORWARD:
938 case IDS_DELETE_BACKWARD:
939 case IDS_DELETE_TO_BEGINNING_OF_LINE:
940 case IDS_DELETE_TO_END_OF_LINE:
941 case IDS_DELETE_WORD_BACKWARD:
942 case IDS_DELETE_WORD_FORWARD:
943 return editable;
944 case IDS_MOVE_LEFT:
945 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION:
946 case IDS_MOVE_RIGHT:
947 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION:
948 case IDS_MOVE_WORD_LEFT:
949 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION:
950 case IDS_MOVE_WORD_RIGHT:
951 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION:
952 case IDS_MOVE_TO_BEGINNING_OF_LINE:
953 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION:
954 case IDS_MOVE_TO_END_OF_LINE:
955 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION:
956 return true;
975 default: 957 default:
976 return false; 958 return false;
977 } 959 }
978 } 960 }
979 961
980 bool Textfield::GetAcceleratorForCommandId(int command_id, 962 bool Textfield::GetAcceleratorForCommandId(int command_id,
981 ui::Accelerator* accelerator) { 963 ui::Accelerator* accelerator) {
982 return false; 964 return false;
983 } 965 }
984 966
985 void Textfield::ExecuteCommand(int command_id, int event_flags) { 967 void Textfield::ExecuteCommand(int command_id, int event_flags) {
986 touch_selection_controller_.reset(); 968 touch_selection_controller_.reset();
987 if (!IsCommandIdEnabled(command_id)) 969 if (!IsCommandIdEnabled(command_id))
988 return; 970 return;
989 971
990 bool text_changed = false; 972 bool text_changed = false;
973 bool cursor_changed = false;
974 bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
975 gfx::VisualCursorDirection begin = rtl ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
976 gfx::VisualCursorDirection end = rtl ? gfx::CURSOR_LEFT : gfx::CURSOR_RIGHT;
977 gfx::Range selection_range = GetSelectedRange();
978
991 OnBeforeUserAction(); 979 OnBeforeUserAction();
992 switch (command_id) { 980 switch (command_id) {
993 case IDS_APP_UNDO: 981 case IDS_APP_UNDO:
994 text_changed = model_->Undo(); 982 text_changed = cursor_changed = model_->Undo();
983 break;
984 case IDS_APP_REDO:
985 text_changed = cursor_changed = model_->Redo();
995 break; 986 break;
996 case IDS_APP_CUT: 987 case IDS_APP_CUT:
997 text_changed = Cut(); 988 text_changed = cursor_changed = Cut();
998 break; 989 break;
999 case IDS_APP_COPY: 990 case IDS_APP_COPY:
1000 Copy(); 991 Copy();
1001 break; 992 break;
1002 case IDS_APP_PASTE: 993 case IDS_APP_PASTE:
1003 text_changed = Paste(); 994 text_changed = cursor_changed = Paste();
1004 break; 995 break;
1005 case IDS_APP_DELETE: 996 case IDS_APP_DELETE:
1006 text_changed = model_->Delete(); 997 text_changed = cursor_changed = model_->Delete();
1007 break; 998 break;
1008 case IDS_APP_SELECT_ALL: 999 case IDS_APP_SELECT_ALL:
1009 SelectAll(false); 1000 SelectAll(false);
1010 break; 1001 break;
1002 case IDS_DELETE_BACKWARD:
1003 text_changed = cursor_changed = model_->Backspace();
1004 break;
1005 case IDS_DELETE_FORWARD:
1006 text_changed = cursor_changed = model_->Delete();
1007 break;
1008 case IDS_DELETE_TO_END_OF_LINE:
1009 model_->MoveCursor(gfx::LINE_BREAK, end, true);
1010 text_changed = cursor_changed = model_->Delete();
1011 break;
1012 case IDS_DELETE_TO_BEGINNING_OF_LINE:
1013 model_->MoveCursor(gfx::LINE_BREAK, begin, true);
1014 text_changed = cursor_changed = model_->Backspace();
1015 break;
1016 case IDS_DELETE_WORD_BACKWARD:
1017 model_->MoveCursor(gfx::WORD_BREAK, begin, true);
1018 text_changed = cursor_changed = model_->Backspace();
1019 break;
1020 case IDS_DELETE_WORD_FORWARD:
1021 model_->MoveCursor(gfx::WORD_BREAK, end, true);
1022 text_changed = cursor_changed = model_->Delete();
1023 break;
1024 case IDS_MOVE_LEFT:
1025 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false);
1026 break;
1027 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION:
1028 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
1029 break;
1030 case IDS_MOVE_RIGHT:
1031 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
1032 break;
1033 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION:
1034 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
1035 break;
1036 case IDS_MOVE_WORD_LEFT:
1037 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false);
1038 break;
1039 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION:
1040 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
1041 break;
1042 case IDS_MOVE_WORD_RIGHT:
1043 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false);
1044 break;
1045 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION:
1046 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
1047 break;
1048 case IDS_MOVE_TO_BEGINNING_OF_LINE:
1049 model_->MoveCursor(gfx::LINE_BREAK, begin, false);
1050 break;
1051 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION:
1052 model_->MoveCursor(gfx::LINE_BREAK, begin, true);
1053 break;
1054 case IDS_MOVE_TO_END_OF_LINE:
1055 model_->MoveCursor(gfx::LINE_BREAK, end, false);
1056 break;
1057 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION:
1058 model_->MoveCursor(gfx::LINE_BREAK, end, true);
1059 break;
1011 default: 1060 default:
1012 NOTREACHED(); 1061 NOTREACHED();
1013 break; 1062 break;
1014 } 1063 }
1015 UpdateAfterChange(text_changed, text_changed); 1064
1065 cursor_changed |= GetSelectedRange() != selection_range;
1066 if (cursor_changed)
1067 UpdateSelectionClipboard();
1068 UpdateAfterChange(text_changed, cursor_changed);
1016 OnAfterUserAction(); 1069 OnAfterUserAction();
1017 } 1070 }
1018 1071
1019 //////////////////////////////////////////////////////////////////////////////// 1072 ////////////////////////////////////////////////////////////////////////////////
1020 // Textfield, ui::TextInputClient overrides: 1073 // Textfield, ui::TextInputClient overrides:
1021 1074
1022 void Textfield::SetCompositionText(const ui::CompositionText& composition) { 1075 void Textfield::SetCompositionText(const ui::CompositionText& composition) {
1023 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) 1076 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
1024 return; 1077 return;
1025 1078
(...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after
1496 const size_t length = selection_clipboard_text.length(); 1549 const size_t length = selection_clipboard_text.length();
1497 range = gfx::Range(range.start() + length, range.end() + length); 1550 range = gfx::Range(range.start() + length, range.end() + length);
1498 } 1551 }
1499 model_->MoveCursorTo(gfx::SelectionModel(range, affinity)); 1552 model_->MoveCursorTo(gfx::SelectionModel(range, affinity));
1500 UpdateAfterChange(true, true); 1553 UpdateAfterChange(true, true);
1501 OnAfterUserAction(); 1554 OnAfterUserAction();
1502 } 1555 }
1503 } 1556 }
1504 1557
1505 } // namespace views 1558 } // namespace views
OLDNEW
« no previous file with comments | « ui/base/strings/ui_strings.grd ('k') | ui/views/controls/textfield/textfield_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698