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

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: Convert remaining key handling to commands, fix enabled checks. 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') | no next file » | 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 406 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 UpdateSelectionClipboard(); 417 UpdateSelectionClipboard();
418 OnAfterUserAction(); 418 OnAfterUserAction();
419 } 419 }
420 420
421 bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { 421 bool Textfield::OnKeyPressed(const ui::KeyEvent& event) {
422 bool handled = controller_ && controller_->HandleKeyEvent(this, event); 422 bool handled = controller_ && controller_->HandleKeyEvent(this, event);
423 touch_selection_controller_.reset(); 423 touch_selection_controller_.reset();
424 if (handled) 424 if (handled)
425 return true; 425 return true;
426 426
427 // TODO(oshima): Refactor and consolidate with ExecuteCommand.
428 if (event.type() == ui::ET_KEY_PRESSED) { 427 if (event.type() == ui::ET_KEY_PRESSED) {
429 ui::KeyboardCode key_code = event.key_code(); 428 ui::KeyboardCode key_code = event.key_code();
430 if (key_code == ui::VKEY_TAB || event.IsUnicodeKeyCode()) 429 if (key_code == ui::VKEY_TAB || event.IsUnicodeKeyCode())
431 return false; 430 return false;
432 431
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(); 432 const bool shift = event.IsShiftDown();
437 const bool control = event.IsControlDown(); 433 const bool control = event.IsControlDown();
438 const bool alt = event.IsAltDown() || event.IsAltGrDown(); 434 const bool alt = event.IsAltDown() || event.IsAltGrDown();
439 bool text_changed = false;
440 bool cursor_changed = false;
441 435
436 // TODO(msw): Make a helper CommandForKeyEvent or similar.
442 OnBeforeUserAction(); 437 OnBeforeUserAction();
443 switch (key_code) { 438 switch (key_code) {
444 case ui::VKEY_Z: 439 case ui::VKEY_Z:
445 if (control && !shift && !alt && editable) 440 if (control && !shift && !alt)
446 cursor_changed = text_changed = model_->Undo(); 441 ExecuteCommand(IDS_APP_UNDO);
447 else if (control && shift && !alt && editable) 442 else if (control && shift && !alt)
448 cursor_changed = text_changed = model_->Redo(); 443 ExecuteCommand(IDS_APP_REDO);
449 break; 444 break;
450 case ui::VKEY_Y: 445 case ui::VKEY_Y:
451 if (control && !alt && editable) 446 if (control && !alt)
452 cursor_changed = text_changed = model_->Redo(); 447 ExecuteCommand(IDS_APP_REDO);
453 break; 448 break;
454 case ui::VKEY_A: 449 case ui::VKEY_A:
455 if (control && !alt) { 450 if (control && !alt)
456 model_->SelectAll(false); 451 ExecuteCommand(IDS_APP_SELECT_ALL);
457 UpdateSelectionClipboard(); 452 break;
458 cursor_changed = true; 453 case ui::VKEY_X:
454 if (control && !alt)
455 ExecuteCommand(IDS_APP_CUT);
456 break;
457 case ui::VKEY_C:
458 if (control && !alt)
459 ExecuteCommand(IDS_APP_COPY);
460 break;
461 case ui::VKEY_V:
462 if (control && !alt)
463 ExecuteCommand(IDS_APP_PASTE);
464 break;
465 case ui::VKEY_RIGHT:
466 // Ignore alt+right, which may be a browser navigation shortcut.
467 if (alt)
468 break;
469 if (shift) {
470 ExecuteCommand(control ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION :
471 IDS_MOVE_RIGHT_AND_MODIFY_SELECTION);
472 } else {
473 ExecuteCommand(control ? IDS_MOVE_WORD_RIGHT : IDS_MOVE_RIGHT);
459 } 474 }
460 break; 475 break;
461 case ui::VKEY_X: 476 case ui::VKEY_LEFT:
462 if (control && !alt && editable && readable) 477 // Ignore alt+left, which may be a browser navigation shortcut.
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) 478 if (alt)
479 break; 479 break;
480 const gfx::Range selection_range = render_text->selection(); 480 if (shift) {
481 model_->MoveCursor( 481 ExecuteCommand(control ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION :
482 control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, 482 IDS_MOVE_LEFT_AND_MODIFY_SELECTION);
483 (key_code == ui::VKEY_RIGHT) ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT, 483 } else {
484 shift); 484 ExecuteCommand(control ? IDS_MOVE_WORD_LEFT : IDS_MOVE_LEFT);
485 UpdateSelectionClipboard(); 485 }
486 cursor_changed = render_text->selection() != selection_range;
487 break; 486 break;
488 } 487 case ui::VKEY_HOME:
488 ExecuteCommand(shift ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTIO N :
489 IDS_MOVE_TO_BEGINNING_OF_LINE);
490 break;
489 case ui::VKEY_END: 491 case ui::VKEY_END:
490 case ui::VKEY_HOME: 492 ExecuteCommand(shift? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION :
491 if ((key_code == ui::VKEY_HOME) == 493 IDS_MOVE_TO_END_OF_LINE);
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; 494 break;
499 case ui::VKEY_BACK: 495 case ui::VKEY_BACK:
496 if (control && !model_->HasSelection()) {
497 if (shift) {
498 #if defined(OS_LINUX)
499 // Only erase by line break on Linux and ChromeOS.
500 ExecuteCommand(IDS_DELETE_TO_BEGINNING_OF_LINE);
501 #endif
502 } else {
503 ExecuteCommand(IDS_DELETE_WORD_BACKWARD);
504 }
505 } else {
506 ExecuteCommand(IDS_DELETE_BACKWARD);
507 }
508 break;
500 case ui::VKEY_DELETE: 509 case ui::VKEY_DELETE:
501 if (!editable) 510 if (control && !model_->HasSelection()) {
502 break; 511 if (shift) {
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) 512 #if defined(OS_LINUX)
510 model_->MoveCursor(gfx::LINE_BREAK, direction, true); 513 // Only erase by line break on Linux and ChromeOS.
511 #else 514 ExecuteCommand(IDS_DELETE_TO_END_OF_LINE);
512 break;
513 #endif 515 #endif
514 } else if (control) { 516 } else {
515 // If only control is pressed, then erase the previous/next word. 517 ExecuteCommand(IDS_DELETE_WORD_FORWARD);
516 model_->MoveCursor(gfx::WORD_BREAK, direction, true);
517 } 518 }
519 } else if (shift && model_->HasSelection()) {
520 ExecuteCommand(IDS_APP_CUT);
521 } else {
522 ExecuteCommand(IDS_DELETE_FORWARD);
518 } 523 }
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; 524 break;
530 case ui::VKEY_INSERT: 525 case ui::VKEY_INSERT:
531 if (control && !shift && readable) 526 if (control && !shift)
532 Copy(); 527 ExecuteCommand(IDS_APP_COPY);
533 else if (shift && !control && editable) 528 else if (shift && !control)
534 cursor_changed = text_changed = Paste(); 529 ExecuteCommand(IDS_APP_PASTE);
535 break; 530 break;
536 default: 531 default:
537 break; 532 return false;
538 } 533 }
539 534
540 // We must have input method in order to support text input. 535 return true;
541 DCHECK(GetInputMethod());
542 UpdateAfterChange(text_changed, cursor_changed);
543 OnAfterUserAction();
544 return (text_changed || cursor_changed);
545 } 536 }
546 return false; 537 return false;
547 } 538 }
548 539
549 ui::TextInputClient* Textfield::GetTextInputClient() { 540 ui::TextInputClient* Textfield::GetTextInputClient() {
550 return read_only_ ? NULL : this; 541 return read_only_ ? NULL : this;
551 } 542 }
552 543
553 void Textfield::OnGestureEvent(ui::GestureEvent* event) { 544 void Textfield::OnGestureEvent(ui::GestureEvent* event) {
554 switch (event->type()) { 545 switch (event->type()) {
(...skipping 398 matching lines...) Expand 10 before | Expand all | Expand 10 after
953 return true; 944 return true;
954 } 945 }
955 946
956 bool Textfield::IsCommandIdEnabled(int command_id) const { 947 bool Textfield::IsCommandIdEnabled(int command_id) const {
957 base::string16 result; 948 base::string16 result;
958 bool editable = !read_only(); 949 bool editable = !read_only();
959 bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD; 950 bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD;
960 switch (command_id) { 951 switch (command_id) {
961 case IDS_APP_UNDO: 952 case IDS_APP_UNDO:
962 return editable && model_->CanUndo(); 953 return editable && model_->CanUndo();
954 case IDS_APP_REDO:
955 return editable && model_->CanRedo();
963 case IDS_APP_CUT: 956 case IDS_APP_CUT:
964 return editable && readable && model_->HasSelection(); 957 return editable && readable && model_->HasSelection();
965 case IDS_APP_COPY: 958 case IDS_APP_COPY:
966 return readable && model_->HasSelection(); 959 return readable && model_->HasSelection();
967 case IDS_APP_PASTE: 960 case IDS_APP_PASTE:
968 ui::Clipboard::GetForCurrentThread()->ReadText( 961 ui::Clipboard::GetForCurrentThread()->ReadText(
969 ui::CLIPBOARD_TYPE_COPY_PASTE, &result); 962 ui::CLIPBOARD_TYPE_COPY_PASTE, &result);
970 return editable && !result.empty(); 963 return editable && !result.empty();
971 case IDS_APP_DELETE: 964 case IDS_APP_DELETE:
972 return editable && model_->HasSelection(); 965 return editable && model_->HasSelection();
973 case IDS_APP_SELECT_ALL: 966 case IDS_APP_SELECT_ALL:
974 return !text().empty(); 967 return !text().empty();
968 case IDS_DELETE_FORWARD:
969 case IDS_DELETE_BACKWARD:
970 case IDS_DELETE_TO_BEGINNING_OF_LINE:
971 case IDS_DELETE_TO_END_OF_LINE:
972 case IDS_DELETE_WORD_BACKWARD:
973 case IDS_DELETE_WORD_FORWARD:
974 return editable;
975 case IDS_MOVE_LEFT:
976 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION:
Elliot Glaysher 2014/03/28 22:47:49 So, if we're going to have versions for the events
msw 2014/03/28 22:55:29 The separate _AND_MODIFY_SELECTION commands come f
977 case IDS_MOVE_RIGHT:
978 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION:
979 case IDS_MOVE_WORD_LEFT:
980 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION:
981 case IDS_MOVE_WORD_RIGHT:
982 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION:
983 case IDS_MOVE_TO_BEGINNING_OF_LINE:
984 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION:
985 case IDS_MOVE_TO_END_OF_LINE:
986 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION:
987 return true;
975 default: 988 default:
976 return false; 989 return false;
977 } 990 }
978 } 991 }
979 992
980 bool Textfield::GetAcceleratorForCommandId(int command_id, 993 bool Textfield::GetAcceleratorForCommandId(int command_id,
981 ui::Accelerator* accelerator) { 994 ui::Accelerator* accelerator) {
982 return false; 995 return false;
983 } 996 }
984 997
985 void Textfield::ExecuteCommand(int command_id, int event_flags) { 998 void Textfield::ExecuteCommand(int command_id, int event_flags) {
986 touch_selection_controller_.reset(); 999 touch_selection_controller_.reset();
987 if (!IsCommandIdEnabled(command_id)) 1000 if (!IsCommandIdEnabled(command_id))
988 return; 1001 return;
989 1002
990 bool text_changed = false; 1003 bool text_changed = false;
1004 bool cursor_changed = false;
1005 bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT;
1006 gfx::VisualCursorDirection begin = rtl ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT;
1007 gfx::VisualCursorDirection end = rtl ? gfx::CURSOR_LEFT : gfx::CURSOR_RIGHT;
1008 gfx::Range selection_range = GetSelectedRange();
1009
991 OnBeforeUserAction(); 1010 OnBeforeUserAction();
992 switch (command_id) { 1011 switch (command_id) {
993 case IDS_APP_UNDO: 1012 case IDS_APP_UNDO:
994 text_changed = model_->Undo(); 1013 text_changed = cursor_changed = model_->Undo();
1014 break;
1015 case IDS_APP_REDO:
1016 text_changed = cursor_changed = model_->Redo();
995 break; 1017 break;
996 case IDS_APP_CUT: 1018 case IDS_APP_CUT:
997 text_changed = Cut(); 1019 text_changed = cursor_changed = Cut();
998 break; 1020 break;
999 case IDS_APP_COPY: 1021 case IDS_APP_COPY:
1000 Copy(); 1022 Copy();
1001 break; 1023 break;
1002 case IDS_APP_PASTE: 1024 case IDS_APP_PASTE:
1003 text_changed = Paste(); 1025 text_changed = cursor_changed = Paste();
1004 break; 1026 break;
1005 case IDS_APP_DELETE: 1027 case IDS_APP_DELETE:
1006 text_changed = model_->Delete(); 1028 text_changed = cursor_changed = model_->Delete();
1007 break; 1029 break;
1008 case IDS_APP_SELECT_ALL: 1030 case IDS_APP_SELECT_ALL:
1009 SelectAll(false); 1031 SelectAll(false);
1010 break; 1032 break;
1033 case IDS_DELETE_BACKWARD:
1034 text_changed = cursor_changed = model_->Backspace();
1035 break;
1036 case IDS_DELETE_FORWARD:
1037 text_changed = cursor_changed = model_->Delete();
1038 break;
1039 case IDS_DELETE_TO_END_OF_LINE:
1040 model_->MoveCursor(gfx::LINE_BREAK, end, true);
1041 text_changed = cursor_changed = model_->Delete();
1042 break;
1043 case IDS_DELETE_TO_BEGINNING_OF_LINE:
1044 model_->MoveCursor(gfx::LINE_BREAK, begin, true);
1045 text_changed = cursor_changed = model_->Backspace();
1046 break;
1047 case IDS_DELETE_WORD_BACKWARD:
1048 model_->MoveCursor(gfx::WORD_BREAK, begin, true);
1049 text_changed = cursor_changed = model_->Backspace();
1050 break;
1051 case IDS_DELETE_WORD_FORWARD:
1052 model_->MoveCursor(gfx::WORD_BREAK, end, true);
1053 text_changed = cursor_changed = model_->Delete();
1054 break;
1055 case IDS_MOVE_LEFT:
1056 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false);
1057 break;
1058 case IDS_MOVE_LEFT_AND_MODIFY_SELECTION:
1059 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true);
1060 break;
1061 case IDS_MOVE_RIGHT:
1062 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false);
1063 break;
1064 case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION:
1065 model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true);
1066 break;
1067 case IDS_MOVE_WORD_LEFT:
1068 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false);
1069 break;
1070 case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION:
1071 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true);
1072 break;
1073 case IDS_MOVE_WORD_RIGHT:
1074 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false);
1075 break;
1076 case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION:
1077 model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true);
1078 break;
1079 case IDS_MOVE_TO_BEGINNING_OF_LINE:
1080 model_->MoveCursor(gfx::LINE_BREAK, begin, false);
1081 break;
1082 case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION:
1083 model_->MoveCursor(gfx::LINE_BREAK, begin, true);
1084 break;
1085 case IDS_MOVE_TO_END_OF_LINE:
1086 model_->MoveCursor(gfx::LINE_BREAK, end, false);
1087 break;
1088 case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION:
1089 model_->MoveCursor(gfx::LINE_BREAK, end, true);
1090 break;
1011 default: 1091 default:
1012 NOTREACHED(); 1092 NOTREACHED();
1013 break; 1093 break;
1014 } 1094 }
1015 UpdateAfterChange(text_changed, text_changed); 1095
1096 cursor_changed |= GetSelectedRange() != selection_range;
1097 if (cursor_changed)
1098 UpdateSelectionClipboard();
1099 UpdateAfterChange(text_changed, cursor_changed);
1016 OnAfterUserAction(); 1100 OnAfterUserAction();
1017 } 1101 }
1018 1102
1019 //////////////////////////////////////////////////////////////////////////////// 1103 ////////////////////////////////////////////////////////////////////////////////
1020 // Textfield, ui::TextInputClient overrides: 1104 // Textfield, ui::TextInputClient overrides:
1021 1105
1022 void Textfield::SetCompositionText(const ui::CompositionText& composition) { 1106 void Textfield::SetCompositionText(const ui::CompositionText& composition) {
1023 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) 1107 if (GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE)
1024 return; 1108 return;
1025 1109
(...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after
1496 const size_t length = selection_clipboard_text.length(); 1580 const size_t length = selection_clipboard_text.length();
1497 range = gfx::Range(range.start() + length, range.end() + length); 1581 range = gfx::Range(range.start() + length, range.end() + length);
1498 } 1582 }
1499 model_->MoveCursorTo(gfx::SelectionModel(range, affinity)); 1583 model_->MoveCursorTo(gfx::SelectionModel(range, affinity));
1500 UpdateAfterChange(true, true); 1584 UpdateAfterChange(true, true);
1501 OnAfterUserAction(); 1585 OnAfterUserAction();
1502 } 1586 }
1503 } 1587 }
1504 1588
1505 } // namespace views 1589 } // namespace views
OLDNEW
« no previous file with comments | « ui/base/strings/ui_strings.grd ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698