Merge "Fix bug #5243493 TextView selection is not working correctly when there is some RTL run into it"

This commit is contained in:
Fabrice Di Meglio
2011-09-06 11:13:40 -07:00
committed by Android (Google) Code Review
3 changed files with 103 additions and 26 deletions

View File

@@ -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

View File

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

View File

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