From 0138e4c7f8b7a9b04b021e12c6bf08bedb42e42f Mon Sep 17 00:00:00 2001 From: Keisuke Kuroyanagi Date: Tue, 12 May 2015 12:51:26 +0900 Subject: [PATCH] Track difference between touch and word in x coordinate. After a handle is snapped to the word, the difference was tracked in offset. It can break grapheme clusters, ignores character width, and makes it difficult to select text near line breaks. Bug: 21005599 Change-Id: I42402a377670c7e3c6d6e6583744d085ae52bba2 --- core/java/android/widget/Editor.java | 88 ++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 814882a4242cd..30f373a35e774 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3855,8 +3855,8 @@ public class Editor { private class SelectionStartHandleView extends HandleView { // Indicates whether the cursor is making adjustments within a word. private boolean mInWord = false; - // Offset to track difference between touch and word boundary. - protected int mTouchWordOffset; + // Difference between touch position and word boundary position. + private float mTouchWordDelta; public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) { super(drawableLtr, drawableRtl); @@ -3908,18 +3908,36 @@ public class Editor { offset = mPreviousOffset; } } - mTouchWordOffset = Math.max(trueOffset - offset, 0); - positionCursor = true; - } else if (offset - mTouchWordOffset > mPreviousOffset || currLine > mPrevLine) { - // User is shrinking the selection. - if (currLine > mPrevLine) { - // We're on a different line, so we'll snap to word boundaries. - offset = start; - mTouchWordOffset = Math.max(trueOffset - offset, 0); + final Layout layout = mTextView.getLayout(); + if (layout != null && offset < trueOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX; } else { - offset -= mTouchWordOffset; + mTouchWordDelta = 0.0f; } positionCursor = true; + } else { + final int adjustedOffset = + mTextView.getOffsetAtCoordinate(currLine, x - mTouchWordDelta); + if (adjustedOffset > mPreviousOffset || currLine > mPrevLine) { + // User is shrinking the selection. + if (currLine > mPrevLine) { + // We're on a different line, so we'll snap to word boundaries. + offset = start; + final Layout layout = mTextView.getLayout(); + if (layout != null && offset < trueOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + mTextView.convertToLocalHorizontalCoordinate(x) - adjustedX; + } else { + mTouchWordDelta = 0.0f; + } + } else { + offset = adjustedOffset; + } + positionCursor = true; + } } // Handles can not cross and selection is at least one character. @@ -3934,7 +3952,7 @@ public class Editor { } else { offset = alteredOffset; } - mTouchWordOffset = 0; + mTouchWordDelta = 0.0f; } mInWord = !getWordIteratorWithText().isBoundary(offset); positionAtCursorOffset(offset, false); @@ -3946,7 +3964,7 @@ public class Editor { boolean superResult = super.onTouchEvent(event); if (event.getActionMasked() == MotionEvent.ACTION_UP) { // Reset the touch word offset when the user has lifted their finger. - mTouchWordOffset = 0; + mTouchWordDelta = 0.0f; } return superResult; } @@ -3955,8 +3973,8 @@ public class Editor { private class SelectionEndHandleView extends HandleView { // Indicates whether the cursor is making adjustments within a word. private boolean mInWord = false; - // Offset to track difference between touch and word boundary. - protected int mTouchWordOffset; + // Difference between touch position and word boundary position. + private float mTouchWordDelta; public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) { super(drawableLtr, drawableRtl); @@ -4008,18 +4026,36 @@ public class Editor { offset = mPreviousOffset; } } - mTouchWordOffset = Math.max(offset - trueOffset, 0); - positionCursor = true; - } else if (offset + mTouchWordOffset < mPreviousOffset || currLine < mPrevLine) { - // User is shrinking the selection. - if (currLine < mPrevLine) { - // We're on a different line, so we'll snap to word boundaries. - offset = end; - mTouchWordOffset = Math.max(offset - trueOffset, 0); + final Layout layout = mTextView.getLayout(); + if (layout != null && offset > trueOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + adjustedX - mTextView.convertToLocalHorizontalCoordinate(x); } else { - offset += mTouchWordOffset; + mTouchWordDelta = 0.0f; } positionCursor = true; + } else { + final int adjustedOffset = + mTextView.getOffsetAtCoordinate(currLine, x + mTouchWordDelta); + if (adjustedOffset < mPreviousOffset || currLine < mPrevLine) { + // User is shrinking the selection. + if (currLine < mPrevLine) { + // We're on a different line, so we'll snap to word boundaries. + offset = end; + final Layout layout = mTextView.getLayout(); + if (layout != null && offset > trueOffset) { + final float adjustedX = layout.getPrimaryHorizontal(offset); + mTouchWordDelta = + adjustedX - mTextView.convertToLocalHorizontalCoordinate(x); + } else { + mTouchWordDelta = 0.0f; + } + } else { + offset = adjustedOffset; + } + positionCursor = true; + } } if (positionCursor) { @@ -4034,7 +4070,7 @@ public class Editor { } else { offset = Math.min(alteredOffset, length); } - mTouchWordOffset = 0; + mTouchWordDelta = 0.0f; } mInWord = !getWordIteratorWithText().isBoundary(offset); positionAtCursorOffset(offset, false); @@ -4046,7 +4082,7 @@ public class Editor { boolean superResult = super.onTouchEvent(event); if (event.getActionMasked() == MotionEvent.ACTION_UP) { // Reset the touch word offset when the user has lifted their finger. - mTouchWordOffset = 0; + mTouchWordDelta = 0.0f; } return superResult; }