diff options
| author | Shu Chen <shuchen@google.com> | 2020-08-04 09:34:35 +0800 |
|---|---|---|
| committer | Shu Chen <shuchen@google.com> | 2020-08-07 19:18:48 +0800 |
| commit | 4021c3dfd682125cdf4bf15ad5e4699cb84c8745 (patch) | |
| tree | 14ca55146621c5c4a40c38cc8473bdd1ceb8c20c /core/java | |
| parent | d551401a81742b31ac29cdf4e46645eba3fec09f (diff) | |
Implement the floating cursor via the fisheye magnifier.
- Remove the lineLeft/lineRight source bounds.
- Make the magnifier center X can be away from the cursor X.
- Let Magnifier know how to render floating cursor.
Test: locally verified.
Bug: 149883037
Change-Id: Iae3c68fa9cda0a83704fa30fa7b9f4db0a8d3314
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/widget/Editor.java | 82 | ||||
| -rw-r--r-- | core/java/android/widget/Magnifier.java | 53 |
2 files changed, 92 insertions, 43 deletions
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index c4eb39626d8b..7064697accf5 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -154,6 +154,10 @@ public class Editor { // Specifies whether to use the magnifier when pressing the insertion or selection handles. private static final boolean FLAG_USE_MAGNIFIER = true; + // Specifies how far to make the cursor start float when drag the cursor away from the + // beginning or end of the line. + private static final int CURSOR_START_FLOAT_DISTANCE_PX = 20; + private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000; private static final int RECENT_CUT_COPY_DURATION_MS = 15 * 1000; // 15 seconds in millis @@ -289,6 +293,9 @@ public class Editor { private boolean mRenderCursorRegardlessTiming; private Blink mBlink; + // Whether to let magnifier draw cursor on its surface. This is for floating cursor effect. + // And it can only be true when |mNewMagnifierEnabled| is true. + private boolean mDrawCursorOnMagnifier; boolean mCursorVisible = true; boolean mSelectAllOnFocus; boolean mTextIsSelectable; @@ -877,7 +884,7 @@ public class Editor { } boolean enabled = windowSupportsHandles && mTextView.getLayout() != null; - mInsertionControllerEnabled = enabled && isCursorVisible(); + mInsertionControllerEnabled = enabled && (mDrawCursorOnMagnifier || isCursorVisible()); mSelectionControllerEnabled = enabled && mTextView.textCanBeSelected(); if (!mInsertionControllerEnabled) { @@ -5088,26 +5095,38 @@ public class Editor { final int[] textViewLocationOnScreen = new int[2]; mTextView.getLocationOnScreen(textViewLocationOnScreen); final float touchXInView = event.getRawX() - textViewLocationOnScreen[0]; - float leftBound = mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); - float rightBound = mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); - if (sameLineSelection && ((trigger == MagnifierHandleTrigger.SELECTION_END) ^ rtl)) { - leftBound += getHorizontal(mTextView.getLayout(), otherHandleOffset); - } else { - leftBound += mTextView.getLayout().getLineLeft(lineNumber); - } - if (sameLineSelection && ((trigger == MagnifierHandleTrigger.SELECTION_START) ^ rtl)) { - rightBound += getHorizontal(mTextView.getLayout(), otherHandleOffset); + float leftBound, rightBound; + if (mNewMagnifierEnabled) { + leftBound = 0; + rightBound = mTextView.getWidth(); + if (touchXInView < leftBound || touchXInView > rightBound) { + // The touch is too far from the current line / selection, so hide the magnifier. + return false; + } } else { - rightBound += mTextView.getLayout().getLineRight(lineNumber); - } - leftBound *= mTextViewScaleX; - rightBound *= mTextViewScaleX; - final float contentWidth = Math.round(mMagnifierAnimator.mMagnifier.getWidth() - / mMagnifierAnimator.mMagnifier.getZoom()); - if (touchXInView < leftBound - contentWidth / 2 - || touchXInView > rightBound + contentWidth / 2) { - // The touch is too far from the current line / selection, so hide the magnifier. - return false; + leftBound = mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); + rightBound = mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); + if (sameLineSelection && ((trigger == MagnifierHandleTrigger.SELECTION_END) + ^ rtl)) { + leftBound += getHorizontal(mTextView.getLayout(), otherHandleOffset); + } else { + leftBound += mTextView.getLayout().getLineLeft(lineNumber); + } + if (sameLineSelection && ((trigger == MagnifierHandleTrigger.SELECTION_START) + ^ rtl)) { + rightBound += getHorizontal(mTextView.getLayout(), otherHandleOffset); + } else { + rightBound += mTextView.getLayout().getLineRight(lineNumber); + } + leftBound *= mTextViewScaleX; + rightBound *= mTextViewScaleX; + final float contentWidth = Math.round(mMagnifierAnimator.mMagnifier.getWidth() + / mMagnifierAnimator.mMagnifier.getZoom()); + if (touchXInView < leftBound - contentWidth / 2 + || touchXInView > rightBound + contentWidth / 2) { + // The touch is too far from the current line / selection, so hide the magnifier. + return false; + } } final float scaledTouchXInView; @@ -5165,7 +5184,8 @@ public class Editor { final Rect magnifierRect = new Rect(magnifierTopLeft.x, magnifierTopLeft.y, magnifierTopLeft.x + mMagnifierAnimator.mMagnifier.getWidth(), magnifierTopLeft.y + mMagnifierAnimator.mMagnifier.getHeight()); - setVisible(!handleOverlapsMagnifier(HandleView.this, magnifierRect)); + setVisible(!handleOverlapsMagnifier(HandleView.this, magnifierRect) + && !mDrawCursorOnMagnifier); final HandleView otherHandle = getOtherSelectionHandle(); if (otherHandle != null) { otherHandle.setVisible(!handleOverlapsMagnifier(otherHandle, magnifierRect)); @@ -5195,7 +5215,20 @@ public class Editor { lineLeft += mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); int lineRight = (int) layout.getLineRight(line); lineRight += mTextView.getTotalPaddingLeft() - mTextView.getScrollX(); - mMagnifierAnimator.mMagnifier.setSourceHorizontalBounds(lineLeft, lineRight); + mDrawCursorOnMagnifier = + showPosInView.x < lineLeft - CURSOR_START_FLOAT_DISTANCE_PX + || showPosInView.x > lineRight + CURSOR_START_FLOAT_DISTANCE_PX; + mMagnifierAnimator.mMagnifier.setDrawCursor( + mDrawCursorOnMagnifier, mDrawableForCursor); + boolean cursorVisible = mCursorVisible; + // Updates cursor visibility, so that the real cursor and the float cursor on + // magnifier surface won't appear at the same time. + mCursorVisible = !mDrawCursorOnMagnifier; + if (mCursorVisible && !cursorVisible) { + // When the real cursor is a drawable, hiding/showing it would change its + // bounds. So, call updateCursorPosition() to correct its position. + updateCursorPosition(); + } final int lineHeight = layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line); float zoom = mInitialZoom; @@ -5217,6 +5250,11 @@ public class Editor { if (mMagnifierAnimator != null) { mMagnifierAnimator.dismiss(); mRenderCursorRegardlessTiming = false; + mDrawCursorOnMagnifier = false; + if (!mCursorVisible) { + mCursorVisible = true; + mTextView.invalidate(); + } resumeBlink(); setVisible(true); final HandleView otherHandle = getOtherSelectionHandle(); diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 89206fda39f3..c72eed45e794 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -149,9 +149,6 @@ public final class Magnifier { private int mLeftCutWidth = 0; // The width of the cut region on the right edge of the pixel copy source rect. private int mRightCutWidth = 0; - // The horizontal bounds of the content source in pixels, relative to the view. - private int mLeftBound = Integer.MIN_VALUE; - private int mRightBound = Integer.MAX_VALUE; // The width of the ramp region in pixels on the left & right sides of the fish-eye effect. private final int mRamp; @@ -244,18 +241,6 @@ public final class Magnifier { } /** - * Sets the horizontal bounds of the source when showing the magnifier. - * This is used for new style magnifier. e.g. limit the source bounds by the text line bounds. - * - * @param left the left of the bounds, relative to the view. - * @param right the right of the bounds, relative to the view. - */ - void setSourceHorizontalBounds(int left, int right) { - mLeftBound = left; - mRightBound = right; - } - - /** * Shows the magnifier on the screen. The method takes the coordinates of the center * of the content source going to be magnified and copied to the magnifier. The coordinates * are relative to the top left corner of the magnified view. The magnifier will be @@ -280,6 +265,14 @@ public final class Magnifier { sourceCenterY + mDefaultVerticalSourceToMagnifierOffset); } + private Drawable mCursorDrawable; + private boolean mDrawCursorEnabled; + + void setDrawCursor(boolean enabled, Drawable cursorDrawable) { + mDrawCursorEnabled = enabled; + mCursorDrawable = cursorDrawable; + } + /** * Shows the magnifier on the screen at a position that is independent from its content * position. The first two arguments represent the coordinates of the center of the @@ -309,8 +302,7 @@ public final class Magnifier { magnifierCenterX = mClampedCenterZoomCoords.x - mViewCoordinatesInSurface[0]; magnifierCenterY = mClampedCenterZoomCoords.y - mViewCoordinatesInSurface[1]; - // mLeftBound & mRightBound (typically the text line left/right) is for magnified - // content. However the PixelCopy requires the pre-magnified bounds. + // PixelCopy requires the pre-magnified bounds. // The below logic calculates the leftBound & rightBound for the pre-magnified bounds. final float rampPre = (mSourceWidth - (mSourceWidth - 2 * mRamp) / mZoom) / 2; @@ -318,7 +310,7 @@ public final class Magnifier { // Calculates the pre-zoomed left edge. // The leftEdge moves from the left of view towards to sourceCenterX, considering the // fisheye-like zooming. - final float x0 = sourceCenterX - mSourceWidth / 2; + final float x0 = sourceCenterX - mSourceWidth / 2f; final float rampX0 = x0 + mRamp; float leftEdge = 0; if (leftEdge > rampX0) { @@ -330,12 +322,12 @@ public final class Magnifier { // increase per ramp zoom (ramp / rampPre). leftEdge = x0 + rampPre - (rampX0 - leftEdge) * rampPre / mRamp; } - int leftBound = Math.min(Math.max((int) leftEdge, mLeftBound), mRightBound); + int leftBound = Math.min((int) leftEdge, mView.getWidth()); // Calculates the pre-zoomed right edge. // The rightEdge moves from the right of view towards to sourceCenterX, considering the // fisheye-like zooming. - final float x1 = sourceCenterX + mSourceWidth / 2; + final float x1 = sourceCenterX + mSourceWidth / 2f; final float rampX1 = x1 - mRamp; float rightEdge = mView.getWidth(); if (rightEdge < rampX1) { @@ -347,7 +339,7 @@ public final class Magnifier { // increase per ramp zoom (ramp / rampPre). rightEdge = x1 - rampPre + (rightEdge - rampX1) * rampPre / mRamp; } - int rightBound = Math.max(leftBound, Math.min((int) rightEdge, mRightBound)); + int rightBound = Math.max(leftBound, (int) rightEdge); // Gets the startX for new style, which should be bounded by the horizontal bounds. // Also calculates the left/right cut width for pixel copy. @@ -772,6 +764,23 @@ public final class Magnifier { } } + private void maybeDrawCursor(Canvas canvas) { + if (mDrawCursorEnabled) { + if (mCursorDrawable != null) { + mCursorDrawable.setBounds( + mSourceWidth / 2, 0, + mSourceWidth / 2 + mCursorDrawable.getIntrinsicWidth(), mSourceHeight); + mCursorDrawable.draw(canvas); + } else { + Paint paint = new Paint(); + paint.setColor(Color.BLACK); // The cursor on magnifier is by default in black. + canvas.drawRect( + new Rect(mSourceWidth / 2 - 1, 0, mSourceWidth / 2 + 1, mSourceHeight), + paint); + } + } + } + private void performPixelCopy(final int startXInSurface, final int startYInSurface, final boolean updateWindowPosition) { if (mContentCopySurface.mSurface == null || !mContentCopySurface.mSurface.isValid()) { @@ -827,8 +836,10 @@ public final class Magnifier { final Rect dstRect = new Rect(mLeftCutWidth, 0, mSourceWidth - mRightCutWidth, bitmap.getHeight()); can.drawBitmap(bitmap, null, dstRect, null); + maybeDrawCursor(can); mWindow.updateContent(newBitmap); } else { + maybeDrawCursor(new Canvas(bitmap)); mWindow.updateContent(bitmap); } } |
