Loading core/java/android/widget/Editor.java +25 −50 Original line number Diff line number Diff line Loading @@ -250,8 +250,7 @@ public class Editor { SuggestionRangeSpan mSuggestionRangeSpan; private Runnable mShowSuggestionRunnable; final Drawable[] mCursorDrawable = new Drawable[2]; int mCursorCount; // Current number of used mCursorDrawable: 0 (resource=0), 1 or 2 (split) Drawable mCursorDrawable = null; private Drawable mSelectHandleLeft; private Drawable mSelectHandleRight; Loading Loading @@ -1658,7 +1657,7 @@ public class Editor { mCorrectionHighlighter.draw(canvas, cursorOffsetVertical); } if (highlight != null && selectionStart == selectionEnd && mCursorCount > 0) { if (highlight != null && selectionStart == selectionEnd && mCursorDrawable != null) { drawCursor(canvas, cursorOffsetVertical); // Rely on the drawable entirely, do not draw the cursor line. // Has to be done after the IMM related code above which relies on the highlight. Loading Loading @@ -1849,8 +1848,8 @@ public class Editor { private void drawCursor(Canvas canvas, int cursorOffsetVertical) { final boolean translate = cursorOffsetVertical != 0; if (translate) canvas.translate(0, cursorOffsetVertical); for (int i = 0; i < mCursorCount; i++) { mCursorDrawable[i].draw(canvas); if (mCursorDrawable != null) { mCursorDrawable.draw(canvas); } if (translate) canvas.translate(0, -cursorOffsetVertical); } Loading Loading @@ -1907,32 +1906,20 @@ public class Editor { } } void updateCursorsPositions() { void updateCursorPosition() { if (mTextView.mCursorDrawableRes == 0) { mCursorCount = 0; mCursorDrawable = null; return; } Layout layout = mTextView.getLayout(); final Layout layout = mTextView.getLayout(); final int offset = mTextView.getSelectionStart(); final int line = layout.getLineForOffset(offset); final int top = layout.getLineTop(line); final int bottom = layout.getLineTop(line + 1); mCursorCount = layout.isLevelBoundary(offset) ? 2 : 1; int middle = bottom; if (mCursorCount == 2) { // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)} middle = (top + bottom) >> 1; } boolean clamped = layout.shouldClampCursor(line); updateCursorPosition(0, top, middle, layout.getPrimaryHorizontal(offset, clamped)); if (mCursorCount == 2) { updateCursorPosition(1, middle, bottom, layout.getSecondaryHorizontal(offset, clamped)); } final boolean clamped = layout.shouldClampCursor(line); updateCursorPosition(top, bottom, layout.getPrimaryHorizontal(offset, clamped)); } void refreshTextActionMode() { Loading Loading @@ -2300,19 +2287,19 @@ public class Editor { } @VisibleForTesting public Drawable[] getCursorDrawable() { @Nullable public Drawable getCursorDrawable() { return mCursorDrawable; } private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) { if (mCursorDrawable[cursorIndex] == null) { mCursorDrawable[cursorIndex] = mTextView.getContext().getDrawable( private void updateCursorPosition(int top, int bottom, float horizontal) { if (mCursorDrawable == null) { mCursorDrawable = mTextView.getContext().getDrawable( mTextView.mCursorDrawableRes); } final Drawable drawable = mCursorDrawable[cursorIndex]; final int left = clampHorizontalPosition(drawable, horizontal); final int width = drawable.getIntrinsicWidth(); drawable.setBounds(left, top - mTempRect.top, left + width, final int left = clampHorizontalPosition(mCursorDrawable, horizontal); final int width = mCursorDrawable.getIntrinsicWidth(); mCursorDrawable.setBounds(left, top - mTempRect.top, left + width, bottom + mTempRect.bottom); } Loading Loading @@ -4011,19 +3998,8 @@ public class Editor { mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mSelectionPath); mSelectionPath.computeBounds(mSelectionBounds, true); mSelectionBounds.bottom += mHandleHeight; } else if (mCursorCount == 2) { // We have a split cursor. In this case, we take the rectangle that includes both // parts of the cursor to ensure we don't obscure either of them. Rect firstCursorBounds = mCursorDrawable[0].getBounds(); Rect secondCursorBounds = mCursorDrawable[1].getBounds(); mSelectionBounds.set( Math.min(firstCursorBounds.left, secondCursorBounds.left), Math.min(firstCursorBounds.top, secondCursorBounds.top), Math.max(firstCursorBounds.right, secondCursorBounds.right), Math.max(firstCursorBounds.bottom, secondCursorBounds.bottom) + mHandleHeight); } else { // We have a single cursor. // We have a cursor. Layout layout = mTextView.getLayout(); int line = layout.getLineForOffset(mTextView.getSelectionStart()); float primaryHorizontal = clampHorizontalPosition(null, Loading Loading @@ -4407,7 +4383,7 @@ public class Editor { } /** * Return the clamped horizontal position for the first cursor. * Return the clamped horizontal position for the cursor. * * @param layout Text layout. * @param offset Character offset for the cursor. Loading Loading @@ -4643,20 +4619,19 @@ public class Editor { @Override protected int getCursorOffset() { int offset = super.getCursorOffset(); final Drawable cursor = mCursorCount > 0 ? mCursorDrawable[0] : null; if (cursor != null) { cursor.getPadding(mTempRect); offset += (cursor.getIntrinsicWidth() - mTempRect.left - mTempRect.right) / 2; if (mCursorDrawable != null) { mCursorDrawable.getPadding(mTempRect); offset += (mCursorDrawable.getIntrinsicWidth() - mTempRect.left - mTempRect.right) / 2; } return offset; } @Override int getCursorHorizontalPosition(Layout layout, int offset) { final Drawable drawable = mCursorCount > 0 ? mCursorDrawable[0] : null; if (drawable != null) { if (mCursorDrawable != null) { final float horizontal = getHorizontal(layout, offset); return clampHorizontalPosition(drawable, horizontal) + mTempRect.left; return clampHorizontalPosition(mCursorDrawable, horizontal) + mTempRect.left; } return super.getCursorHorizontalPosition(layout, offset); } Loading core/java/android/widget/TextView.java +9 −13 Original line number Diff line number Diff line Loading @@ -6271,7 +6271,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int horizontalPadding = getCompoundPaddingLeft(); final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); if (mEditor.mCursorCount == 0) { if (mEditor.mCursorDrawable == null) { synchronized (TEMP_RECTF) { /* * The reason for this concern about the thickness of the Loading @@ -6298,14 +6298,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener (int) Math.ceil(verticalPadding + TEMP_RECTF.bottom + thick)); } } else { for (int i = 0; i < mEditor.mCursorCount; i++) { Rect bounds = mEditor.mCursorDrawable[i].getBounds(); final Rect bounds = mEditor.mCursorDrawable.getBounds(); invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding, bounds.right + horizontalPadding, bounds.bottom + verticalPadding); } } } } void invalidateCursor() { int where = getSelectionEnd(); Loading Loading @@ -6352,13 +6350,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int bottom = mLayout.getLineBottom(lineEnd); // mEditor can be null in case selection is set programmatically. if (invalidateCursor && mEditor != null) { for (int i = 0; i < mEditor.mCursorCount; i++) { Rect bounds = mEditor.mCursorDrawable[i].getBounds(); if (invalidateCursor && mEditor != null && mEditor.mCursorDrawable != null) { final Rect bounds = mEditor.mCursorDrawable.getBounds(); top = Math.min(top, bounds.top); bottom = Math.max(bottom, bounds.bottom); } } final int compoundPaddingLeft = getCompoundPaddingLeft(); final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); Loading Loading @@ -6702,7 +6698,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mHighlightPath == null) mHighlightPath = new Path(); mHighlightPath.reset(); mLayout.getCursorPath(selStart, mHighlightPath, mText); mEditor.updateCursorsPositions(); mEditor.updateCursorPosition(); mHighlightPathBogus = false; } Loading core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java +4 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,9 @@ package android.widget.espresso; import static android.support.test.espresso.matcher.ViewMatchers.assertThat; import static com.android.internal.util.Preconditions.checkNotNull; import static org.hamcrest.Matchers.is; import static org.hamcrest.number.IsCloseTo.closeTo; Loading @@ -33,6 +35,7 @@ import android.widget.EditText; import android.widget.TextView; import junit.framework.AssertionFailedError; import org.hamcrest.Matcher; import java.lang.annotation.Retention; Loading Loading @@ -202,7 +205,7 @@ public final class TextViewAssertions { throw new AssertionFailedError("View should be an instance of EditText"); } EditText editText = (EditText) view; Drawable drawable = editText.getEditorForTesting().getCursorDrawable()[0]; Drawable drawable = editText.getEditorForTesting().getCursorDrawable(); Rect drawableBounds = drawable.getBounds(); Rect drawablePadding = new Rect(); drawable.getPadding(drawablePadding); Loading Loading
core/java/android/widget/Editor.java +25 −50 Original line number Diff line number Diff line Loading @@ -250,8 +250,7 @@ public class Editor { SuggestionRangeSpan mSuggestionRangeSpan; private Runnable mShowSuggestionRunnable; final Drawable[] mCursorDrawable = new Drawable[2]; int mCursorCount; // Current number of used mCursorDrawable: 0 (resource=0), 1 or 2 (split) Drawable mCursorDrawable = null; private Drawable mSelectHandleLeft; private Drawable mSelectHandleRight; Loading Loading @@ -1658,7 +1657,7 @@ public class Editor { mCorrectionHighlighter.draw(canvas, cursorOffsetVertical); } if (highlight != null && selectionStart == selectionEnd && mCursorCount > 0) { if (highlight != null && selectionStart == selectionEnd && mCursorDrawable != null) { drawCursor(canvas, cursorOffsetVertical); // Rely on the drawable entirely, do not draw the cursor line. // Has to be done after the IMM related code above which relies on the highlight. Loading Loading @@ -1849,8 +1848,8 @@ public class Editor { private void drawCursor(Canvas canvas, int cursorOffsetVertical) { final boolean translate = cursorOffsetVertical != 0; if (translate) canvas.translate(0, cursorOffsetVertical); for (int i = 0; i < mCursorCount; i++) { mCursorDrawable[i].draw(canvas); if (mCursorDrawable != null) { mCursorDrawable.draw(canvas); } if (translate) canvas.translate(0, -cursorOffsetVertical); } Loading Loading @@ -1907,32 +1906,20 @@ public class Editor { } } void updateCursorsPositions() { void updateCursorPosition() { if (mTextView.mCursorDrawableRes == 0) { mCursorCount = 0; mCursorDrawable = null; return; } Layout layout = mTextView.getLayout(); final Layout layout = mTextView.getLayout(); final int offset = mTextView.getSelectionStart(); final int line = layout.getLineForOffset(offset); final int top = layout.getLineTop(line); final int bottom = layout.getLineTop(line + 1); mCursorCount = layout.isLevelBoundary(offset) ? 2 : 1; int middle = bottom; if (mCursorCount == 2) { // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)} middle = (top + bottom) >> 1; } boolean clamped = layout.shouldClampCursor(line); updateCursorPosition(0, top, middle, layout.getPrimaryHorizontal(offset, clamped)); if (mCursorCount == 2) { updateCursorPosition(1, middle, bottom, layout.getSecondaryHorizontal(offset, clamped)); } final boolean clamped = layout.shouldClampCursor(line); updateCursorPosition(top, bottom, layout.getPrimaryHorizontal(offset, clamped)); } void refreshTextActionMode() { Loading Loading @@ -2300,19 +2287,19 @@ public class Editor { } @VisibleForTesting public Drawable[] getCursorDrawable() { @Nullable public Drawable getCursorDrawable() { return mCursorDrawable; } private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) { if (mCursorDrawable[cursorIndex] == null) { mCursorDrawable[cursorIndex] = mTextView.getContext().getDrawable( private void updateCursorPosition(int top, int bottom, float horizontal) { if (mCursorDrawable == null) { mCursorDrawable = mTextView.getContext().getDrawable( mTextView.mCursorDrawableRes); } final Drawable drawable = mCursorDrawable[cursorIndex]; final int left = clampHorizontalPosition(drawable, horizontal); final int width = drawable.getIntrinsicWidth(); drawable.setBounds(left, top - mTempRect.top, left + width, final int left = clampHorizontalPosition(mCursorDrawable, horizontal); final int width = mCursorDrawable.getIntrinsicWidth(); mCursorDrawable.setBounds(left, top - mTempRect.top, left + width, bottom + mTempRect.bottom); } Loading Loading @@ -4011,19 +3998,8 @@ public class Editor { mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mSelectionPath); mSelectionPath.computeBounds(mSelectionBounds, true); mSelectionBounds.bottom += mHandleHeight; } else if (mCursorCount == 2) { // We have a split cursor. In this case, we take the rectangle that includes both // parts of the cursor to ensure we don't obscure either of them. Rect firstCursorBounds = mCursorDrawable[0].getBounds(); Rect secondCursorBounds = mCursorDrawable[1].getBounds(); mSelectionBounds.set( Math.min(firstCursorBounds.left, secondCursorBounds.left), Math.min(firstCursorBounds.top, secondCursorBounds.top), Math.max(firstCursorBounds.right, secondCursorBounds.right), Math.max(firstCursorBounds.bottom, secondCursorBounds.bottom) + mHandleHeight); } else { // We have a single cursor. // We have a cursor. Layout layout = mTextView.getLayout(); int line = layout.getLineForOffset(mTextView.getSelectionStart()); float primaryHorizontal = clampHorizontalPosition(null, Loading Loading @@ -4407,7 +4383,7 @@ public class Editor { } /** * Return the clamped horizontal position for the first cursor. * Return the clamped horizontal position for the cursor. * * @param layout Text layout. * @param offset Character offset for the cursor. Loading Loading @@ -4643,20 +4619,19 @@ public class Editor { @Override protected int getCursorOffset() { int offset = super.getCursorOffset(); final Drawable cursor = mCursorCount > 0 ? mCursorDrawable[0] : null; if (cursor != null) { cursor.getPadding(mTempRect); offset += (cursor.getIntrinsicWidth() - mTempRect.left - mTempRect.right) / 2; if (mCursorDrawable != null) { mCursorDrawable.getPadding(mTempRect); offset += (mCursorDrawable.getIntrinsicWidth() - mTempRect.left - mTempRect.right) / 2; } return offset; } @Override int getCursorHorizontalPosition(Layout layout, int offset) { final Drawable drawable = mCursorCount > 0 ? mCursorDrawable[0] : null; if (drawable != null) { if (mCursorDrawable != null) { final float horizontal = getHorizontal(layout, offset); return clampHorizontalPosition(drawable, horizontal) + mTempRect.left; return clampHorizontalPosition(mCursorDrawable, horizontal) + mTempRect.left; } return super.getCursorHorizontalPosition(layout, offset); } Loading
core/java/android/widget/TextView.java +9 −13 Original line number Diff line number Diff line Loading @@ -6271,7 +6271,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int horizontalPadding = getCompoundPaddingLeft(); final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); if (mEditor.mCursorCount == 0) { if (mEditor.mCursorDrawable == null) { synchronized (TEMP_RECTF) { /* * The reason for this concern about the thickness of the Loading @@ -6298,14 +6298,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener (int) Math.ceil(verticalPadding + TEMP_RECTF.bottom + thick)); } } else { for (int i = 0; i < mEditor.mCursorCount; i++) { Rect bounds = mEditor.mCursorDrawable[i].getBounds(); final Rect bounds = mEditor.mCursorDrawable.getBounds(); invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding, bounds.right + horizontalPadding, bounds.bottom + verticalPadding); } } } } void invalidateCursor() { int where = getSelectionEnd(); Loading Loading @@ -6352,13 +6350,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int bottom = mLayout.getLineBottom(lineEnd); // mEditor can be null in case selection is set programmatically. if (invalidateCursor && mEditor != null) { for (int i = 0; i < mEditor.mCursorCount; i++) { Rect bounds = mEditor.mCursorDrawable[i].getBounds(); if (invalidateCursor && mEditor != null && mEditor.mCursorDrawable != null) { final Rect bounds = mEditor.mCursorDrawable.getBounds(); top = Math.min(top, bounds.top); bottom = Math.max(bottom, bounds.bottom); } } final int compoundPaddingLeft = getCompoundPaddingLeft(); final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); Loading Loading @@ -6702,7 +6698,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mHighlightPath == null) mHighlightPath = new Path(); mHighlightPath.reset(); mLayout.getCursorPath(selStart, mHighlightPath, mText); mEditor.updateCursorsPositions(); mEditor.updateCursorPosition(); mHighlightPathBogus = false; } Loading
core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java +4 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,9 @@ package android.widget.espresso; import static android.support.test.espresso.matcher.ViewMatchers.assertThat; import static com.android.internal.util.Preconditions.checkNotNull; import static org.hamcrest.Matchers.is; import static org.hamcrest.number.IsCloseTo.closeTo; Loading @@ -33,6 +35,7 @@ import android.widget.EditText; import android.widget.TextView; import junit.framework.AssertionFailedError; import org.hamcrest.Matcher; import java.lang.annotation.Retention; Loading Loading @@ -202,7 +205,7 @@ public final class TextViewAssertions { throw new AssertionFailedError("View should be an instance of EditText"); } EditText editText = (EditText) view; Drawable drawable = editText.getEditorForTesting().getCursorDrawable()[0]; Drawable drawable = editText.getEditorForTesting().getCursorDrawable(); Rect drawableBounds = drawable.getBounds(); Rect drawablePadding = new Rect(); drawable.getPadding(drawablePadding); Loading