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
This commit is contained in:
Shu Chen
2020-08-04 09:34:35 +08:00
parent d551401a81
commit 4021c3dfd6
2 changed files with 92 additions and 43 deletions

View File

@@ -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);
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 {
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;
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();

View File

@@ -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;
@@ -243,18 +240,6 @@ public final class Magnifier {
sPixelCopyHandlerThread.start();
}
/**
* 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
@@ -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);
}
}