Introduce Ctrl+Backspace/Ctrl+ForwardDelete shortcut.

Ctrl+Backspace deletes preceding one word and Ctrl+ForwardDelete delets
following one word.
Used Chrome's implementation/behvior as a reference.

Bug: 17810742
Change-Id: I7b2ca515b2f017e457f7104ec7b8e7473ca4e081
This commit is contained in:
Seigo Nonaka
2015-04-07 21:02:13 +09:00
parent d119e52050
commit f2b233d44c

View File

@@ -22,6 +22,8 @@ import android.text.*;
import android.text.method.TextKeyListener.Capitalize;
import android.widget.TextView;
import java.text.BreakIterator;
/**
* Abstract base class for key listeners.
*
@@ -63,9 +65,9 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener
private boolean backspaceOrForwardDelete(View view, Editable content, int keyCode,
KeyEvent event, boolean isForwardDelete) {
// Ensure the key event does not have modifiers except ALT or SHIFT.
// Ensure the key event does not have modifiers except ALT or SHIFT or CTRL.
if (!KeyEvent.metaStateHasNoModifiers(event.getMetaState()
& ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK))) {
& ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK))) {
return false;
}
@@ -74,18 +76,28 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener
return true;
}
// Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible.
if (getMetaState(content, META_ALT_ON, event) == 1) {
if (deleteLine(view, content)) {
return true;
// MetaKeyKeyListener doesn't track control key state. Need to check the KeyEvent instead.
boolean isCtrlActive = ((event.getMetaState() & KeyEvent.META_CTRL_ON) != 0);
boolean isShiftActive = (getMetaState(content, META_SHIFT_ON, event) == 1);
boolean isAltActive = (getMetaState(content, META_ALT_ON, event) == 1);
if (isCtrlActive) {
if (isAltActive || isShiftActive) {
// Ctrl+Alt, Ctrl+Shift, Ctrl+Alt+Shift should not delete any characters.
return false;
}
return deleteUntilWordBoundary(view, content, isForwardDelete);
}
// Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible.
if (isAltActive && deleteLine(view, content)) {
return true;
}
// Delete a character.
final int start = Selection.getSelectionEnd(content);
final int end;
if (isForwardDelete || event.isShiftPressed()
|| getMetaState(content, META_SHIFT_ON) == 1) {
if (isForwardDelete || event.isShiftPressed() || isShiftActive) {
end = TextUtils.getOffsetAfter(content, start);
} else {
end = TextUtils.getOffsetBefore(content, start);
@@ -97,6 +109,54 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener
return false;
}
private boolean deleteUntilWordBoundary(View view, Editable content, boolean isForwardDelete) {
int currentCursorOffset = Selection.getSelectionStart(content);
// If there is a selection, do nothing.
if (currentCursorOffset != Selection.getSelectionEnd(content)) {
return false;
}
// Early exit if there is no contents to delete.
if ((!isForwardDelete && currentCursorOffset == 0) ||
(isForwardDelete && currentCursorOffset == content.length())) {
return false;
}
WordIterator wordIterator = null;
if (view instanceof TextView) {
wordIterator = ((TextView)view).getWordIterator();
}
if (wordIterator == null) {
// Default locale is used for WordIterator since the appropriate locale is not clear
// here.
// TODO: Use appropriate locale for WordIterator.
wordIterator = new WordIterator();
}
int deleteFrom;
int deleteTo;
if (isForwardDelete) {
deleteFrom = currentCursorOffset;
wordIterator.setCharSequence(content, deleteFrom, content.length());
deleteTo = wordIterator.following(currentCursorOffset);
if (deleteTo == BreakIterator.DONE) {
deleteTo = content.length();
}
} else {
deleteTo = currentCursorOffset;
wordIterator.setCharSequence(content, 0, deleteTo);
deleteFrom = wordIterator.preceding(currentCursorOffset);
if (deleteFrom == BreakIterator.DONE) {
deleteFrom = 0;
}
}
content.delete(deleteFrom, deleteTo);
return true;
}
private boolean deleteSelection(View view, Editable content) {
int selectionStart = Selection.getSelectionStart(content);
int selectionEnd = Selection.getSelectionEnd(content);