summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGilles Debunne <debunne@google.com>2012-02-24 20:01:13 -0800
committerGilles Debunne <debunne@google.com>2012-02-27 14:54:58 -0800
commit83051b8c75a145a12eca1a021ff1a4fbcc77ba4b (patch)
tree200c9f6f1a3d016f8b3a8886320d8548ec6d294e
parent35ae6e2a841d8a287bccb56881f58e9fe24a07b6 (diff)
downloadframeworks_base-83051b8c75a145a12eca1a021ff1a4fbcc77ba4b.zip
frameworks_base-83051b8c75a145a12eca1a021ff1a4fbcc77ba4b.tar.gz
frameworks_base-83051b8c75a145a12eca1a021ff1a4fbcc77ba4b.tar.bz2
Selection highlight made possible with no editor
Bug 6065081 Selecting text programatically is possible (like when a link is highlighted by LinkMovementMethod) even when the text is not editable. Also removed the textCanBeSelected text is highlight computation which prevented links from being highlighted (legacy problem, not reported). Now that they work, we can see that the highlight is not removed when the text loses focus. Will be addressed in a different CL. Change-Id: I673f435966686234b4c0b0a97bcc65abd01169ff
-rw-r--r--core/java/android/widget/TextView.java300
1 files changed, 143 insertions, 157 deletions
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d6dd15e..f855266 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -406,9 +406,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private InputFilter[] mFilters = NO_FILTERS;
+ // It is possible to have a selection even when mEditor is null (programmatically set, like when
+ // a link is pressed). These highlight-related fields do not go in mEditor.
+ private int mHighlightColor = 0x6633B5E5;
+ private Path mHighlightPath;
+ private final Paint mHighlightPaint;
+ private boolean mHighlightPathBogus = true;
+
// Although these fields are specific to editable text, they are not added to Editor because
// they are defined by the TextView's style and are theme-dependent.
- private int mHighlightColor = 0x6633B5E5;
private int mCursorDrawableRes;
// These four fields, could be moved to Editor, since we know their default values and we
// could condition the creation of the Editor to a non standard value. This is however
@@ -477,6 +483,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mTextPaint.density = res.getDisplayMetrics().density;
mTextPaint.setCompatibilityScaling(compat.applicationScale);
+ mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mHighlightPaint.setCompatibilityScaling(compat.applicationScale);
+
mMovement = getDefaultMovementMethod();
mTransformation = null;
@@ -4064,7 +4073,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private void invalidateCursorPath() {
- if (getEditor().mHighlightPathBogus) {
+ if (mHighlightPathBogus) {
invalidateCursor();
} else {
final int horizontalPadding = getCompoundPaddingLeft();
@@ -4088,7 +4097,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
thick /= 2.0f;
- getEditor().mHighlightPath.computeBounds(TEMP_RECTF, false);
+ // mHighlightPath is guaranteed to be non null at that point.
+ mHighlightPath.computeBounds(TEMP_RECTF, false);
invalidate((int) FloatMath.floor(horizontalPadding + TEMP_RECTF.left - thick),
(int) FloatMath.floor(verticalPadding + TEMP_RECTF.top - thick),
@@ -4150,7 +4160,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int bottom = mLayout.getLineBottom(lineEnd);
- if (invalidateCursor) {
+ // mEditor can be null in case selection is set programmatically.
+ if (invalidateCursor && mEditor != null) {
for (int i = 0; i < getEditor().mCursorCount; i++) {
Rect bounds = getEditor().mCursorDrawable[i].getBounds();
top = Math.min(top, bounds.top);
@@ -4515,6 +4526,55 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return drawableState;
}
+ private Path getUpdatedHighlightPath() {
+ Path highlight = null;
+ Paint highlightPaint = mHighlightPaint;
+
+ final int selStart = getSelectionStart();
+ final int selEnd = getSelectionEnd();
+ if (mMovement != null && (isFocused() || isPressed()) && selStart >= 0) {
+ if (selStart == selEnd) {
+ if (mEditor != null && isCursorVisible() &&
+ (SystemClock.uptimeMillis() - getEditor().mShowCursor) % (2 * BLINK) < BLINK) {
+ if (mHighlightPathBogus) {
+ if (mHighlightPath == null) mHighlightPath = new Path();
+ mHighlightPath.reset();
+ mLayout.getCursorPath(selStart, mHighlightPath, mText);
+ getEditor().updateCursorsPositions();
+ mHighlightPathBogus = false;
+ }
+
+ // XXX should pass to skin instead of drawing directly
+ highlightPaint.setColor(mCurTextColor);
+ if (mCurrentAlpha != 255) {
+ highlightPaint.setAlpha(
+ (mCurrentAlpha * Color.alpha(mCurTextColor)) / 255);
+ }
+ highlightPaint.setStyle(Paint.Style.STROKE);
+ highlight = mHighlightPath;
+ }
+ } else {
+ if (mHighlightPathBogus) {
+ if (mHighlightPath == null) mHighlightPath = new Path();
+ mHighlightPath.reset();
+ mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
+ mHighlightPathBogus = false;
+ }
+
+ // XXX should pass to skin instead of drawing directly
+ highlightPaint.setColor(mHighlightColor);
+ if (mCurrentAlpha != 255) {
+ highlightPaint.setAlpha(
+ (mCurrentAlpha * Color.alpha(mHighlightColor)) / 255);
+ }
+ highlightPaint.setStyle(Paint.Style.FILL);
+
+ highlight = mHighlightPath;
+ }
+ }
+ return highlight;
+ }
+
@Override
protected void onDraw(Canvas canvas) {
if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return;
@@ -4666,68 +4726,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int cursorOffsetVertical = voffsetCursor - voffsetText;
+ Path highlight = getUpdatedHighlightPath();
if (mEditor != null) {
- getEditor().onDraw(canvas, layout, cursorOffsetVertical);
+ getEditor().onDraw(canvas, layout, highlight, cursorOffsetVertical);
} else {
- layout.draw(canvas, null, null, cursorOffsetVertical);
+ layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
if (mMarquee != null && mMarquee.shouldDrawGhost()) {
canvas.translate((int) mMarquee.getGhostOffset(), 0.0f);
- layout.draw(canvas, null, null, cursorOffsetVertical);
+ layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
}
}
canvas.restore();
}
- private void updateCursorsPositions() {
- if (mCursorDrawableRes == 0) {
- getEditor().mCursorCount = 0;
- return;
- }
-
- final int offset = getSelectionStart();
- final int line = mLayout.getLineForOffset(offset);
- final int top = mLayout.getLineTop(line);
- final int bottom = mLayout.getLineTop(line + 1);
-
- getEditor().mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1;
-
- int middle = bottom;
- if (getEditor().mCursorCount == 2) {
- // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)}
- middle = (top + bottom) >> 1;
- }
-
- updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset));
-
- if (getEditor().mCursorCount == 2) {
- updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset));
- }
- }
-
- private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
- if (getEditor().mCursorDrawable[cursorIndex] == null)
- getEditor().mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes);
-
- if (mTempRect == null) mTempRect = new Rect();
- getEditor().mCursorDrawable[cursorIndex].getPadding(mTempRect);
- final int width = getEditor().mCursorDrawable[cursorIndex].getIntrinsicWidth();
- horizontal = Math.max(0.5f, horizontal - 0.5f);
- final int left = (int) (horizontal) - mTempRect.left;
- getEditor().mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
- bottom + mTempRect.bottom);
- }
-
- private void drawCursor(Canvas canvas, int cursorOffsetVertical) {
- final boolean translate = cursorOffsetVertical != 0;
- if (translate) canvas.translate(0, cursorOffsetVertical);
- for (int i = 0; i < getEditor().mCursorCount; i++) {
- getEditor().mCursorDrawable[i].draw(canvas);
- }
- if (translate) canvas.translate(0, -cursorOffsetVertical);
- }
-
@Override
public void getFocusedRect(Rect r) {
if (mLayout == null) {
@@ -4759,21 +4772,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} else {
// Selection extends across multiple lines -- make the focused
// rect cover the entire width.
- if (mEditor != null) {
- if (getEditor().mHighlightPath == null) getEditor().mHighlightPath = new Path();
- if (getEditor().mHighlightPathBogus) {
- getEditor().mHighlightPath.reset();
- mLayout.getSelectionPath(selStart, selEnd, getEditor().mHighlightPath);
- getEditor().mHighlightPathBogus = false;
- }
- synchronized (TEMP_RECTF) {
- getEditor().mHighlightPath.computeBounds(TEMP_RECTF, true);
- r.left = (int)TEMP_RECTF.left-1;
- r.right = (int)TEMP_RECTF.right+1;
- }
- } else {
- r.left = 0;
- r.right = getMeasuredWidth();
+ if (mHighlightPathBogus) {
+ if (mHighlightPath == null) mHighlightPath = new Path();
+ mHighlightPath.reset();
+ mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
+ mHighlightPathBogus = false;
+ }
+ synchronized (TEMP_RECTF) {
+ mHighlightPath.computeBounds(TEMP_RECTF, true);
+ r.left = (int)TEMP_RECTF.left-1;
+ r.right = (int)TEMP_RECTF.right+1;
}
}
}
@@ -5584,7 +5592,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (curs >= 0) {
- getEditor().mHighlightPathBogus = true;
+ mHighlightPathBogus = true;
makeBlink();
bringPointIntoView(curs);
}
@@ -5741,7 +5749,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mOldMaximum = mMaximum;
mOldMaxMode = mMaxMode;
- if (mEditor != null) getEditor().mHighlightPathBogus = true;
+ mHighlightPathBogus = true;
if (wantWidth < 0) {
wantWidth = 0;
@@ -6982,10 +6990,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
protected void onSelectionChanged(int selStart, int selEnd) {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
- // mEditor may be null if selection is created programatically.
- createEditorIfNeeded("onSelectionChanged");
- // Invalidate even when selection range is empty, to remove previous highlight
- getEditor().mTextDisplayListIsValid = false;
+ if (mEditor != null) getEditor().mTextDisplayListIsValid = false;
}
/**
@@ -7141,10 +7146,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (selChanged) {
- if (mEditor != null) {
- getEditor().mHighlightPathBogus = true;
- if (!isFocused()) getEditor().mSelectionMoved = true;
- }
+ mHighlightPathBogus = true;
+ if (mEditor != null && !isFocused()) getEditor().mSelectionMoved = true;
if ((buf.getSpanFlags(what)&Spanned.SPAN_INTERMEDIATE) == 0) {
if (newSelStart < 0) {
@@ -7161,7 +7164,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
what instanceof CharacterStyle) {
if (ims == null || ims.mBatchEditNesting == 0) {
invalidate();
- if (mEditor != null) getEditor().mHighlightPathBogus = true;
+ mHighlightPathBogus = true;
checkForResize();
} else {
ims.mContentChanged = true;
@@ -7170,7 +7173,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (MetaKeyKeyListener.isMetaTracker(buf, what)) {
- if (mEditor != null) getEditor().mHighlightPathBogus = true;
+ mHighlightPathBogus = true;
if (ims != null && MetaKeyKeyListener.isSelectingMetaTracker(buf, what)) {
ims.mSelectionModeChanged = true;
}
@@ -11330,12 +11333,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private class Editor {
- Editor() {
- mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- final CompatibilityInfo compat = TextView.this.getResources().getCompatibilityInfo();
- mHighlightPaint.setCompatibilityScaling(compat.applicationScale);
- }
-
// Cursor Controllers.
InsertionPointCursorController mInsertionPointCursorController;
SelectionModifierCursorController mSelectionModifierCursorController;
@@ -11349,10 +11346,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
InputContentType mInputContentType;
InputMethodState mInputMethodState;
- Path mHighlightPath;
- boolean mHighlightPathBogus = true;
- final Paint mHighlightPaint;
-
DisplayList mTextDisplayList;
boolean mTextDisplayListIsValid;
@@ -11391,7 +11384,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Runnable mShowSuggestionRunnable;
final Drawable[] mCursorDrawable = new Drawable[2];
- int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 (split)
+ int mCursorCount; // Current number of used mCursorDrawable: 0 (resource=0), 1 or 2 (split)
Drawable mSelectHandleLeft;
Drawable mSelectHandleRight;
@@ -11649,65 +11642,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- void onDraw(Canvas canvas, Layout layout, int cursorOffsetVertical) {
- Path highlight = null;
- Paint highlightPaint = null;
-
- int selStart = -1, selEnd = -1;
- boolean drawCursor = false;
-
- highlightPaint = mHighlightPaint;
- // If there is no movement method, then there can be no selection.
- // Check that first and attempt to skip everything having to do with
- // the cursor.
- // XXX This is not strictly true -- a program could set the
- // selection manually if it really wanted to.
- if (mMovement != null && (isFocused() || isPressed())) {
- selStart = getSelectionStart();
- selEnd = getSelectionEnd();
-
- if (selStart >= 0) {
- if (mHighlightPath == null) mHighlightPath = new Path();
-
- if (selStart == selEnd) {
- if (isCursorVisible() &&
- (SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK) {
- if (mHighlightPathBogus) {
- mHighlightPath.reset();
- mLayout.getCursorPath(selStart, mHighlightPath, mText);
- updateCursorsPositions();
- mHighlightPathBogus = false;
- }
-
- // XXX should pass to skin instead of drawing directly
- highlightPaint.setColor(mCurTextColor);
- if (mCurrentAlpha != 255) {
- highlightPaint.setAlpha(
- (mCurrentAlpha * Color.alpha(mCurTextColor)) / 255);
- }
- highlightPaint.setStyle(Paint.Style.STROKE);
- highlight = mHighlightPath;
- drawCursor = mCursorCount > 0;
- }
- } else if (textCanBeSelected()) {
- if (mHighlightPathBogus) {
- mHighlightPath.reset();
- mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
- mHighlightPathBogus = false;
- }
-
- // XXX should pass to skin instead of drawing directly
- highlightPaint.setColor(mHighlightColor);
- if (mCurrentAlpha != 255) {
- highlightPaint.setAlpha(
- (mCurrentAlpha * Color.alpha(mHighlightColor)) / 255);
- }
- highlightPaint.setStyle(Paint.Style.FILL);
-
- highlight = mHighlightPath;
- }
- }
- }
+ void onDraw(Canvas canvas, Layout layout, Path highlight, int cursorOffsetVertical) {
+ final int selectionStart = getSelectionStart();
+ final int selectionEnd = getSelectionEnd();
final InputMethodState ims = mInputMethodState;
if (ims != null && ims.mBatchEditNesting == 0) {
@@ -11729,7 +11666,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
candStart = EditableInputConnection.getComposingSpanStart(sp);
candEnd = EditableInputConnection.getComposingSpanEnd(sp);
}
- imm.updateSelection(TextView.this, selStart, selEnd, candStart, candEnd);
+ imm.updateSelection(TextView.this,
+ selectionStart, selectionEnd, candStart, candEnd);
}
}
@@ -11758,7 +11696,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mCorrectionHighlighter.draw(canvas, cursorOffsetVertical);
}
- if (drawCursor) {
+ if (highlight != null && selectionStart == selectionEnd && mCursorCount > 0) {
drawCursor(canvas, cursorOffsetVertical);
// Rely on the drawable entirely, do not draw the cursor line.
// Has to be done after the IMM related code above which relies on the highlight.
@@ -11781,7 +11719,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// The dirty rect should always be null for a display list
hardwareCanvas.onPreDraw(null);
hardwareCanvas.translate(-mScrollX, -mScrollY);
- layout.draw(hardwareCanvas, highlight, highlightPaint, cursorOffsetVertical);
+ layout.draw(hardwareCanvas, highlight, mHighlightPaint, cursorOffsetVertical);
hardwareCanvas.translate(mScrollX, mScrollY);
} finally {
hardwareCanvas.onPostDraw();
@@ -11794,13 +11732,61 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
DisplayList.FLAG_CLIP_CHILDREN);
canvas.translate(-mScrollX, -mScrollY);
} else {
- layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);
+ layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
}
if (mMarquee != null && mMarquee.shouldDrawGhost()) {
canvas.translate((int) mMarquee.getGhostOffset(), 0.0f);
- layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical);
+ layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
+ }
+ }
+
+ private void drawCursor(Canvas canvas, int cursorOffsetVertical) {
+ final boolean translate = cursorOffsetVertical != 0;
+ if (translate) canvas.translate(0, cursorOffsetVertical);
+ for (int i = 0; i < getEditor().mCursorCount; i++) {
+ mCursorDrawable[i].draw(canvas);
}
+ if (translate) canvas.translate(0, -cursorOffsetVertical);
+ }
+
+ private void updateCursorsPositions() {
+ if (mCursorDrawableRes == 0) {
+ mCursorCount = 0;
+ return;
+ }
+
+ final int offset = getSelectionStart();
+ final int line = mLayout.getLineForOffset(offset);
+ final int top = mLayout.getLineTop(line);
+ final int bottom = mLayout.getLineTop(line + 1);
+
+ mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1;
+
+ int middle = bottom;
+ if (mCursorCount == 2) {
+ // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)}
+ middle = (top + bottom) >> 1;
+ }
+
+ updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset));
+
+ if (mCursorCount == 2) {
+ updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset));
+ }
+ }
+
+ private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
+ if (mCursorDrawable[cursorIndex] == null)
+ mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes);
+
+ if (mTempRect == null) mTempRect = new Rect();
+ mCursorDrawable[cursorIndex].getPadding(mTempRect);
+ final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth();
+ horizontal = Math.max(0.5f, horizontal - 0.5f);
+ final int left = (int) (horizontal) - mTempRect.left;
+ mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
+ bottom + mTempRect.bottom);
}
}
}