Merge "TextView cursor and selection improvements." into gingerbread

This commit is contained in:
Gilles Debunne
2010-10-08 14:42:42 -07:00
committed by Android (Google) Code Review

View File

@@ -6549,12 +6549,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int selEnd = getSelectionEnd(); int selEnd = getSelectionEnd();
if (!mFrozenWithFocus || (selStart < 0 || selEnd < 0)) { if (!mFrozenWithFocus || (selStart < 0 || selEnd < 0)) {
// If a tap was used to give focus to that view, move cursor at tap position.
// Has to be done before onTakeFocus, which can be overloaded. // Has to be done before onTakeFocus, which can be overloaded.
if (mLastTouchOffset >= 0) { moveCursorToLastTapPosition();
// Can happen when a TextView is displayed after its content has been deleted.
mLastTouchOffset = Math.min(mLastTouchOffset, mText.length());
Selection.setSelection((Spannable) mText, mLastTouchOffset);
}
if (mMovement != null) { if (mMovement != null) {
mMovement.onTakeFocus(this, (Spannable) mText, direction); mMovement.onTakeFocus(this, (Spannable) mText, direction);
@@ -6613,8 +6610,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} else { } else {
terminateTextSelectionMode(); terminateTextSelectionMode();
} }
mLastTouchOffset = -1;
} }
startStopMarquee(focused); startStopMarquee(focused);
@@ -6626,6 +6621,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
super.onFocusChanged(focused, direction, previouslyFocusedRect); super.onFocusChanged(focused, direction, previouslyFocusedRect);
} }
private void moveCursorToLastTapPosition() {
if (mSelectionModifierCursorController != null) {
int mTapToFocusPosition = ((SelectionModifierCursorController)
mSelectionModifierCursorController).getMinTouchOffset();
if (mTapToFocusPosition >= 0) {
// Safety check, should not be possible.
if (mTapToFocusPosition > mText.length()) {
Log.e(LOG_TAG, "Invalid tap focus position (" + mTapToFocusPosition + " vs "
+ mText.length() + ")");
mTapToFocusPosition = mText.length();
}
Selection.setSelection((Spannable) mText, mTapToFocusPosition);
}
}
}
@Override @Override
public void onWindowFocusChanged(boolean hasWindowFocus) { public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus); super.onWindowFocusChanged(hasWindowFocus);
@@ -6717,7 +6728,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// Tapping outside stops selection mode, if any // Tapping outside stops selection mode, if any
stopTextSelectionMode(); stopTextSelectionMode();
if (mInsertionPointCursorController != null) { if (mInsertionPointCursorController != null && mText.length() > 0) {
mInsertionPointCursorController.show(); mInsertionPointCursorController.show();
} }
} }
@@ -7255,7 +7266,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
int minOffset, maxOffset; int minOffset, maxOffset;
if (mDPadCenterIsDown || mEnterKeyIsDown) { if (mContextMenuTriggeredByKey) {
minOffset = getSelectionStart(); minOffset = getSelectionStart();
maxOffset = getSelectionEnd(); maxOffset = getSelectionEnd();
} else { } else {
@@ -7286,6 +7297,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} }
private String getWordForDictionary() { private String getWordForDictionary() {
if (!mContextMenuTriggeredByKey) {
moveCursorToLastTapPosition();
}
long wordLimits = getWordLimitsAt(getSelectionStart()); long wordLimits = getWordLimitsAt(getSelectionStart());
if (wordLimits >= 0) { if (wordLimits >= 0) {
int start = extractRangeStartFromLong(wordLimits); int start = extractRangeStartFromLong(wordLimits);
@@ -7336,6 +7350,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
protected void onCreateContextMenu(ContextMenu menu) { protected void onCreateContextMenu(ContextMenu menu) {
super.onCreateContextMenu(menu); super.onCreateContextMenu(menu);
boolean added = false; boolean added = false;
mContextMenuTriggeredByKey = mDPadCenterIsDown || mEnterKeyIsDown;
// Problem with context menu on long press: the menu appears while the key in down and when
// the key is released, the view does not receive the key_up event. This ensures that the
// state is reset whenever the context menu action is displayed.
// mContextMenuTriggeredByKey saved that state so that it is available in
// onTextContextMenuItem. We cannot simply clear these flags in onTextContextMenuItem since
// it may not be called (if the user/ discards the context menu with the back key).
mDPadCenterIsDown = mEnterKeyIsDown = false;
if (mIsInTextSelectionMode) { if (mIsInTextSelectionMode) {
MenuHandler handler = new MenuHandler(); MenuHandler handler = new MenuHandler();
@@ -7361,21 +7383,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
added = true; added = true;
} }
} else { } else {
/*
if (!isFocused()) {
if (isFocusable() && mInput != null) {
if (canCopy()) {
MenuHandler handler = new MenuHandler();
menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
setOnMenuItemClickListener(handler).
setAlphabeticShortcut('c');
menu.setHeaderTitle(com.android.internal.R.string.editTextMenuTitle);
}
}
//return;
}
*/
MenuHandler handler = new MenuHandler(); MenuHandler handler = new MenuHandler();
if (canSelectText()) { if (canSelectText()) {
@@ -7529,7 +7536,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
case ID_ADD_TO_DICTIONARY: case ID_ADD_TO_DICTIONARY:
String word = getWordForDictionary(); String word = getWordForDictionary();
if (word != null) { if (word != null) {
Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT"); Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT");
i.putExtra("word", word); i.putExtra("word", word);
@@ -8013,6 +8019,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
SelectionModifierCursorController() { SelectionModifierCursorController() {
mStartHandle = new HandleView(this, HandleView.LEFT); mStartHandle = new HandleView(this, HandleView.LEFT);
mEndHandle = new HandleView(this, HandleView.RIGHT); mEndHandle = new HandleView(this, HandleView.RIGHT);
mMinTouchOffset = mMaxTouchOffset = -1;
} }
public void show() { public void show() {
@@ -8095,14 +8102,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} }
public boolean onTouchEvent(MotionEvent event) { public boolean onTouchEvent(MotionEvent event) {
if (isFocused() && isTextEditable()) { // This is done even when the View does not have focus, so that long presses can start
// selection and tap can move cursor from this tap position.
if (isTextEditable()) {
switch (event.getActionMasked()) { switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
final int x = (int) event.getX(); final int x = (int) event.getX();
final int y = (int) event.getY(); final int y = (int) event.getY();
// Remember finger down position, to be able to start selection from there // Remember finger down position, to be able to start selection from there
mMinTouchOffset = mMaxTouchOffset = mLastTouchOffset = getOffset(x, y); mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
break; break;
@@ -8259,11 +8268,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private CursorController mInsertionPointCursorController; private CursorController mInsertionPointCursorController;
private CursorController mSelectionModifierCursorController; private CursorController mSelectionModifierCursorController;
private boolean mIsInTextSelectionMode = false; private boolean mIsInTextSelectionMode = false;
private int mLastTouchOffset = -1;
// These are needed to desambiguate a long click. If the long click comes from ones of these, we // These are needed to desambiguate a long click. If the long click comes from ones of these, we
// select from the current cursor position. Otherwise, select from long pressed position. // select from the current cursor position. Otherwise, select from long pressed position.
private boolean mDPadCenterIsDown = false; private boolean mDPadCenterIsDown = false;
private boolean mEnterKeyIsDown = false; private boolean mEnterKeyIsDown = false;
private boolean mContextMenuTriggeredByKey = false;
// Created once and shared by different CursorController helper methods. // Created once and shared by different CursorController helper methods.
// Only one cursor controller is active at any time which prevent race conditions. // Only one cursor controller is active at any time which prevent race conditions.
private static Rect sCursorControllerTempRect = new Rect(); private static Rect sCursorControllerTempRect = new Rect();