Index: ui/views/controls/textfield/textfield.cc |
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc |
index f3f58593762e27544c5edf5a8c10c871a0fb5ae6..28cc6ccc2a8a611b7a977320f6e7d6e8159e55e0 100644 |
--- a/ui/views/controls/textfield/textfield.cc |
+++ b/ui/views/controls/textfield/textfield.cc |
@@ -47,6 +47,8 @@ namespace { |
// Default placeholder text color. |
const SkColor kDefaultPlaceholderTextColor = SK_ColorLTGRAY; |
+const int kNoCommand = 0; |
+ |
void ConvertRectToScreen(const View* src, gfx::Rect* r) { |
DCHECK(src); |
@@ -55,6 +57,78 @@ void ConvertRectToScreen(const View* src, gfx::Rect* r) { |
r->set_origin(new_origin); |
} |
+int GetCommandForKeyEvent(const ui::KeyEvent& event, bool has_selection) { |
+ if (event.type() != ui::ET_KEY_PRESSED || event.IsUnicodeKeyCode()) |
+ return kNoCommand; |
+ |
+ const bool shift = event.IsShiftDown(); |
+ const bool control = event.IsControlDown(); |
+ const bool alt = event.IsAltDown() || event.IsAltGrDown(); |
+ switch (event.key_code()) { |
+ case ui::VKEY_Z: |
+ if (control && !shift && !alt) |
+ return IDS_APP_UNDO; |
+ return (control && shift && !alt) ? IDS_APP_REDO : kNoCommand; |
+ case ui::VKEY_Y: |
+ return (control && !alt) ? IDS_APP_REDO : kNoCommand; |
+ case ui::VKEY_A: |
+ return (control && !alt) ? IDS_APP_SELECT_ALL : kNoCommand; |
+ case ui::VKEY_X: |
+ return (control && !alt) ? IDS_APP_CUT : kNoCommand; |
+ case ui::VKEY_C: |
+ return (control && !alt) ? IDS_APP_COPY : kNoCommand; |
+ case ui::VKEY_V: |
+ return (control && !alt) ? IDS_APP_PASTE : kNoCommand; |
+ case ui::VKEY_RIGHT: |
+ // Ignore alt+right, which may be a browser navigation shortcut. |
+ if (alt) |
+ return kNoCommand; |
+ if (!shift) |
+ return control ? IDS_MOVE_WORD_RIGHT : IDS_MOVE_RIGHT; |
+ return control ? IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION : |
+ IDS_MOVE_RIGHT_AND_MODIFY_SELECTION; |
+ case ui::VKEY_LEFT: |
+ // Ignore alt+left, which may be a browser navigation shortcut. |
+ if (alt) |
+ return kNoCommand; |
+ if (!shift) |
+ return control ? IDS_MOVE_WORD_LEFT : IDS_MOVE_LEFT; |
+ return control ? IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION : |
+ IDS_MOVE_LEFT_AND_MODIFY_SELECTION; |
+ case ui::VKEY_HOME: |
+ return shift ? IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION : |
+ IDS_MOVE_TO_BEGINNING_OF_LINE; |
+ case ui::VKEY_END: |
+ return shift ? IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION : |
+ IDS_MOVE_TO_END_OF_LINE; |
+ case ui::VKEY_BACK: |
+ if (!control || has_selection) |
+ return IDS_DELETE_BACKWARD; |
+#if defined(OS_LINUX) |
+ // Only erase by line break on Linux and ChromeOS. |
+ if (shift) |
+ return IDS_DELETE_TO_BEGINNING_OF_LINE; |
+#endif |
+ return IDS_DELETE_WORD_BACKWARD; |
+ case ui::VKEY_DELETE: |
+ if (!control || has_selection) |
+ return (shift && has_selection) ? IDS_APP_CUT : IDS_DELETE_FORWARD; |
+#if defined(OS_LINUX) |
+ // Only erase by line break on Linux and ChromeOS. |
+ if (shift) |
+ return IDS_DELETE_TO_END_OF_LINE; |
+#endif |
+ return IDS_DELETE_WORD_FORWARD; |
+ case ui::VKEY_INSERT: |
+ if (control && !shift) |
+ return IDS_APP_COPY; |
+ return (shift && !control) ? IDS_APP_PASTE : kNoCommand; |
+ default: |
+ return kNoCommand; |
+ } |
+ return kNoCommand; |
+} |
+ |
} // namespace |
// static |
@@ -419,129 +493,15 @@ void Textfield::OnMouseReleased(const ui::MouseEvent& event) { |
} |
bool Textfield::OnKeyPressed(const ui::KeyEvent& event) { |
- bool handled = controller_ && controller_->HandleKeyEvent(this, event); |
+ const bool handled = controller_ && controller_->HandleKeyEvent(this, event); |
touch_selection_controller_.reset(); |
if (handled) |
return true; |
- // TODO(oshima): Refactor and consolidate with ExecuteCommand. |
- if (event.type() == ui::ET_KEY_PRESSED) { |
- ui::KeyboardCode key_code = event.key_code(); |
- if (key_code == ui::VKEY_TAB || event.IsUnicodeKeyCode()) |
- return false; |
- |
- gfx::RenderText* render_text = GetRenderText(); |
- const bool editable = !read_only(); |
- const bool readable = text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD; |
- const bool shift = event.IsShiftDown(); |
- const bool control = event.IsControlDown(); |
- const bool alt = event.IsAltDown() || event.IsAltGrDown(); |
- bool text_changed = false; |
- bool cursor_changed = false; |
- |
- OnBeforeUserAction(); |
- switch (key_code) { |
- case ui::VKEY_Z: |
- if (control && !shift && !alt && editable) |
- cursor_changed = text_changed = model_->Undo(); |
- else if (control && shift && !alt && editable) |
- cursor_changed = text_changed = model_->Redo(); |
- break; |
- case ui::VKEY_Y: |
- if (control && !alt && editable) |
- cursor_changed = text_changed = model_->Redo(); |
- break; |
- case ui::VKEY_A: |
- if (control && !alt) { |
- model_->SelectAll(false); |
- UpdateSelectionClipboard(); |
- cursor_changed = true; |
- } |
- break; |
- case ui::VKEY_X: |
- if (control && !alt && editable && readable) |
- cursor_changed = text_changed = Cut(); |
- break; |
- case ui::VKEY_C: |
- if (control && !alt && readable) |
- Copy(); |
- break; |
- case ui::VKEY_V: |
- if (control && !alt && editable) |
- cursor_changed = text_changed = Paste(); |
- break; |
- case ui::VKEY_RIGHT: |
- case ui::VKEY_LEFT: { |
- // We should ignore the alt-left/right keys because alt key doesn't make |
- // any special effects for them and they can be shortcut keys such like |
- // forward/back of the browser history. |
- if (alt) |
- break; |
- const gfx::Range selection_range = render_text->selection(); |
- model_->MoveCursor( |
- control ? gfx::WORD_BREAK : gfx::CHARACTER_BREAK, |
- (key_code == ui::VKEY_RIGHT) ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT, |
- shift); |
- UpdateSelectionClipboard(); |
- cursor_changed = render_text->selection() != selection_range; |
- break; |
- } |
- case ui::VKEY_END: |
- case ui::VKEY_HOME: |
- if ((key_code == ui::VKEY_HOME) == |
- (render_text->GetTextDirection() == base::i18n::RIGHT_TO_LEFT)) |
- model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_RIGHT, shift); |
- else |
- model_->MoveCursor(gfx::LINE_BREAK, gfx::CURSOR_LEFT, shift); |
- UpdateSelectionClipboard(); |
- cursor_changed = true; |
- break; |
- case ui::VKEY_BACK: |
- case ui::VKEY_DELETE: |
- if (!editable) |
- break; |
- if (!model_->HasSelection()) { |
- gfx::VisualCursorDirection direction = (key_code == ui::VKEY_DELETE) ? |
- gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT; |
- if (shift && control) { |
- // If shift and control are pressed, erase up to the next line break |
- // on Linux and ChromeOS. Otherwise, do nothing. |
-#if defined(OS_LINUX) |
- model_->MoveCursor(gfx::LINE_BREAK, direction, true); |
-#else |
- break; |
-#endif |
- } else if (control) { |
- // If only control is pressed, then erase the previous/next word. |
- model_->MoveCursor(gfx::WORD_BREAK, direction, true); |
- } |
- } |
- if (key_code == ui::VKEY_BACK) |
- model_->Backspace(); |
- else if (shift && model_->HasSelection() && readable) |
- Cut(); |
- else |
- model_->Delete(); |
- |
- // Consume backspace and delete keys even if the edit did nothing. This |
- // prevents potential unintended side-effects of further event handling. |
- text_changed = true; |
- break; |
- case ui::VKEY_INSERT: |
- if (control && !shift && readable) |
- Copy(); |
- else if (shift && !control && editable) |
- cursor_changed = text_changed = Paste(); |
- break; |
- default: |
- break; |
- } |
- |
- // We must have input method in order to support text input. |
- DCHECK(GetInputMethod()); |
- UpdateAfterChange(text_changed, cursor_changed); |
- OnAfterUserAction(); |
- return (text_changed || cursor_changed); |
+ const int command = GetCommandForKeyEvent(event, HasSelection()); |
+ if (IsCommandIdEnabled(command)) { |
+ ExecuteCommand(command); |
+ return true; |
} |
return false; |
} |
@@ -960,6 +920,8 @@ bool Textfield::IsCommandIdEnabled(int command_id) const { |
switch (command_id) { |
case IDS_APP_UNDO: |
return editable && model_->CanUndo(); |
+ case IDS_APP_REDO: |
+ return editable && model_->CanRedo(); |
case IDS_APP_CUT: |
return editable && readable && model_->HasSelection(); |
case IDS_APP_COPY: |
@@ -972,6 +934,26 @@ bool Textfield::IsCommandIdEnabled(int command_id) const { |
return editable && model_->HasSelection(); |
case IDS_APP_SELECT_ALL: |
return !text().empty(); |
+ case IDS_DELETE_FORWARD: |
+ case IDS_DELETE_BACKWARD: |
+ case IDS_DELETE_TO_BEGINNING_OF_LINE: |
+ case IDS_DELETE_TO_END_OF_LINE: |
+ case IDS_DELETE_WORD_BACKWARD: |
+ case IDS_DELETE_WORD_FORWARD: |
+ return editable; |
+ case IDS_MOVE_LEFT: |
+ case IDS_MOVE_LEFT_AND_MODIFY_SELECTION: |
+ case IDS_MOVE_RIGHT: |
+ case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION: |
+ case IDS_MOVE_WORD_LEFT: |
+ case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION: |
+ case IDS_MOVE_WORD_RIGHT: |
+ case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION: |
+ case IDS_MOVE_TO_BEGINNING_OF_LINE: |
+ case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION: |
+ case IDS_MOVE_TO_END_OF_LINE: |
+ case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION: |
+ return true; |
default: |
return false; |
} |
@@ -988,31 +970,102 @@ void Textfield::ExecuteCommand(int command_id, int event_flags) { |
return; |
bool text_changed = false; |
+ bool cursor_changed = false; |
+ bool rtl = GetTextDirection() == base::i18n::RIGHT_TO_LEFT; |
+ gfx::VisualCursorDirection begin = rtl ? gfx::CURSOR_RIGHT : gfx::CURSOR_LEFT; |
+ gfx::VisualCursorDirection end = rtl ? gfx::CURSOR_LEFT : gfx::CURSOR_RIGHT; |
+ gfx::Range selection_range = GetSelectedRange(); |
+ |
OnBeforeUserAction(); |
switch (command_id) { |
case IDS_APP_UNDO: |
- text_changed = model_->Undo(); |
+ text_changed = cursor_changed = model_->Undo(); |
+ break; |
+ case IDS_APP_REDO: |
+ text_changed = cursor_changed = model_->Redo(); |
break; |
case IDS_APP_CUT: |
- text_changed = Cut(); |
+ text_changed = cursor_changed = Cut(); |
break; |
case IDS_APP_COPY: |
Copy(); |
break; |
case IDS_APP_PASTE: |
- text_changed = Paste(); |
+ text_changed = cursor_changed = Paste(); |
break; |
case IDS_APP_DELETE: |
- text_changed = model_->Delete(); |
+ text_changed = cursor_changed = model_->Delete(); |
break; |
case IDS_APP_SELECT_ALL: |
SelectAll(false); |
break; |
+ case IDS_DELETE_BACKWARD: |
+ text_changed = cursor_changed = model_->Backspace(); |
+ break; |
+ case IDS_DELETE_FORWARD: |
+ text_changed = cursor_changed = model_->Delete(); |
+ break; |
+ case IDS_DELETE_TO_END_OF_LINE: |
+ model_->MoveCursor(gfx::LINE_BREAK, end, true); |
+ text_changed = cursor_changed = model_->Delete(); |
+ break; |
+ case IDS_DELETE_TO_BEGINNING_OF_LINE: |
+ model_->MoveCursor(gfx::LINE_BREAK, begin, true); |
+ text_changed = cursor_changed = model_->Backspace(); |
+ break; |
+ case IDS_DELETE_WORD_BACKWARD: |
+ model_->MoveCursor(gfx::WORD_BREAK, begin, true); |
+ text_changed = cursor_changed = model_->Backspace(); |
+ break; |
+ case IDS_DELETE_WORD_FORWARD: |
+ model_->MoveCursor(gfx::WORD_BREAK, end, true); |
+ text_changed = cursor_changed = model_->Delete(); |
+ break; |
+ case IDS_MOVE_LEFT: |
+ model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, false); |
+ break; |
+ case IDS_MOVE_LEFT_AND_MODIFY_SELECTION: |
+ model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_LEFT, true); |
+ break; |
+ case IDS_MOVE_RIGHT: |
+ model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, false); |
+ break; |
+ case IDS_MOVE_RIGHT_AND_MODIFY_SELECTION: |
+ model_->MoveCursor(gfx::CHARACTER_BREAK, gfx::CURSOR_RIGHT, true); |
+ break; |
+ case IDS_MOVE_WORD_LEFT: |
+ model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, false); |
+ break; |
+ case IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION: |
+ model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_LEFT, true); |
+ break; |
+ case IDS_MOVE_WORD_RIGHT: |
+ model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, false); |
+ break; |
+ case IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION: |
+ model_->MoveCursor(gfx::WORD_BREAK, gfx::CURSOR_RIGHT, true); |
+ break; |
+ case IDS_MOVE_TO_BEGINNING_OF_LINE: |
+ model_->MoveCursor(gfx::LINE_BREAK, begin, false); |
+ break; |
+ case IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION: |
+ model_->MoveCursor(gfx::LINE_BREAK, begin, true); |
+ break; |
+ case IDS_MOVE_TO_END_OF_LINE: |
+ model_->MoveCursor(gfx::LINE_BREAK, end, false); |
+ break; |
+ case IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION: |
+ model_->MoveCursor(gfx::LINE_BREAK, end, true); |
+ break; |
default: |
NOTREACHED(); |
break; |
} |
- UpdateAfterChange(text_changed, text_changed); |
+ |
+ cursor_changed |= GetSelectedRange() != selection_range; |
+ if (cursor_changed) |
+ UpdateSelectionClipboard(); |
+ UpdateAfterChange(text_changed, cursor_changed); |
OnAfterUserAction(); |
} |