From ddf00b8afe43c7c2020f0d085e90f410a689e190 Mon Sep 17 00:00:00 2001 From: Gilles Debunne Date: Wed, 23 Feb 2011 17:25:13 -0800 Subject: [PATCH] Text handles movement improvements. Bug 3329346 Making sure the cursor is never hidden by the finger. Some vertical movement is not repercuted on the handles' position if it moves the finger closer to its 'ideal' touch position, where both the insertion line and the top of the handle are visible. Also removed the hysteresis line filter which is not that usefull and feels sluggy. Change-Id: I6ad0fed0cf66753c6571b3bc620b1a0f2397c7b2 --- core/java/android/widget/TextView.java | 65 ++++++++++++-------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 6791a1d22e46d..d5515b8bb1fd2 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8655,9 +8655,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private float mTouchToWindowOffsetX; private float mTouchToWindowOffsetY; private float mHotspotX; - private float mHotspotY; private int mHeight; + // Offsets the hotspot point up, so that cursor is not hidden by the finger when moving up private float mTouchOffsetY; + // Where the touch position should be on the handle to ensure a maximum cursor visibility + private float mIdealVerticalOffset; private int mLastParentX; private int mLastParentY; private float mDownPositionX, mDownPositionY; @@ -8767,7 +8769,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int handleHeight = mDrawable.getIntrinsicHeight(); mTouchOffsetY = -handleHeight * 0.3f; - mHotspotY = 0; + mIdealVerticalOffset = 0.7f * handleHeight; mHeight = handleHeight; invalidate(); } @@ -8836,7 +8838,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int[] coords = mTempCoords; hostView.getLocationInWindow(coords); final int posX = coords[0] + mPositionX + (int) mHotspotX; - final int posY = coords[1] + mPositionY + (int) mHotspotY; + final int posY = coords[1] + mPositionY; // Offset by 1 to take into account 0.5 and int rounding around getPrimaryHorizontal. return posX >= clip.left - 1 && posX <= clip.right + 1 && @@ -8901,6 +8903,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mDownPositionY = ev.getRawY(); mTouchToWindowOffsetX = mDownPositionX - mPositionX; mTouchToWindowOffsetY = mDownPositionY - mPositionY; + final int[] coords = mTempCoords; TextView.this.getLocationInWindow(coords); mLastParentX = coords[0]; @@ -8915,8 +8918,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case MotionEvent.ACTION_MOVE: { final float rawX = ev.getRawX(); final float rawY = ev.getRawY(); + + // Vertical hysteresis: vertical down movement tends to snap to ideal offset + final float previousVerticalOffset = mTouchToWindowOffsetY - mLastParentY; + final float currentVerticalOffset = rawY - mPositionY - mLastParentY; + float newVerticalOffset; + if (previousVerticalOffset < mIdealVerticalOffset) { + newVerticalOffset = Math.min(currentVerticalOffset, mIdealVerticalOffset); + newVerticalOffset = Math.max(newVerticalOffset, previousVerticalOffset); + } else { + newVerticalOffset = Math.max(currentVerticalOffset, mIdealVerticalOffset); + newVerticalOffset = Math.min(newVerticalOffset, previousVerticalOffset); + } + mTouchToWindowOffsetY = newVerticalOffset + mLastParentY; + final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX; - final float newPosY = rawY - mTouchToWindowOffsetY + mHotspotY + mTouchOffsetY; + final float newPosY = rawY - mTouchToWindowOffsetY + mTouchOffsetY; mController.updatePosition(this, Math.round(newPosX), Math.round(newPosY)); break; @@ -8954,18 +8971,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mIsDragging; } - void positionAtCursor(final int offset, boolean bottom) { + void positionAtCursor(final int offset) { addPositionToTouchUpFilter(offset); final int width = mDrawable.getIntrinsicWidth(); final int height = mDrawable.getIntrinsicHeight(); final int line = mLayout.getLineForOffset(offset); - final int lineTop = mLayout.getLineTop(line); final int lineBottom = mLayout.getLineBottom(line); final Rect bounds = sCursorControllerTempRect; bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX) + TextView.this.mScrollX; - bounds.top = (bottom ? lineBottom : lineTop - mHeight) + TextView.this.mScrollY; + bounds.top = lineBottom + TextView.this.mScrollY; bounds.right = bounds.left + width; bounds.bottom = bounds.top + height; @@ -9107,10 +9123,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void updatePosition(HandleView handle, int x, int y) { final int previousOffset = getSelectionStart(); - int offset = getHysteresisOffset(x, y, previousOffset); + final int newOffset = getOffset(x, y); - if (offset != previousOffset) { - updateOffset(handle, offset); + if (newOffset != previousOffset) { + updateOffset(handle, newOffset); removePastePopupCallback(); } hideDelayed(); @@ -9131,7 +9147,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } - getHandle().positionAtCursor(offset, true); + getHandle().positionAtCursor(offset); } public int getCurrentOffset(HandleView handle) { @@ -9215,8 +9231,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int selectionStart = getSelectionStart(); int selectionEnd = getSelectionEnd(); - final int previousOffset = handle == mStartHandle ? selectionStart : selectionEnd; - int offset = getHysteresisOffset(x, y, previousOffset); + int offset = getOffset(x, y); // Handle the case where start and end are swapped, making sure start <= end if (handle == mStartHandle) { @@ -9275,8 +9290,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // The handles have been created since the controller isShowing(). - mStartHandle.positionAtCursor(selectionStart, true); - mEndHandle.positionAtCursor(selectionEnd, true); + mStartHandle.positionAtCursor(selectionStart); + mEndHandle.positionAtCursor(selectionEnd); } public int getCurrentOffset(HandleView handle) { @@ -9406,26 +9421,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return offset; } - int getHysteresisOffset(int x, int y, int previousOffset) { - final Layout layout = getLayout(); - if (layout == null) return -1; - - int line = getLineAtCoordinate(y); - final int previousLine = layout.getLineForOffset(previousOffset); - final int previousLineTop = layout.getLineTop(previousLine); - final int previousLineBottom = layout.getLineBottom(previousLine); - final int hysteresisThreshold = (previousLineBottom - previousLineTop) / 8; - - // If new line is just before or after previous line and y position is less than - // hysteresisThreshold away from previous line, keep cursor on previous line. - if (((line == previousLine + 1) && ((y - previousLineBottom) < hysteresisThreshold)) || - ((line == previousLine - 1) && ((previousLineTop - y) < hysteresisThreshold))) { - line = previousLine; - } - - return getOffsetAtCoordinate(line, x); - } - private int convertToLocalHorizontalCoordinate(int x) { x -= getTotalPaddingLeft(); // Clamp the position to inside of the view.