OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |