OSDN Git Service

Text selection: add some slop for moving between lines
authorMady Mellor <madym@google.com>
Wed, 17 Jun 2015 16:25:19 +0000 (09:25 -0700)
committerMady Mellor <madym@google.com>
Wed, 17 Jun 2015 16:25:19 +0000 (09:25 -0700)
It's pretty easy to accidentally move to a different line when
selecting text. This CL makes an adjustment so that if the user
is on a line, they must move above the top of it + slop to move
up a line, and they must move below the bottom of it + slop to move
down a line.

This CL also makes a small modification to check if the layout is
null and return early which simplifies some of the following code.

Bug: 21306433
Change-Id: I4a854e644faafaf1e159201ae1f2838862f16b25

core/java/android/widget/Editor.java

index f89ee91..2c98961 100644 (file)
@@ -122,6 +122,7 @@ public class Editor {
     static final int BLINK = 500;
     private static final float[] TEMP_POSITION = new float[2];
     private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
+    private static final float LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS = 0.5f;
     // Tag used when the Editor maintains its own separate UndoManager.
     private static final String UNDO_OWNER_TAG = "Editor";
 
@@ -4072,16 +4073,23 @@ public class Editor {
 
         @Override
         public void updatePosition(float x, float y) {
-            final int selectionEnd = mTextView.getSelectionEnd();
             final Layout layout = mTextView.getLayout();
-            int initialOffset = mTextView.getOffsetForPosition(x, y);
-            int currLine = mTextView.getLineAtCoordinate(y);
+            if (layout == null) {
+                // HandleView will deal appropriately in positionAtCursorOffset when
+                // layout is null.
+                positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false);
+                return;
+            }
+
             boolean positionCursor = false;
+            final int selectionEnd = mTextView.getSelectionEnd();
+            int currLine = getCurrentLineAdjustedForSlop(layout, mPrevLine, y);
+            int initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
 
             if (initialOffset >= selectionEnd) {
                 // Handles have crossed, bound it to the last selected line and
                 // adjust by word / char as normal.
-                currLine = layout != null ? layout.getLineForOffset(selectionEnd) : mPrevLine;
+                currLine = layout.getLineForOffset(selectionEnd);
                 initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
             }
 
@@ -4199,16 +4207,23 @@ public class Editor {
 
         @Override
         public void updatePosition(float x, float y) {
-            final int selectionStart = mTextView.getSelectionStart();
             final Layout layout = mTextView.getLayout();
-            int initialOffset = mTextView.getOffsetForPosition(x, y);
-            int currLine = mTextView.getLineAtCoordinate(y);
+            if (layout == null) {
+                // HandleView will deal appropriately in positionAtCursorOffset when
+                // layout is null.
+                positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false);
+                return;
+            }
+
             boolean positionCursor = false;
+            final int selectionStart = mTextView.getSelectionStart();
+            int currLine = getCurrentLineAdjustedForSlop(layout, mPrevLine, y);
+            int initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
 
             if (initialOffset <= selectionStart) {
                 // Handles have crossed, bound it to the first selected line and
                 // adjust by word / char as normal.
-                currLine = layout != null ? layout.getLineForOffset(selectionStart) : mPrevLine;
+                currLine = layout.getLineForOffset(selectionStart);
                 initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
             }
 
@@ -4228,7 +4243,7 @@ public class Editor {
                         offset = mPreviousOffset;
                     }
                 }
-                if (layout != null && offset > initialOffset) {
+                if (offset > initialOffset) {
                     final float adjustedX = layout.getPrimaryHorizontal(offset);
                     mTouchWordDelta =
                             adjustedX - mTextView.convertToLocalHorizontalCoordinate(x);
@@ -4244,7 +4259,7 @@ public class Editor {
                     if (currLine < mPrevLine) {
                         // We're on a different line, so we'll snap to word boundaries.
                         offset = end;
-                        if (layout != null && offset > initialOffset) {
+                        if (offset > initialOffset) {
                             final float adjustedX = layout.getPrimaryHorizontal(offset);
                             mTouchWordDelta =
                                     adjustedX - mTextView.convertToLocalHorizontalCoordinate(x);
@@ -4285,6 +4300,37 @@ public class Editor {
         }
     }
 
+    private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
+        if (layout == null || prevLine > layout.getLineCount()
+                || layout.getLineCount() <= 0 || prevLine < 0) {
+            // Invalid parameters, just return whatever line is at y.
+            return mTextView.getLineAtCoordinate(y);
+        }
+
+        final float verticalOffset = mTextView.viewportToContentVerticalOffset();
+        final int lineCount = layout.getLineCount();
+        final float slop = mTextView.getLineHeight() * LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS;
+
+        final float firstLineTop = layout.getLineTop(0) + verticalOffset;
+        final float prevLineTop = layout.getLineTop(prevLine) + verticalOffset;
+        final float yTopBound = Math.max(prevLineTop - slop, firstLineTop + slop);
+
+        final float lastLineBottom = layout.getLineBottom(lineCount - 1) + verticalOffset;
+        final float prevLineBottom = layout.getLineBottom(prevLine) + verticalOffset;
+        final float yBottomBound = Math.min(prevLineBottom + slop, lastLineBottom - slop);
+
+        // Determine if we've moved lines based on y position and previous line.
+        int currLine;
+        if (y <= yTopBound) {
+            currLine = Math.max(prevLine - 1, 0);
+        } else if (y >= yBottomBound) {
+            currLine = Math.min(prevLine + 1, lineCount - 1);
+        } else {
+            currLine = prevLine;
+        }
+        return currLine;
+    }
+
     /**
      * A CursorController instance can be used to control a cursor in the text.
      */