Loading core/java/android/text/Layout.java +25 −56 Original line number Diff line number Diff line Loading @@ -1030,24 +1030,17 @@ public abstract class Layout { * the paragraph's primary direction. */ public float getPrimaryHorizontal(int offset) { return getPrimaryHorizontal(offset, false /* not clamped */, true /* getNewLineStartPosOnLineBreak */); return getPrimaryHorizontal(offset, false /* not clamped */); } /** * Get the primary horizontal position for the specified text offset, but * optionally clamp it so that it doesn't exceed the width of the layout. * * @param offset the offset to get horizontal position * @param clamped whether to clamp the position by using the width of this layout. * @param getNewLineStartPosOnLineBreak whether to get the start position of new line when the * offset is at automatic line break. * @hide */ public float getPrimaryHorizontal(int offset, boolean clamped, boolean getNewLineStartPosOnLineBreak) { public float getPrimaryHorizontal(int offset, boolean clamped) { boolean trailing = primaryIsTrailingPrevious(offset); return getHorizontal(offset, trailing, clamped, getNewLineStartPosOnLineBreak); return getHorizontal(offset, trailing, clamped); } /** Loading @@ -1056,37 +1049,26 @@ public abstract class Layout { * the direction other than the paragraph's primary direction. */ public float getSecondaryHorizontal(int offset) { return getSecondaryHorizontal(offset, false /* not clamped */, true /* getNewLineStartPosOnLineBreak */); return getSecondaryHorizontal(offset, false /* not clamped */); } /** * Get the secondary horizontal position for the specified text offset, but * optionally clamp it so that it doesn't exceed the width of the layout. * * @param offset the offset to get horizontal position * @param clamped whether to clamp the position by using the width of this layout. * @param getNewLineStartPosOnLineBreak whether to get the start position of new line when the * offset is at automatic line break. * @hide */ public float getSecondaryHorizontal(int offset, boolean clamped, boolean getNewLineStartPosOnLineBreak) { public float getSecondaryHorizontal(int offset, boolean clamped) { boolean trailing = primaryIsTrailingPrevious(offset); return getHorizontal(offset, !trailing, clamped, getNewLineStartPosOnLineBreak); return getHorizontal(offset, !trailing, clamped); } private float getHorizontal(int offset, boolean primary, boolean getNewLineStartPosOnLineBreak) { return primary ? getPrimaryHorizontal(offset, false /* not clamped */, getNewLineStartPosOnLineBreak) : getSecondaryHorizontal(offset, false /* not clamped */, getNewLineStartPosOnLineBreak); private float getHorizontal(int offset, boolean primary) { return primary ? getPrimaryHorizontal(offset) : getSecondaryHorizontal(offset); } private float getHorizontal(int offset, boolean trailing, boolean clamped, boolean getNewLineStartPosOnLineBreak) { final int line = getLineForOffset(offset, getNewLineStartPosOnLineBreak); private float getHorizontal(int offset, boolean trailing, boolean clamped) { int line = getLineForOffset(offset); return getHorizontal(offset, trailing, line, clamped); } Loading Loading @@ -1300,10 +1282,6 @@ public abstract class Layout { * beyond the end of the text, you get the last line. */ public int getLineForOffset(int offset) { return getLineForOffset(offset, true); } private int getLineForOffset(int offset, boolean getNewLineOnLineBreak) { int high = getLineCount(), low = -1, guess; while (high - low > 1) { Loading @@ -1318,10 +1296,6 @@ public abstract class Layout { if (low < 0) { return 0; } else { if (!getNewLineOnLineBreak && low > 0 && getLineStart(low) == offset && mText.charAt(offset - 1) != '\n') { return low - 1; } return low; } } Loading Loading @@ -1357,14 +1331,14 @@ public abstract class Layout { false, null); final int max; if (line != getLineCount() - 1 && mText.charAt(lineEndOffset - 1) == '\n') { if (line == getLineCount() - 1) { max = lineEndOffset; } else { max = tl.getOffsetToLeftRightOf(lineEndOffset - lineStartOffset, !isRtlCharAt(lineEndOffset - 1)) + lineStartOffset; } else { max = lineEndOffset; } int best = lineStartOffset; float bestdist = Math.abs(getHorizontal(best, primary, true) - horiz); float bestdist = Math.abs(getHorizontal(best, primary) - horiz); for (int i = 0; i < dirs.mDirections.length; i += 2) { int here = lineStartOffset + dirs.mDirections[i]; Loading @@ -1380,9 +1354,7 @@ public abstract class Layout { guess = (high + low) / 2; int adguess = getOffsetAtStartOf(guess); if (getHorizontal(adguess, primary, adguess == lineStartOffset || adguess != lineEndOffset) * swap >= horiz * swap) { if (getHorizontal(adguess, primary) * swap >= horiz * swap) { high = guess; } else { low = guess; Loading @@ -1396,11 +1368,9 @@ public abstract class Layout { int aft = tl.getOffsetToLeftRightOf(low - lineStartOffset, isRtl) + lineStartOffset; low = tl.getOffsetToLeftRightOf(aft - lineStartOffset, !isRtl) + lineStartOffset; if (low >= here && low < there) { float dist = Math.abs(getHorizontal(low, primary, low == lineStartOffset || low != lineEndOffset) - horiz); float dist = Math.abs(getHorizontal(low, primary) - horiz); if (aft < there) { float other = Math.abs(getHorizontal(aft, primary, aft == lineStartOffset || aft != lineEndOffset) - horiz); float other = Math.abs(getHorizontal(aft, primary) - horiz); if (other < dist) { dist = other; Loading @@ -1415,8 +1385,7 @@ public abstract class Layout { } } float dist = Math.abs(getHorizontal(here, primary, here == lineStartOffset || here != lineEndOffset) - horiz); float dist = Math.abs(getHorizontal(here, primary) - horiz); if (dist < bestdist) { bestdist = dist; Loading @@ -1424,10 +1393,10 @@ public abstract class Layout { } } float dist = Math.abs(getHorizontal(max, primary, max == lineStartOffset || max != lineEndOffset) - horiz); float dist = Math.abs(getHorizontal(max, primary) - horiz); if (dist <= bestdist) { bestdist = dist; best = max; } Loading Loading @@ -1621,9 +1590,8 @@ public abstract class Layout { int bottom = getLineTop(line+1); boolean clamped = shouldClampCursor(line); float h1 = getPrimaryHorizontal(point, clamped, true) - 0.5f; float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped, true) - 0.5f : h1; float h1 = getPrimaryHorizontal(point, clamped) - 0.5f; float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped) - 0.5f : h1; int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) | TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING); Loading Loading @@ -1740,7 +1708,8 @@ public abstract class Layout { } int startline = getLineForOffset(start); int endline = getLineForOffset(end, false); int endline = getLineForOffset(end); int top = getLineTop(startline); int bottom = getLineBottom(endline); Loading core/java/android/widget/Editor.java +10 −41 Original line number Diff line number Diff line Loading @@ -1915,11 +1915,10 @@ public class Editor { } boolean clamped = layout.shouldClampCursor(line); updateCursorPosition(0, top, middle, layout.getPrimaryHorizontal(offset, clamped, true)); updateCursorPosition(0, top, middle, layout.getPrimaryHorizontal(offset, clamped)); if (mCursorCount == 2) { updateCursorPosition(1, middle, bottom, layout.getSecondaryHorizontal(offset, clamped, true)); updateCursorPosition(1, middle, bottom, layout.getSecondaryHorizontal(offset, clamped)); } } Loading Loading @@ -4340,7 +4339,7 @@ public class Editor { updateSelection(offset); addPositionToTouchUpFilter(offset); } final int line = getLineForOffset(layout, offset); final int line = layout.getLineForOffset(offset); mPrevLine = line; mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX Loading @@ -4367,15 +4366,6 @@ public class Editor { return (int) (getHorizontal(layout, offset) - 0.5f); } /** * @param layout Text layout. * @param offset Character offset for the cursor. * @return The line the cursor should be at. */ int getLineForOffset(Layout layout, int offset) { return layout.getLineForOffset(offset); } @Override public void updatePosition(int parentPositionX, int parentPositionY, boolean parentPositionChanged, boolean parentScrolled) { Loading Loading @@ -4804,7 +4794,7 @@ public class Editor { || !isStartHandle() && initialOffset <= anotherHandleOffset) { // Handles have crossed, bound it to the first selected line and // adjust by word / char as normal. currLine = getLineForOffset(layout, anotherHandleOffset, !isStartHandle()); currLine = layout.getLineForOffset(anotherHandleOffset); initialOffset = getOffsetAtCoordinate(layout, currLine, x); } Loading Loading @@ -4876,18 +4866,14 @@ public class Editor { if (isExpanding) { // User is increasing the selection. int wordBoundary = isStartHandle() ? wordStart : wordEnd; final boolean atLineBoundary = layout.getLineStart(currLine) == offset || layout.getLineEnd(currLine) == offset; final boolean atWordBoundary = getWordIteratorWithText().isBoundary(offset); final boolean snapToWord = !(atLineBoundary && atWordBoundary) && (!mInWord final boolean snapToWord = (!mInWord || (isStartHandle() ? currLine < mPrevLine : currLine > mPrevLine)) && atRtl == isAtRtlRun(layout, wordBoundary); if (snapToWord) { // Sometimes words can be broken across lines (Chinese, hyphenation). // We still snap to the word boundary but we only use the letters on the // current line to determine if the user is far enough into the word to snap. if (getLineForOffset(layout, wordBoundary) != currLine) { if (layout.getLineForOffset(wordBoundary) != currLine) { wordBoundary = isStartHandle() ? layout.getLineStart(currLine) : layout.getLineEnd(currLine); } Loading Loading @@ -5035,29 +5021,12 @@ public class Editor { } private float getHorizontal(@NonNull Layout layout, int offset, boolean startHandle) { final int line = getLineForOffset(layout, offset); final int line = layout.getLineForOffset(offset); final int offsetToCheck = startHandle ? offset : Math.max(offset - 1, 0); final boolean isRtlChar = layout.isRtlCharAt(offsetToCheck); final boolean isRtlParagraph = layout.getParagraphDirection(line) == -1; return (isRtlChar == isRtlParagraph) ? layout.getPrimaryHorizontal(offset, false, startHandle) : layout.getSecondaryHorizontal(offset, false, startHandle); } @Override public int getLineForOffset(@NonNull Layout layout, int offset) { return getLineForOffset(layout, offset, isStartHandle()); } private int getLineForOffset(@NonNull Layout layout, int offset, boolean startHandle) { final int line = layout.getLineForOffset(offset); if (!startHandle && line > 0 && layout.getLineStart(line) == offset && mTextView.getText().charAt(offset - 1) != '\n') { // If end handle is at a line break in a paragraph, the handle should be at the // previous line. return line - 1; } return line; ? layout.getPrimaryHorizontal(offset) : layout.getSecondaryHorizontal(offset); } @Override Loading core/java/android/widget/TextView.java +1 −1 Original line number Diff line number Diff line Loading @@ -8470,7 +8470,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // right where it is most likely to be annoying. final boolean clamped = grav > 0; // FIXME: Is it okay to truncate this, or should we round? final int x = (int) layout.getPrimaryHorizontal(offset, clamped, true); final int x = (int) layout.getPrimaryHorizontal(offset, clamped); final int top = layout.getLineTop(line); final int bottom = layout.getLineTop(line + 1); Loading core/tests/coretests/src/android/widget/TextViewActivityTest.java +0 −21 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import static android.widget.espresso.TextViewActions.dragHandle; import static android.widget.espresso.TextViewActions.Handle; import static android.widget.espresso.TextViewActions.longPressAndDragOnText; import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex; import static android.widget.espresso.TextViewAssertions.handleIsOnLine; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; import static android.widget.espresso.TextViewAssertions.hasSelection; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed; Loading Loading @@ -448,26 +447,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn\nopqr")); } public void testSelectionHandles_multiLine_japanese() throws Exception { final TextView textView = (TextView) getActivity().findViewById(R.id.textview); final StringBuilder builder = new StringBuilder(); for (int i = 0; i < 100; ++i) { builder.append("\u3042\u3044\u3046\u3048\u304A"); onView(withId(R.id.textview)).perform(replaceText(builder.toString())); final int lineEnd = textView.getLayout().getLineEnd(0); if (lineEnd < builder.length()) { break; } } onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(3)); final int lineEnd = textView.getLayout().getLineEnd(0); onHandleView(com.android.internal.R.id.selection_end_handle) .perform(dragHandle(textView, Handle.SELECTION_END, lineEnd, true, false)); onHandleView(com.android.internal.R.id.selection_end_handle) .check(handleIsOnLine(textView, 0)); } public void testSelectionHandles_multiLine_rtl() throws Exception { // Arabic text. final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n" Loading core/tests/coretests/src/android/widget/espresso/TextViewActions.java +26 −64 Original line number Diff line number Diff line Loading @@ -331,37 +331,15 @@ public final class TextViewActions { */ public static ViewAction dragHandle(TextView textView, Handle handleType, int endIndex, boolean primary) { return dragHandle(textView, handleType, endIndex, primary, true); } /** * Returns an action that tap then drags on the handle from the current position to endIndex on * the TextView.<br> * <br> * View constraints: * <ul> * <li>must be a TextView's drag-handle displayed on screen * <ul> * * @param textView TextView the handle is on * @param handleType Type of the handle * @param endIndex The index of the TextView's text to end the drag at * @param primary whether to use primary direction to get coordinate form index when endIndex is * at a direction boundary. * @param getNewLineStartPosOnLineBreak whether to use new line start coordinate on a line break * within a paragraph. */ public static ViewAction dragHandle(TextView textView, Handle handleType, int endIndex, boolean primary, boolean getNewLineStartPosOnLineBreak) { return actionWithAssertions( new DragAction( DragAction.Drag.TAP, new CurrentHandleCoordinates(textView), new HandleCoordinates(textView, handleType, endIndex, primary, getNewLineStartPosOnLineBreak), new HandleCoordinates(textView, handleType, endIndex, primary), Press.FINGER, Editor.HandleView.class)); } /** * A provider of the x, y coordinates of the handle dragging point. */ Loading Loading @@ -424,16 +402,13 @@ public final class TextViewActions { private final Handle mHandleType; private final int mIndex; private final boolean mPrimary; private final boolean mGetNewLineStartPosOnLineBreak; private final String mActionDescription; public HandleCoordinates(TextView textView, Handle handleType, int index, boolean primary, boolean getNewLineStartPosOnLineBreak) { public HandleCoordinates(TextView textView, Handle handleType, int index, boolean primary) { mTextView = textView; mHandleType = handleType; mIndex = index; mPrimary = primary; mGetNewLineStartPosOnLineBreak = getNewLineStartPosOnLineBreak; mActionDescription = "Could not locate " + handleType.toString() + " handle that points text index: " + index + " (" + (primary ? "primary" : "secondary" ) + ")"; Loading Loading @@ -470,10 +445,9 @@ public final class TextViewActions { final float currentX = handleView.getHorizontal(layout, currentOffset); final float currentY = layout.getLineTop(currentLine); final float[] currentCoordinates = convertToScreenCoordinates(mTextView, currentX, currentY); TextCoordinates.convertToScreenCoordinates(mTextView, currentX, currentY); final float[] targetCoordinates = (new TextCoordinates(mIndex, mPrimary, mGetNewLineStartPosOnLineBreak)) .calculateCoordinates(mTextView); (new TextCoordinates(mIndex, mPrimary)).calculateCoordinates(mTextView); final Rect bounds = new Rect(); view.getBoundsOnScreen(bounds); final Rect visibleDisplayBounds = new Rect(); Loading Loading @@ -511,27 +485,23 @@ public final class TextViewActions { private final int mIndex; private final boolean mPrimary; private final boolean mGetNewLineStartPosOnLineBreak; private final String mActionDescription; public TextCoordinates(int index) { this(index, true, true); this(index, true); } public TextCoordinates(int index, boolean primary, boolean getNewLineStartPosOnLineBreak) { public TextCoordinates(int index, boolean primary) { mIndex = index; mPrimary = primary; mGetNewLineStartPosOnLineBreak = getNewLineStartPosOnLineBreak; mActionDescription = "Could not locate text at index: " + mIndex + " (" + (primary ? "primary" : "secondary" ) + ", mGetNewLineStartPosOnLineBreak: " + mGetNewLineStartPosOnLineBreak + ")"; + " (" + (primary ? "primary" : "secondary" ) + ")"; } @Override public float[] calculateCoordinates(View view) { try { return locateTextAtIndex((TextView) view, mIndex, mPrimary, mGetNewLineStartPosOnLineBreak); return locateTextAtIndex((TextView) view, mIndex, mPrimary); } catch (ClassCastException e) { throw new PerformException.Builder() .withActionDescription(mActionDescription) Loading @@ -550,26 +520,17 @@ public final class TextViewActions { /** * @throws StringIndexOutOfBoundsException */ private float[] locateTextAtIndex(TextView textView, int index, boolean primary, boolean getNewLineStartPosOnLineBreak) { private float[] locateTextAtIndex(TextView textView, int index, boolean primary) { if (index < 0 || index > textView.getText().length()) { throw new StringIndexOutOfBoundsException(index); } final Layout layout = textView.getLayout(); int line = layout.getLineForOffset(index); if (!getNewLineStartPosOnLineBreak && line > 0 && layout.getLineStart(line) == index && textView.getText().charAt(index - 1) != '\n') { line = line - 1; } final int line = layout.getLineForOffset(index); return convertToScreenCoordinates(textView, (primary ? layout.getPrimaryHorizontal(index, false, getNewLineStartPosOnLineBreak) : layout.getSecondaryHorizontal(index, false, getNewLineStartPosOnLineBreak)), (primary ? layout.getPrimaryHorizontal(index) : layout.getSecondaryHorizontal(index)), layout.getLineTop(line)); } } /** * Convert TextView's local coordinates to on screen coordinates. Loading @@ -585,3 +546,4 @@ public final class TextViewActions { y + textView.getTotalPaddingTop() - textView.getScrollY() + xy[1] }; } } } Loading
core/java/android/text/Layout.java +25 −56 Original line number Diff line number Diff line Loading @@ -1030,24 +1030,17 @@ public abstract class Layout { * the paragraph's primary direction. */ public float getPrimaryHorizontal(int offset) { return getPrimaryHorizontal(offset, false /* not clamped */, true /* getNewLineStartPosOnLineBreak */); return getPrimaryHorizontal(offset, false /* not clamped */); } /** * Get the primary horizontal position for the specified text offset, but * optionally clamp it so that it doesn't exceed the width of the layout. * * @param offset the offset to get horizontal position * @param clamped whether to clamp the position by using the width of this layout. * @param getNewLineStartPosOnLineBreak whether to get the start position of new line when the * offset is at automatic line break. * @hide */ public float getPrimaryHorizontal(int offset, boolean clamped, boolean getNewLineStartPosOnLineBreak) { public float getPrimaryHorizontal(int offset, boolean clamped) { boolean trailing = primaryIsTrailingPrevious(offset); return getHorizontal(offset, trailing, clamped, getNewLineStartPosOnLineBreak); return getHorizontal(offset, trailing, clamped); } /** Loading @@ -1056,37 +1049,26 @@ public abstract class Layout { * the direction other than the paragraph's primary direction. */ public float getSecondaryHorizontal(int offset) { return getSecondaryHorizontal(offset, false /* not clamped */, true /* getNewLineStartPosOnLineBreak */); return getSecondaryHorizontal(offset, false /* not clamped */); } /** * Get the secondary horizontal position for the specified text offset, but * optionally clamp it so that it doesn't exceed the width of the layout. * * @param offset the offset to get horizontal position * @param clamped whether to clamp the position by using the width of this layout. * @param getNewLineStartPosOnLineBreak whether to get the start position of new line when the * offset is at automatic line break. * @hide */ public float getSecondaryHorizontal(int offset, boolean clamped, boolean getNewLineStartPosOnLineBreak) { public float getSecondaryHorizontal(int offset, boolean clamped) { boolean trailing = primaryIsTrailingPrevious(offset); return getHorizontal(offset, !trailing, clamped, getNewLineStartPosOnLineBreak); return getHorizontal(offset, !trailing, clamped); } private float getHorizontal(int offset, boolean primary, boolean getNewLineStartPosOnLineBreak) { return primary ? getPrimaryHorizontal(offset, false /* not clamped */, getNewLineStartPosOnLineBreak) : getSecondaryHorizontal(offset, false /* not clamped */, getNewLineStartPosOnLineBreak); private float getHorizontal(int offset, boolean primary) { return primary ? getPrimaryHorizontal(offset) : getSecondaryHorizontal(offset); } private float getHorizontal(int offset, boolean trailing, boolean clamped, boolean getNewLineStartPosOnLineBreak) { final int line = getLineForOffset(offset, getNewLineStartPosOnLineBreak); private float getHorizontal(int offset, boolean trailing, boolean clamped) { int line = getLineForOffset(offset); return getHorizontal(offset, trailing, line, clamped); } Loading Loading @@ -1300,10 +1282,6 @@ public abstract class Layout { * beyond the end of the text, you get the last line. */ public int getLineForOffset(int offset) { return getLineForOffset(offset, true); } private int getLineForOffset(int offset, boolean getNewLineOnLineBreak) { int high = getLineCount(), low = -1, guess; while (high - low > 1) { Loading @@ -1318,10 +1296,6 @@ public abstract class Layout { if (low < 0) { return 0; } else { if (!getNewLineOnLineBreak && low > 0 && getLineStart(low) == offset && mText.charAt(offset - 1) != '\n') { return low - 1; } return low; } } Loading Loading @@ -1357,14 +1331,14 @@ public abstract class Layout { false, null); final int max; if (line != getLineCount() - 1 && mText.charAt(lineEndOffset - 1) == '\n') { if (line == getLineCount() - 1) { max = lineEndOffset; } else { max = tl.getOffsetToLeftRightOf(lineEndOffset - lineStartOffset, !isRtlCharAt(lineEndOffset - 1)) + lineStartOffset; } else { max = lineEndOffset; } int best = lineStartOffset; float bestdist = Math.abs(getHorizontal(best, primary, true) - horiz); float bestdist = Math.abs(getHorizontal(best, primary) - horiz); for (int i = 0; i < dirs.mDirections.length; i += 2) { int here = lineStartOffset + dirs.mDirections[i]; Loading @@ -1380,9 +1354,7 @@ public abstract class Layout { guess = (high + low) / 2; int adguess = getOffsetAtStartOf(guess); if (getHorizontal(adguess, primary, adguess == lineStartOffset || adguess != lineEndOffset) * swap >= horiz * swap) { if (getHorizontal(adguess, primary) * swap >= horiz * swap) { high = guess; } else { low = guess; Loading @@ -1396,11 +1368,9 @@ public abstract class Layout { int aft = tl.getOffsetToLeftRightOf(low - lineStartOffset, isRtl) + lineStartOffset; low = tl.getOffsetToLeftRightOf(aft - lineStartOffset, !isRtl) + lineStartOffset; if (low >= here && low < there) { float dist = Math.abs(getHorizontal(low, primary, low == lineStartOffset || low != lineEndOffset) - horiz); float dist = Math.abs(getHorizontal(low, primary) - horiz); if (aft < there) { float other = Math.abs(getHorizontal(aft, primary, aft == lineStartOffset || aft != lineEndOffset) - horiz); float other = Math.abs(getHorizontal(aft, primary) - horiz); if (other < dist) { dist = other; Loading @@ -1415,8 +1385,7 @@ public abstract class Layout { } } float dist = Math.abs(getHorizontal(here, primary, here == lineStartOffset || here != lineEndOffset) - horiz); float dist = Math.abs(getHorizontal(here, primary) - horiz); if (dist < bestdist) { bestdist = dist; Loading @@ -1424,10 +1393,10 @@ public abstract class Layout { } } float dist = Math.abs(getHorizontal(max, primary, max == lineStartOffset || max != lineEndOffset) - horiz); float dist = Math.abs(getHorizontal(max, primary) - horiz); if (dist <= bestdist) { bestdist = dist; best = max; } Loading Loading @@ -1621,9 +1590,8 @@ public abstract class Layout { int bottom = getLineTop(line+1); boolean clamped = shouldClampCursor(line); float h1 = getPrimaryHorizontal(point, clamped, true) - 0.5f; float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped, true) - 0.5f : h1; float h1 = getPrimaryHorizontal(point, clamped) - 0.5f; float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point, clamped) - 0.5f : h1; int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) | TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING); Loading Loading @@ -1740,7 +1708,8 @@ public abstract class Layout { } int startline = getLineForOffset(start); int endline = getLineForOffset(end, false); int endline = getLineForOffset(end); int top = getLineTop(startline); int bottom = getLineBottom(endline); Loading
core/java/android/widget/Editor.java +10 −41 Original line number Diff line number Diff line Loading @@ -1915,11 +1915,10 @@ public class Editor { } boolean clamped = layout.shouldClampCursor(line); updateCursorPosition(0, top, middle, layout.getPrimaryHorizontal(offset, clamped, true)); updateCursorPosition(0, top, middle, layout.getPrimaryHorizontal(offset, clamped)); if (mCursorCount == 2) { updateCursorPosition(1, middle, bottom, layout.getSecondaryHorizontal(offset, clamped, true)); updateCursorPosition(1, middle, bottom, layout.getSecondaryHorizontal(offset, clamped)); } } Loading Loading @@ -4340,7 +4339,7 @@ public class Editor { updateSelection(offset); addPositionToTouchUpFilter(offset); } final int line = getLineForOffset(layout, offset); final int line = layout.getLineForOffset(offset); mPrevLine = line; mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX Loading @@ -4367,15 +4366,6 @@ public class Editor { return (int) (getHorizontal(layout, offset) - 0.5f); } /** * @param layout Text layout. * @param offset Character offset for the cursor. * @return The line the cursor should be at. */ int getLineForOffset(Layout layout, int offset) { return layout.getLineForOffset(offset); } @Override public void updatePosition(int parentPositionX, int parentPositionY, boolean parentPositionChanged, boolean parentScrolled) { Loading Loading @@ -4804,7 +4794,7 @@ public class Editor { || !isStartHandle() && initialOffset <= anotherHandleOffset) { // Handles have crossed, bound it to the first selected line and // adjust by word / char as normal. currLine = getLineForOffset(layout, anotherHandleOffset, !isStartHandle()); currLine = layout.getLineForOffset(anotherHandleOffset); initialOffset = getOffsetAtCoordinate(layout, currLine, x); } Loading Loading @@ -4876,18 +4866,14 @@ public class Editor { if (isExpanding) { // User is increasing the selection. int wordBoundary = isStartHandle() ? wordStart : wordEnd; final boolean atLineBoundary = layout.getLineStart(currLine) == offset || layout.getLineEnd(currLine) == offset; final boolean atWordBoundary = getWordIteratorWithText().isBoundary(offset); final boolean snapToWord = !(atLineBoundary && atWordBoundary) && (!mInWord final boolean snapToWord = (!mInWord || (isStartHandle() ? currLine < mPrevLine : currLine > mPrevLine)) && atRtl == isAtRtlRun(layout, wordBoundary); if (snapToWord) { // Sometimes words can be broken across lines (Chinese, hyphenation). // We still snap to the word boundary but we only use the letters on the // current line to determine if the user is far enough into the word to snap. if (getLineForOffset(layout, wordBoundary) != currLine) { if (layout.getLineForOffset(wordBoundary) != currLine) { wordBoundary = isStartHandle() ? layout.getLineStart(currLine) : layout.getLineEnd(currLine); } Loading Loading @@ -5035,29 +5021,12 @@ public class Editor { } private float getHorizontal(@NonNull Layout layout, int offset, boolean startHandle) { final int line = getLineForOffset(layout, offset); final int line = layout.getLineForOffset(offset); final int offsetToCheck = startHandle ? offset : Math.max(offset - 1, 0); final boolean isRtlChar = layout.isRtlCharAt(offsetToCheck); final boolean isRtlParagraph = layout.getParagraphDirection(line) == -1; return (isRtlChar == isRtlParagraph) ? layout.getPrimaryHorizontal(offset, false, startHandle) : layout.getSecondaryHorizontal(offset, false, startHandle); } @Override public int getLineForOffset(@NonNull Layout layout, int offset) { return getLineForOffset(layout, offset, isStartHandle()); } private int getLineForOffset(@NonNull Layout layout, int offset, boolean startHandle) { final int line = layout.getLineForOffset(offset); if (!startHandle && line > 0 && layout.getLineStart(line) == offset && mTextView.getText().charAt(offset - 1) != '\n') { // If end handle is at a line break in a paragraph, the handle should be at the // previous line. return line - 1; } return line; ? layout.getPrimaryHorizontal(offset) : layout.getSecondaryHorizontal(offset); } @Override Loading
core/java/android/widget/TextView.java +1 −1 Original line number Diff line number Diff line Loading @@ -8470,7 +8470,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // right where it is most likely to be annoying. final boolean clamped = grav > 0; // FIXME: Is it okay to truncate this, or should we round? final int x = (int) layout.getPrimaryHorizontal(offset, clamped, true); final int x = (int) layout.getPrimaryHorizontal(offset, clamped); final int top = layout.getLineTop(line); final int bottom = layout.getLineTop(line + 1); Loading
core/tests/coretests/src/android/widget/TextViewActivityTest.java +0 −21 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import static android.widget.espresso.TextViewActions.dragHandle; import static android.widget.espresso.TextViewActions.Handle; import static android.widget.espresso.TextViewActions.longPressAndDragOnText; import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex; import static android.widget.espresso.TextViewAssertions.handleIsOnLine; import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; import static android.widget.espresso.TextViewAssertions.hasSelection; import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed; Loading Loading @@ -448,26 +447,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV onView(withId(R.id.textview)).check(hasSelection("abcd\nefg\nhijk\nlmn\nopqr")); } public void testSelectionHandles_multiLine_japanese() throws Exception { final TextView textView = (TextView) getActivity().findViewById(R.id.textview); final StringBuilder builder = new StringBuilder(); for (int i = 0; i < 100; ++i) { builder.append("\u3042\u3044\u3046\u3048\u304A"); onView(withId(R.id.textview)).perform(replaceText(builder.toString())); final int lineEnd = textView.getLayout().getLineEnd(0); if (lineEnd < builder.length()) { break; } } onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(3)); final int lineEnd = textView.getLayout().getLineEnd(0); onHandleView(com.android.internal.R.id.selection_end_handle) .perform(dragHandle(textView, Handle.SELECTION_END, lineEnd, true, false)); onHandleView(com.android.internal.R.id.selection_end_handle) .check(handleIsOnLine(textView, 0)); } public void testSelectionHandles_multiLine_rtl() throws Exception { // Arabic text. final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n" Loading
core/tests/coretests/src/android/widget/espresso/TextViewActions.java +26 −64 Original line number Diff line number Diff line Loading @@ -331,37 +331,15 @@ public final class TextViewActions { */ public static ViewAction dragHandle(TextView textView, Handle handleType, int endIndex, boolean primary) { return dragHandle(textView, handleType, endIndex, primary, true); } /** * Returns an action that tap then drags on the handle from the current position to endIndex on * the TextView.<br> * <br> * View constraints: * <ul> * <li>must be a TextView's drag-handle displayed on screen * <ul> * * @param textView TextView the handle is on * @param handleType Type of the handle * @param endIndex The index of the TextView's text to end the drag at * @param primary whether to use primary direction to get coordinate form index when endIndex is * at a direction boundary. * @param getNewLineStartPosOnLineBreak whether to use new line start coordinate on a line break * within a paragraph. */ public static ViewAction dragHandle(TextView textView, Handle handleType, int endIndex, boolean primary, boolean getNewLineStartPosOnLineBreak) { return actionWithAssertions( new DragAction( DragAction.Drag.TAP, new CurrentHandleCoordinates(textView), new HandleCoordinates(textView, handleType, endIndex, primary, getNewLineStartPosOnLineBreak), new HandleCoordinates(textView, handleType, endIndex, primary), Press.FINGER, Editor.HandleView.class)); } /** * A provider of the x, y coordinates of the handle dragging point. */ Loading Loading @@ -424,16 +402,13 @@ public final class TextViewActions { private final Handle mHandleType; private final int mIndex; private final boolean mPrimary; private final boolean mGetNewLineStartPosOnLineBreak; private final String mActionDescription; public HandleCoordinates(TextView textView, Handle handleType, int index, boolean primary, boolean getNewLineStartPosOnLineBreak) { public HandleCoordinates(TextView textView, Handle handleType, int index, boolean primary) { mTextView = textView; mHandleType = handleType; mIndex = index; mPrimary = primary; mGetNewLineStartPosOnLineBreak = getNewLineStartPosOnLineBreak; mActionDescription = "Could not locate " + handleType.toString() + " handle that points text index: " + index + " (" + (primary ? "primary" : "secondary" ) + ")"; Loading Loading @@ -470,10 +445,9 @@ public final class TextViewActions { final float currentX = handleView.getHorizontal(layout, currentOffset); final float currentY = layout.getLineTop(currentLine); final float[] currentCoordinates = convertToScreenCoordinates(mTextView, currentX, currentY); TextCoordinates.convertToScreenCoordinates(mTextView, currentX, currentY); final float[] targetCoordinates = (new TextCoordinates(mIndex, mPrimary, mGetNewLineStartPosOnLineBreak)) .calculateCoordinates(mTextView); (new TextCoordinates(mIndex, mPrimary)).calculateCoordinates(mTextView); final Rect bounds = new Rect(); view.getBoundsOnScreen(bounds); final Rect visibleDisplayBounds = new Rect(); Loading Loading @@ -511,27 +485,23 @@ public final class TextViewActions { private final int mIndex; private final boolean mPrimary; private final boolean mGetNewLineStartPosOnLineBreak; private final String mActionDescription; public TextCoordinates(int index) { this(index, true, true); this(index, true); } public TextCoordinates(int index, boolean primary, boolean getNewLineStartPosOnLineBreak) { public TextCoordinates(int index, boolean primary) { mIndex = index; mPrimary = primary; mGetNewLineStartPosOnLineBreak = getNewLineStartPosOnLineBreak; mActionDescription = "Could not locate text at index: " + mIndex + " (" + (primary ? "primary" : "secondary" ) + ", mGetNewLineStartPosOnLineBreak: " + mGetNewLineStartPosOnLineBreak + ")"; + " (" + (primary ? "primary" : "secondary" ) + ")"; } @Override public float[] calculateCoordinates(View view) { try { return locateTextAtIndex((TextView) view, mIndex, mPrimary, mGetNewLineStartPosOnLineBreak); return locateTextAtIndex((TextView) view, mIndex, mPrimary); } catch (ClassCastException e) { throw new PerformException.Builder() .withActionDescription(mActionDescription) Loading @@ -550,26 +520,17 @@ public final class TextViewActions { /** * @throws StringIndexOutOfBoundsException */ private float[] locateTextAtIndex(TextView textView, int index, boolean primary, boolean getNewLineStartPosOnLineBreak) { private float[] locateTextAtIndex(TextView textView, int index, boolean primary) { if (index < 0 || index > textView.getText().length()) { throw new StringIndexOutOfBoundsException(index); } final Layout layout = textView.getLayout(); int line = layout.getLineForOffset(index); if (!getNewLineStartPosOnLineBreak && line > 0 && layout.getLineStart(line) == index && textView.getText().charAt(index - 1) != '\n') { line = line - 1; } final int line = layout.getLineForOffset(index); return convertToScreenCoordinates(textView, (primary ? layout.getPrimaryHorizontal(index, false, getNewLineStartPosOnLineBreak) : layout.getSecondaryHorizontal(index, false, getNewLineStartPosOnLineBreak)), (primary ? layout.getPrimaryHorizontal(index) : layout.getSecondaryHorizontal(index)), layout.getLineTop(line)); } } /** * Convert TextView's local coordinates to on screen coordinates. Loading @@ -585,3 +546,4 @@ public final class TextViewActions { y + textView.getTotalPaddingTop() - textView.getScrollY() + xy[1] }; } } }