summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorShu Chen <shuchen@google.com>2020-08-04 09:34:35 +0800
committerShu Chen <shuchen@google.com>2020-08-07 19:18:48 +0800
commit4021c3dfd682125cdf4bf15ad5e4699cb84c8745 (patch)
tree14ca55146621c5c4a40c38cc8473bdd1ceb8c20c /core/java
parentd551401a81742b31ac29cdf4e46645eba3fec09f (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.java82
-rw-r--r--core/java/android/widget/Magnifier.java53
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);
}
}