Merge "Fix bug #5243493 TextView selection is not working correctly when there is some RTL run into it"
This commit is contained in:
committed by
Android (Google) Code Review
commit
be724dbc45
@@ -19932,6 +19932,7 @@ package android.text {
|
|||||||
method public abstract int getTopPadding();
|
method public abstract int getTopPadding();
|
||||||
method public final int getWidth();
|
method public final int getWidth();
|
||||||
method public final void increaseWidthTo(int);
|
method public final void increaseWidthTo(int);
|
||||||
|
method public boolean isRtlCharAt(int);
|
||||||
method protected final boolean isSpanned();
|
method protected final boolean isSpanned();
|
||||||
field public static final int DIR_LEFT_TO_RIGHT = 1; // 0x1
|
field public static final int DIR_LEFT_TO_RIGHT = 1; // 0x1
|
||||||
field public static final int DIR_RIGHT_TO_LEFT = -1; // 0xffffffff
|
field public static final int DIR_RIGHT_TO_LEFT = -1; // 0xffffffff
|
||||||
|
|||||||
@@ -673,6 +673,35 @@ public abstract class Layout {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the character at offset is right to left (RTL).
|
||||||
|
* @param offset the offset
|
||||||
|
* @return true if the character is RTL, false if it is LTR
|
||||||
|
*/
|
||||||
|
public boolean isRtlCharAt(int offset) {
|
||||||
|
int line = getLineForOffset(offset);
|
||||||
|
Directions dirs = getLineDirections(line);
|
||||||
|
if (dirs == DIRS_ALL_LEFT_TO_RIGHT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dirs == DIRS_ALL_RIGHT_TO_LEFT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
int[] runs = dirs.mDirections;
|
||||||
|
int lineStart = getLineStart(line);
|
||||||
|
for (int i = 0; i < runs.length; i += 2) {
|
||||||
|
int start = lineStart + (runs[i] & RUN_LENGTH_MASK);
|
||||||
|
// No need to test the end as an offset after the last run should return the value
|
||||||
|
// corresponding of the last run
|
||||||
|
if (offset >= start) {
|
||||||
|
int level = (runs[i+1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK;
|
||||||
|
return ((level & 1) != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Should happen only if the offset is "out of bounds"
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean primaryIsTrailingPrevious(int offset) {
|
private boolean primaryIsTrailingPrevious(int offset) {
|
||||||
int line = getLineForOffset(offset);
|
int line = getLineForOffset(offset);
|
||||||
int lineStart = getLineStart(line);
|
int lineStart = getLineStart(line);
|
||||||
|
|||||||
@@ -10333,6 +10333,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
|
|
||||||
private abstract class HandleView extends View implements TextViewPositionListener {
|
private abstract class HandleView extends View implements TextViewPositionListener {
|
||||||
protected Drawable mDrawable;
|
protected Drawable mDrawable;
|
||||||
|
protected Drawable mDrawableLtr;
|
||||||
|
protected Drawable mDrawableRtl;
|
||||||
private final PopupWindow mContainer;
|
private final PopupWindow mContainer;
|
||||||
// Position with respect to the parent TextView
|
// Position with respect to the parent TextView
|
||||||
private int mPositionX, mPositionY;
|
private int mPositionX, mPositionY;
|
||||||
@@ -10355,7 +10357,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
// Used to delay the appearance of the action popup window
|
// Used to delay the appearance of the action popup window
|
||||||
private Runnable mActionPopupShower;
|
private Runnable mActionPopupShower;
|
||||||
|
|
||||||
public HandleView() {
|
public HandleView(Drawable drawableLtr, Drawable drawableRtl) {
|
||||||
super(TextView.this.mContext);
|
super(TextView.this.mContext);
|
||||||
mContainer = new PopupWindow(TextView.this.mContext, null,
|
mContainer = new PopupWindow(TextView.this.mContext, null,
|
||||||
com.android.internal.R.attr.textSelectHandleWindowStyle);
|
com.android.internal.R.attr.textSelectHandleWindowStyle);
|
||||||
@@ -10364,14 +10366,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
|
mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
|
||||||
mContainer.setContentView(this);
|
mContainer.setContentView(this);
|
||||||
|
|
||||||
initDrawable();
|
mDrawableLtr = drawableLtr;
|
||||||
|
mDrawableRtl = drawableRtl;
|
||||||
|
|
||||||
|
updateDrawable();
|
||||||
|
|
||||||
final int handleHeight = mDrawable.getIntrinsicHeight();
|
final int handleHeight = mDrawable.getIntrinsicHeight();
|
||||||
mTouchOffsetY = -0.3f * handleHeight;
|
mTouchOffsetY = -0.3f * handleHeight;
|
||||||
mIdealVerticalOffset = 0.7f * handleHeight;
|
mIdealVerticalOffset = 0.7f * handleHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void initDrawable();
|
protected void updateDrawable() {
|
||||||
|
final int offset = getCurrentCursorOffset();
|
||||||
|
final boolean isRtlCharAtOffset = mLayout.isRtlCharAt(offset);
|
||||||
|
mDrawable = isRtlCharAtOffset ? mDrawableRtl : mDrawableLtr;
|
||||||
|
mHotspotX = getHotspotX(mDrawable, isRtlCharAtOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract int getHotspotX(Drawable drawable, boolean isRtlRun);
|
||||||
|
|
||||||
// Touch-up filter: number of previous positions remembered
|
// Touch-up filter: number of previous positions remembered
|
||||||
private static final int HISTORY_SIZE = 5;
|
private static final int HISTORY_SIZE = 5;
|
||||||
@@ -10487,7 +10499,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
|
|
||||||
public abstract int getCurrentCursorOffset();
|
public abstract int getCurrentCursorOffset();
|
||||||
|
|
||||||
public abstract void updateSelection(int offset);
|
protected void updateSelection(int offset) {
|
||||||
|
updateDrawable();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void updatePosition(float x, float y);
|
public abstract void updatePosition(float x, float y);
|
||||||
|
|
||||||
@@ -10629,6 +10643,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
private float mDownPositionX, mDownPositionY;
|
private float mDownPositionX, mDownPositionY;
|
||||||
private Runnable mHider;
|
private Runnable mHider;
|
||||||
|
|
||||||
|
public InsertionHandleView(Drawable drawable) {
|
||||||
|
super(drawable, drawable);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void show() {
|
public void show() {
|
||||||
super.show();
|
super.show();
|
||||||
@@ -10665,13 +10683,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initDrawable() {
|
protected int getHotspotX(Drawable drawable, boolean isRtlRun) {
|
||||||
if (mSelectHandleCenter == null) {
|
return drawable.getIntrinsicWidth() / 2;
|
||||||
mSelectHandleCenter = mContext.getResources().getDrawable(
|
|
||||||
mTextSelectHandleRes);
|
|
||||||
}
|
|
||||||
mDrawable = mSelectHandleCenter;
|
|
||||||
mHotspotX = mDrawable.getIntrinsicWidth() / 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -10741,14 +10754,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class SelectionStartHandleView extends HandleView {
|
private class SelectionStartHandleView extends HandleView {
|
||||||
|
|
||||||
|
public SelectionStartHandleView(Drawable drawableLtr, Drawable drawableRtl) {
|
||||||
|
super(drawableLtr, drawableRtl);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initDrawable() {
|
protected int getHotspotX(Drawable drawable, boolean isRtlRun) {
|
||||||
if (mSelectHandleLeft == null) {
|
if (isRtlRun) {
|
||||||
mSelectHandleLeft = mContext.getResources().getDrawable(
|
return drawable.getIntrinsicWidth() / 4;
|
||||||
mTextSelectHandleLeftRes);
|
} else {
|
||||||
|
return (drawable.getIntrinsicWidth() * 3) / 4;
|
||||||
}
|
}
|
||||||
mDrawable = mSelectHandleLeft;
|
|
||||||
mHotspotX = (mDrawable.getIntrinsicWidth() * 3) / 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -10758,6 +10775,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateSelection(int offset) {
|
public void updateSelection(int offset) {
|
||||||
|
super.updateSelection(offset);
|
||||||
Selection.setSelection((Spannable) mText, offset, getSelectionEnd());
|
Selection.setSelection((Spannable) mText, offset, getSelectionEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10778,14 +10796,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class SelectionEndHandleView extends HandleView {
|
private class SelectionEndHandleView extends HandleView {
|
||||||
|
|
||||||
|
public SelectionEndHandleView(Drawable drawableLtr, Drawable drawableRtl) {
|
||||||
|
super(drawableLtr, drawableRtl);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initDrawable() {
|
protected int getHotspotX(Drawable drawable, boolean isRtlRun) {
|
||||||
if (mSelectHandleRight == null) {
|
if (isRtlRun) {
|
||||||
mSelectHandleRight = mContext.getResources().getDrawable(
|
return (drawable.getIntrinsicWidth() * 3) / 4;
|
||||||
mTextSelectHandleRightRes);
|
} else {
|
||||||
|
return drawable.getIntrinsicWidth() / 4;
|
||||||
}
|
}
|
||||||
mDrawable = mSelectHandleRight;
|
|
||||||
mHotspotX = mDrawable.getIntrinsicWidth() / 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -10795,6 +10817,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateSelection(int offset) {
|
public void updateSelection(int offset) {
|
||||||
|
super.updateSelection(offset);
|
||||||
Selection.setSelection((Spannable) mText, getSelectionStart(), offset);
|
Selection.setSelection((Spannable) mText, getSelectionStart(), offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10864,8 +10887,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InsertionHandleView getHandle() {
|
private InsertionHandleView getHandle() {
|
||||||
|
if (mSelectHandleCenter == null) {
|
||||||
|
mSelectHandleCenter = mContext.getResources().getDrawable(
|
||||||
|
mTextSelectHandleRes);
|
||||||
|
}
|
||||||
if (mHandle == null) {
|
if (mHandle == null) {
|
||||||
mHandle = new InsertionHandleView();
|
mHandle = new InsertionHandleView(mSelectHandleCenter);
|
||||||
}
|
}
|
||||||
return mHandle;
|
return mHandle;
|
||||||
}
|
}
|
||||||
@@ -10899,10 +10926,30 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
if (isInBatchEditMode()) {
|
if (isInBatchEditMode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
initDrawables();
|
||||||
|
initHandles();
|
||||||
|
hideInsertionPointCursorController();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDrawables() {
|
||||||
|
if (mSelectHandleLeft == null) {
|
||||||
|
mSelectHandleLeft = mContext.getResources().getDrawable(
|
||||||
|
mTextSelectHandleLeftRes);
|
||||||
|
}
|
||||||
|
if (mSelectHandleRight == null) {
|
||||||
|
mSelectHandleRight = mContext.getResources().getDrawable(
|
||||||
|
mTextSelectHandleRightRes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initHandles() {
|
||||||
// Lazy object creation has to be done before updatePosition() is called.
|
// Lazy object creation has to be done before updatePosition() is called.
|
||||||
if (mStartHandle == null) mStartHandle = new SelectionStartHandleView();
|
if (mStartHandle == null) {
|
||||||
if (mEndHandle == null) mEndHandle = new SelectionEndHandleView();
|
mStartHandle = new SelectionStartHandleView(mSelectHandleLeft, mSelectHandleRight);
|
||||||
|
}
|
||||||
|
if (mEndHandle == null) {
|
||||||
|
mEndHandle = new SelectionEndHandleView(mSelectHandleRight, mSelectHandleLeft);
|
||||||
|
}
|
||||||
|
|
||||||
mStartHandle.show();
|
mStartHandle.show();
|
||||||
mEndHandle.show();
|
mEndHandle.show();
|
||||||
|
|||||||
Reference in New Issue
Block a user