diff options
| author | 2011-02-15 10:41:30 -0800 | |
|---|---|---|
| committer | 2011-02-15 10:41:30 -0800 | |
| commit | 05cc6dbf70f3aab56c28300745cbbf9215977602 (patch) | |
| tree | fa28e4b9f7d018e4f9eb2e08f16bc61c4d513472 | |
| parent | 0a20767f610e048b29b86e86c7e647d293be0308 (diff) | |
| parent | f75c97e023af7d4ad9a8c129d4ea282b1c3b8f94 (diff) | |
Merge "Text insertion cursor is now defined by a Drawable."
| -rw-r--r-- | core/java/android/text/Layout.java | 6 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 165 | ||||
| -rw-r--r-- | core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png | bin | 0 -> 274 bytes | |||
| -rw-r--r-- | core/res/res/drawable-mdpi/text_cursor_holo_light.9.png | bin | 0 -> 249 bytes | |||
| -rwxr-xr-x | core/res/res/values/attrs.xml | 6 | ||||
| -rw-r--r-- | core/res/res/values/public.xml | 3 | ||||
| -rw-r--r-- | core/res/res/values/styles.xml | 1 | ||||
| -rw-r--r-- | core/res/res/values/themes.xml | 3 |
8 files changed, 138 insertions, 46 deletions
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 8700af802ae6..97a216a8415d 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -600,8 +600,9 @@ public abstract class Layout { * are at different run levels (and thus there's a split caret). * @param offset the offset * @return true if at a level boundary + * @hide */ - private boolean isLevelBoundary(int offset) { + public boolean isLevelBoundary(int offset) { int line = getLineForOffset(offset); Directions dirs = getLineDirections(line); if (dirs == DIRS_ALL_LEFT_TO_RIGHT || dirs == DIRS_ALL_RIGHT_TO_LEFT) { @@ -1148,8 +1149,7 @@ public abstract class Layout { int bottom = getLineTop(line+1); float h1 = getPrimaryHorizontal(point) - 0.5f; - float h2 = isLevelBoundary(point) ? - getSecondaryHorizontal(point) - 0.5f : h1; + float h2 = isLevelBoundary(point) ? getSecondaryHorizontal(point) - 0.5f : h1; int caps = TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SHIFT_ON) | TextKeyListener.getMetaState(editingBuffer, TextKeyListener.META_SELECTING); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 28b106bc4be4..993af31c31cd 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -304,15 +304,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } InputMethodState mInputMethodState; - int mTextSelectHandleLeftRes; - int mTextSelectHandleRightRes; - int mTextSelectHandleRes; - int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout; - int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout; + private int mTextSelectHandleLeftRes; + private int mTextSelectHandleRightRes; + private int mTextSelectHandleRes; + private int mTextEditPasteWindowLayout, mTextEditSidePasteWindowLayout; + private int mTextEditNoPasteWindowLayout, mTextEditSideNoPasteWindowLayout; - Drawable mSelectHandleLeft; - Drawable mSelectHandleRight; - Drawable mSelectHandleCenter; + private int mCursorDrawableRes; + private final Drawable[] mCursorDrawable = new Drawable[2]; + private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 + + private Drawable mSelectHandleLeft; + private Drawable mSelectHandleRight; + private Drawable mSelectHandleCenter; private int mLastDownPositionX, mLastDownPositionY; private Callback mCustomSelectionActionModeCallback; @@ -742,6 +746,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } break; + case com.android.internal.R.styleable.TextView_textCursorDrawable: + mCursorDrawableRes = a.getResourceId(attr, 0); + break; + case com.android.internal.R.styleable.TextView_textSelectHandleLeft: mTextSelectHandleLeftRes = a.getResourceId(attr, 0); break; @@ -3770,33 +3778,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mHighlightPathBogus) { invalidateCursor(); } else { - synchronized (sTempRect) { - /* - * The reason for this concern about the thickness of the - * cursor and doing the floor/ceil on the coordinates is that - * some EditTexts (notably textfields in the Browser) have - * anti-aliased text where not all the characters are - * necessarily at integer-multiple locations. This should - * make sure the entire cursor gets invalidated instead of - * sometimes missing half a pixel. - */ - - float thick = FloatMath.ceil(mTextPaint.getStrokeWidth()); - if (thick < 1.0f) { - thick = 1.0f; - } + final int horizontalPadding = getCompoundPaddingLeft(); + final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); - thick /= 2; + if (mCursorCount == 0) { + synchronized (sTempRect) { + /* + * The reason for this concern about the thickness of the + * cursor and doing the floor/ceil on the coordinates is that + * some EditTexts (notably textfields in the Browser) have + * anti-aliased text where not all the characters are + * necessarily at integer-multiple locations. This should + * make sure the entire cursor gets invalidated instead of + * sometimes missing half a pixel. + */ + float thick = FloatMath.ceil(mTextPaint.getStrokeWidth()); + if (thick < 1.0f) { + thick = 1.0f; + } - mHighlightPath.computeBounds(sTempRect, false); + thick /= 2.0f; - int left = getCompoundPaddingLeft(); - int top = getExtendedPaddingTop() + getVerticalOffset(true); + mHighlightPath.computeBounds(sTempRect, false); - invalidate((int) FloatMath.floor(left + sTempRect.left - thick), - (int) FloatMath.floor(top + sTempRect.top - thick), - (int) FloatMath.ceil(left + sTempRect.right + thick), - (int) FloatMath.ceil(top + sTempRect.bottom + thick)); + invalidate((int) FloatMath.floor(horizontalPadding + sTempRect.left - thick), + (int) FloatMath.floor(verticalPadding + sTempRect.top - thick), + (int) FloatMath.ceil(horizontalPadding + sTempRect.right + thick), + (int) FloatMath.ceil(verticalPadding + sTempRect.bottom + thick)); + } + } else { + for (int i = 0; i < mCursorCount; i++) { + Rect bounds = mCursorDrawable[i].getBounds(); + invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding, + bounds.right + horizontalPadding, bounds.bottom + verticalPadding); + } } } } @@ -3836,13 +3851,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener line2 = mLayout.getLineForOffset(last); int bottom = mLayout.getLineTop(line2 + 1); - int voffset = getVerticalOffset(true); - int left = getCompoundPaddingLeft() + mScrollX; - invalidate(left, top + voffset + getExtendedPaddingTop(), - left + getWidth() - getCompoundPaddingLeft() - - getCompoundPaddingRight(), - bottom + voffset + getExtendedPaddingTop()); + final int horizontalPadding = getCompoundPaddingLeft(); + final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); + + // If used, the cursor drawables can have an arbitrary dimension that can go beyond + // the invalidated lines specified above. + for (int i = 0; i < mCursorCount; i++) { + Rect bounds = mCursorDrawable[i].getBounds(); + top = Math.min(top, bounds.top); + bottom = Math.max(bottom, bounds.bottom); + // Horizontal bounds are already full width, no need to update + } + + invalidate(horizontalPadding + mScrollX, top + verticalPadding, + horizontalPadding + mScrollX + getWidth() - + getCompoundPaddingLeft() - getCompoundPaddingRight(), + bottom + verticalPadding); } } } @@ -4346,6 +4371,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Path highlight = null; int selStart = -1, selEnd = -1; + boolean drawCursor = false; // If there is no movement method, then there can be no selection. // Check that first and attempt to skip everything having to do with @@ -4366,6 +4392,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mHighlightPathBogus) { mHighlightPath.reset(); mLayout.getCursorPath(selStart, mHighlightPath, mText); + updateCursorsPositions(); mHighlightPathBogus = false; } @@ -4377,7 +4404,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mHighlightPaint.setStyle(Paint.Style.STROKE); - highlight = mHighlightPath; + if (mCursorCount > 0) { + drawCursor = true; + } else { + highlight = mHighlightPath; + } } } else { if (mHighlightPathBogus) { @@ -4460,6 +4491,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mCorrectionHighlighter.draw(canvas, cursorOffsetVertical); } + if (drawCursor) drawCursor(canvas, cursorOffsetVertical); + layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); if (mMarquee != null && mMarquee.shouldDrawGhost()) { @@ -4478,6 +4511,52 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener updateCursorControllerPositions(); } + private void updateCursorsPositions() { + if (mCursorDrawableRes == 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); + } + + private void drawCursor(Canvas canvas, int cursorOffsetVertical) { + final boolean translate = cursorOffsetVertical != 0; + if (translate) canvas.translate(0, cursorOffsetVertical); + for (int i = 0; i < mCursorCount; i++) { + mCursorDrawable[i].draw(canvas); + } + if (translate) canvas.translate(0, -cursorOffsetVertical); + } + /** * Update the positions of the CursorControllers. Needed by WebTextView, * which does not draw. @@ -8699,7 +8778,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mDrawable = mSelectHandleLeft; handleWidth = mDrawable.getIntrinsicWidth(); - mHotspotX = (handleWidth * 3) / 4; + mHotspotX = handleWidth * 3.0f / 4.0f; break; } @@ -8710,7 +8789,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mDrawable = mSelectHandleRight; handleWidth = mDrawable.getIntrinsicWidth(); - mHotspotX = handleWidth / 4; + mHotspotX = handleWidth / 4.0f; break; } @@ -8722,7 +8801,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mDrawable = mSelectHandleCenter; handleWidth = mDrawable.getIntrinsicWidth(); - mHotspotX = handleWidth / 2; + mHotspotX = handleWidth / 2.0f; mIsInsertionHandle = true; break; } @@ -8937,8 +9016,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int lineBottom = mLayout.getLineBottom(line); final Rect bounds = sCursorControllerTempRect; - bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - mHotspotX) - + TextView.this.mScrollX; + bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX) + + TextView.this.mScrollX; bounds.top = (bottom ? lineBottom : lineTop - mHeight) + TextView.this.mScrollY; bounds.right = bounds.left + width; diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png Binary files differnew file mode 100644 index 000000000000..b9435b645117 --- /dev/null +++ b/core/res/res/drawable-mdpi/text_cursor_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png Binary files differnew file mode 100644 index 000000000000..477d820cc965 --- /dev/null +++ b/core/res/res/drawable-mdpi/text_cursor_holo_light.9.png diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 8802003af518..6f37dc097010 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -778,6 +778,9 @@ <!-- Color of link text (URLs). --> <attr name="textColorLink" format="reference|color" /> + <!-- Reference to a drawable that will be drawn under the insertion cursor. --> + <attr name="textCursorDrawable" format="reference" /> + <!-- Indicates that the content of a non-editable TextView can be selected. Default value is false. EditText content is always selectable. --> <attr name="textIsSelectable" format="boolean" /> @@ -2783,6 +2786,9 @@ <!-- Variation of textEditSidePasteWindowLayout displayed when the clipboard is empty. --> <attr name="textEditSideNoPasteWindowLayout" /> + <!-- Reference to a drawable that will be drawn under the insertion cursor. --> + <attr name="textCursorDrawable" /> + <!-- Indicates that the content of a non-editable text can be selected. --> <attr name="textIsSelectable" /> </declare-styleable> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index aaf071b329ec..454257522a09 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1642,4 +1642,7 @@ <!-- Default icon for applications that don't specify an icon. --> <public type="mipmap" name="sym_def_app_icon" id="0x010d0000" /> + <!-- Theme attribute used to customize the text insertion cursor --> + <!-- Commented out for HC MR1 to prevent an API change --> + <!-- <public type="attr" name="textCursorDrawable" id="0x01010362" /> --> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 5700641f3986..8cc5944dc058 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -426,6 +426,7 @@ <item name="android:textEditNoPasteWindowLayout">?android:attr/textEditNoPasteWindowLayout</item> <item name="android:textEditSidePasteWindowLayout">?android:attr/textEditSidePasteWindowLayout</item> <item name="android:textEditSideNoPasteWindowLayout">?android:attr/textEditSideNoPasteWindowLayout</item> + <item name="android:textCursorDrawable">?android:attr/textCursorDrawable</item> </style> <style name="Widget.TextView.ListSeparator"> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 6d5b4822b1dc..927a668ac857 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -180,6 +180,7 @@ <item name="textEditNoPasteWindowLayout">@android:layout/text_edit_no_paste_window</item> <item name="textEditSidePasteWindowLayout">@android:layout/text_edit_side_paste_window</item> <item name="textEditSideNoPasteWindowLayout">@android:layout/text_edit_side_no_paste_window</item> + <item name="textCursorDrawable">@null</item> <!-- Widget styles --> <item name="absListViewStyle">@android:style/Widget.AbsListView</item> @@ -906,6 +907,7 @@ <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item> <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item> <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item> + <item name="textCursorDrawable">@android:drawable/text_cursor_holo_dark</item> <!-- Widget styles --> <item name="absListViewStyle">@android:style/Widget.Holo.AbsListView</item> @@ -1181,6 +1183,7 @@ <item name="textSelectHandleRight">@android:drawable/text_select_handle_right</item> <item name="textSelectHandle">@android:drawable/text_select_handle_middle</item> <item name="textSelectHandleWindowStyle">@android:style/Widget.Holo.TextSelectHandle</item> + <item name="textCursorDrawable">@android:drawable/text_cursor_holo_light</item> <!-- Widget styles --> <item name="absListViewStyle">@android:style/Widget.Holo.Light.AbsListView</item> |