Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ddf00b8a authored by Gilles Debunne's avatar Gilles Debunne
Browse files

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
parent 75a03f9a
Loading
Loading
Loading
Loading
+30 −35
Original line number Diff line number Diff line
@@ -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.