Text selection - Don't select vertical white space
This alters text selection so that you won't select a
whitespace line above or below your selection unless you select
a non-whitespace character past the line.
Change-Id: Ic93f77f6ccecb06be2acc83524ca9e9a627660ce
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index d93b212..253d95c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3331,7 +3331,7 @@
// Minimum touch target size for handles
private int mMinSize;
// Indicates the line of text that the handle is on.
- protected int mLine = -1;
+ protected int mPrevLine = -1;
public HandleView(Drawable drawableLtr, Drawable drawableRtl) {
super(mTextView.getContext());
@@ -3509,7 +3509,7 @@
addPositionToTouchUpFilter(offset);
}
final int line = layout.getLineForOffset(offset);
- mLine = line;
+ mPrevLine = line;
mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
getHorizontalOffset() + getCursorOffset());
@@ -3858,19 +3858,22 @@
public void updatePosition(float x, float y) {
final int trueOffset = mTextView.getOffsetForPosition(x, y);
final int currLine = mTextView.getLineAtCoordinate(y);
- int offset = trueOffset;
- boolean positionCursor = false;
+ // Don't select white space on different lines.
+ if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
+
+ boolean positionCursor = false;
+ int offset = trueOffset;
int end = getWordEnd(offset, true);
int start = getWordStart(offset);
if (offset < mPrevOffset) {
// User is increasing the selection.
- if (!mInWord || currLine < mLine) {
+ if (!mInWord || currLine < mPrevLine) {
// We're not in a word, or we're on a different line so we'll expand by
// word. First ensure the user has at least entered the next word.
int offsetToWord = Math.min((end - start) / 2, 2);
- if (offset <= end - offsetToWord || currLine < mLine) {
+ if (offset <= end - offsetToWord || currLine < mPrevLine) {
offset = start;
} else {
offset = mPrevOffset;
@@ -3882,7 +3885,7 @@
positionCursor = true;
} else if (offset - mTouchWordOffset > mPrevOffset) {
// User is shrinking the selection.
- if (currLine > mLine) {
+ if (currLine > mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
offset = end;
}
@@ -3897,7 +3900,7 @@
final int selectionEnd = mTextView.getSelectionEnd();
if (offset >= selectionEnd) {
// We can't cross the handles so let's just constrain the Y value.
- int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x);
+ int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
if (alteredOffset >= selectionEnd) {
// Can't pass the other drag handle.
offset = Math.max(0, selectionEnd - 1);
@@ -3962,6 +3965,10 @@
public void updatePosition(float x, float y) {
final int trueOffset = mTextView.getOffsetForPosition(x, y);
final int currLine = mTextView.getLineAtCoordinate(y);
+
+ // Don't select white space on different lines.
+ if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
+
int offset = trueOffset;
boolean positionCursor = false;
@@ -3970,11 +3977,11 @@
if (offset > mPrevOffset) {
// User is increasing the selection.
- if (!mInWord || currLine > mLine) {
+ if (!mInWord || currLine > mPrevLine) {
// We're not in a word, or we're on a different line so we'll expand by
// word. First ensure the user has at least entered the next word.
int midPoint = Math.min((end - start) / 2, 2);
- if (offset >= start + midPoint || currLine > mLine) {
+ if (offset >= start + midPoint || currLine > mPrevLine) {
offset = end;
} else {
offset = mPrevOffset;
@@ -3986,7 +3993,7 @@
positionCursor = true;
} else if (offset + mTouchWordOffset < mPrevOffset) {
// User is shrinking the selection.
- if (currLine > mLine) {
+ if (currLine > mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
offset = getWordStart(offset);
}
@@ -4000,7 +4007,7 @@
final int selectionStart = mTextView.getSelectionStart();
if (offset <= selectionStart) {
// We can't cross the handles so let's just constrain the Y value.
- int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x);
+ int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
int length = mTextView.getText().length();
if (alteredOffset <= selectionStart) {
// Can't pass the other drag handle.
@@ -4029,6 +4036,36 @@
}
/**
+ * Checks whether selection is happening on a different line than previous and
+ * if that line only contains whitespace up to the touch location.
+ *
+ * @param prevLine The previous line the selection was on.
+ * @param currLine The current line being selected.
+ * @param offset The offset in the text where the touch occurred.
+ * @return Whether or not it was just a white space line being selected.
+ */
+ private boolean isWhitespaceLine(int prevLine, int currLine, int offset) {
+ if (prevLine == currLine) {
+ // Same line; don't care.
+ return false;
+ }
+ CharSequence text = mTextView.getText();
+ if (offset == text.length()) {
+ // No character at the last position.
+ return false;
+ }
+ int lineEndOffset = mTextView.getLayout().getLineEnd(currLine);
+ for (int cp, i = offset; i < lineEndOffset; i += Character.charCount(cp)) {
+ cp = Character.codePointAt(text, i);
+ if (!Character.isSpaceChar(cp) && !Character.isWhitespace(cp)) {
+ // There are non white space chars on the line.
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* A CursorController instance can be used to control a cursor in the text.
*/
private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener {
@@ -4112,6 +4149,8 @@
private int mStartOffset = -1;
// Indicates whether the user is selecting text and using the drag accelerator.
private boolean mDragAcceleratorActive;
+ // Indicates the line of text the drag accelerator is on.
+ private int mPrevLine = -1;
SelectionModifierCursorController() {
resetTouchOffsets();
@@ -4209,6 +4248,8 @@
}
}
+ // New selection, reset line.
+ mPrevLine = mTextView.getLineAtCoordinate(y);
mDownPositionX = x;
mDownPositionY = y;
mGestureStayedInTapRegion = true;
@@ -4265,6 +4306,13 @@
if (my > fingerOffset) my -= fingerOffset;
offset = mTextView.getOffsetForPosition(mx, my);
+ int currLine = mTextView.getLineAtCoordinate(my);
+
+ // Don't select white space on different lines.
+ if (isWhitespaceLine(mPrevLine, currLine, offset)) return;
+
+ mPrevLine = currLine;
+
// Perform the check for closeness at edge of view, if we're very close
// don't adjust the offset to be in front of the finger - otherwise the
// user can't select words at the edge.