Text Selection: smart word detection. DO NOT MERGE.
Selecting between words selects the spaces instead of the closest word, facilitating paste insertion vs word replacement. Pasting adds/removes space before and after if needed so that pasted text is not appended to existing text. Cut and copy not available when selection is empty. Change-Id: I331413bdef341a4eceffe76fa2957f388d540b6e
This commit is contained in:
@@ -5817,7 +5817,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
* Return true iff there is a selection inside this text view.
|
* Return true iff there is a selection inside this text view.
|
||||||
*/
|
*/
|
||||||
public boolean hasSelection() {
|
public boolean hasSelection() {
|
||||||
return getSelectionStart() != getSelectionEnd();
|
final int selectionStart = getSelectionStart();
|
||||||
|
final int selectionEnd = getSelectionEnd();
|
||||||
|
|
||||||
|
return selectionStart >= 0 && selectionStart != selectionEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7005,7 +7008,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mText.length() > 0 && getSelectionStart() >= 0) {
|
if (mText.length() > 0 && hasSelection()) {
|
||||||
if (mText instanceof Editable && mInput != null) {
|
if (mText instanceof Editable && mInput != null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -7019,7 +7022,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mText.length() > 0 && getSelectionStart() >= 0) {
|
if (mText.length() > 0 && hasSelection()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7140,6 +7143,49 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
int minOffset = selectionModifierCursorController.getMinTouchOffset();
|
int minOffset = selectionModifierCursorController.getMinTouchOffset();
|
||||||
int maxOffset = selectionModifierCursorController.getMaxTouchOffset();
|
int maxOffset = selectionModifierCursorController.getMaxTouchOffset();
|
||||||
|
|
||||||
|
if (minOffset == maxOffset) {
|
||||||
|
int offset = Math.max(0, Math.min(minOffset, mTransformed.length()));
|
||||||
|
|
||||||
|
// Tolerance, number of charaters around tapped position
|
||||||
|
final int range = 1;
|
||||||
|
final int max = mTransformed.length() - 1;
|
||||||
|
|
||||||
|
// 'Smart' word selection: detect position between words
|
||||||
|
for (int i = -range; i <= range; i++) {
|
||||||
|
int index = offset + i;
|
||||||
|
if (index >= 0 && index <= max) {
|
||||||
|
if (Character.isSpaceChar(mTransformed.charAt(index))) {
|
||||||
|
// Select current space
|
||||||
|
selectionStart = index;
|
||||||
|
selectionEnd = selectionStart + 1;
|
||||||
|
|
||||||
|
// Extend selection to maximum space range
|
||||||
|
while (selectionStart > 0 &&
|
||||||
|
Character.isSpaceChar(mTransformed.charAt(selectionStart - 1))) {
|
||||||
|
selectionStart--;
|
||||||
|
}
|
||||||
|
while (selectionEnd < max &&
|
||||||
|
Character.isSpaceChar(mTransformed.charAt(selectionEnd))) {
|
||||||
|
selectionEnd++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Selection.setSelection((Spannable) mText, selectionStart, selectionEnd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'Smart' word selection: detect position at beginning or end of text.
|
||||||
|
if (offset <= range) {
|
||||||
|
Selection.setSelection((Spannable) mText, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (offset >= (max - range)) {
|
||||||
|
Selection.setSelection((Spannable) mText, max + 1, max + 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
long wordLimits = getWordLimitsAt(minOffset);
|
long wordLimits = getWordLimitsAt(minOffset);
|
||||||
if (wordLimits >= 0) {
|
if (wordLimits >= 0) {
|
||||||
selectionStart = (int) (wordLimits >>> 32);
|
selectionStart = (int) (wordLimits >>> 32);
|
||||||
@@ -7220,21 +7266,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
|
menu.add(0, ID_CUT, 0, com.android.internal.R.string.cut).
|
||||||
setOnMenuItemClickListener(handler).
|
setOnMenuItemClickListener(handler).
|
||||||
setAlphabeticShortcut('x');
|
setAlphabeticShortcut('x');
|
||||||
|
added = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canCopy()) {
|
if (canCopy()) {
|
||||||
menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
|
menu.add(0, ID_COPY, 0, com.android.internal.R.string.copy).
|
||||||
setOnMenuItemClickListener(handler).
|
setOnMenuItemClickListener(handler).
|
||||||
setAlphabeticShortcut('c');
|
setAlphabeticShortcut('c');
|
||||||
|
added = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canPaste()) {
|
if (canPaste()) {
|
||||||
menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
|
menu.add(0, ID_PASTE, 0, com.android.internal.R.string.paste).
|
||||||
setOnMenuItemClickListener(handler).
|
setOnMenuItemClickListener(handler).
|
||||||
setAlphabeticShortcut('v');
|
setAlphabeticShortcut('v');
|
||||||
|
added = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
added = true;
|
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
if (!isFocused()) {
|
if (!isFocused()) {
|
||||||
@@ -7313,11 +7360,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
|
|
||||||
private boolean textIsOnlySpaces() {
|
private boolean textIsOnlySpaces() {
|
||||||
final int length = mTransformed.length();
|
final int length = mTransformed.length();
|
||||||
for (int i=0; i<length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
final char c = mTransformed.charAt(i);
|
if (!Character.isSpaceChar(mTransformed.charAt(i))) {
|
||||||
final int type = Character.getType(c);
|
|
||||||
if (type != Character.SPACE_SEPARATOR)
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -7350,7 +7396,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
/**
|
/**
|
||||||
* Called when a context menu option for the text view is selected. Currently
|
* Called when a context menu option for the text view is selected. Currently
|
||||||
* this will be one of: {@link android.R.id#selectAll},
|
* this will be one of: {@link android.R.id#selectAll},
|
||||||
* {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText},
|
* {@link android.R.id#startSelectingText},
|
||||||
* {@link android.R.id#cut}, {@link android.R.id#copy},
|
* {@link android.R.id#cut}, {@link android.R.id#copy},
|
||||||
* {@link android.R.id#paste}, {@link android.R.id#copyUrl},
|
* {@link android.R.id#paste}, {@link android.R.id#copyUrl},
|
||||||
* or {@link android.R.id#switchInputMethod}.
|
* or {@link android.R.id#switchInputMethod}.
|
||||||
@@ -7394,7 +7440,37 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
case ID_PASTE:
|
case ID_PASTE:
|
||||||
CharSequence paste = clip.getText();
|
CharSequence paste = clip.getText();
|
||||||
|
|
||||||
if (paste != null) {
|
if (paste != null && paste.length() > 0) {
|
||||||
|
// Paste adds/removes spaces before or after insertion as needed.
|
||||||
|
|
||||||
|
if (Character.isSpaceChar(paste.charAt(0))) {
|
||||||
|
if (min > 0 && Character.isSpaceChar(mTransformed.charAt(min - 1))) {
|
||||||
|
// Two spaces at beginning of paste: remove one
|
||||||
|
((Editable) mText).replace(min - 1, min, "");
|
||||||
|
min = min - 1;
|
||||||
|
max = max - 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (min > 0 && !Character.isSpaceChar(mTransformed.charAt(min - 1))) {
|
||||||
|
// No space at beginning of paste: add one
|
||||||
|
((Editable) mText).replace(min, min, " ");
|
||||||
|
min = min + 1;
|
||||||
|
max = max + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Character.isSpaceChar(paste.charAt(paste.length() - 1))) {
|
||||||
|
if (max < mText.length() && Character.isSpaceChar(mTransformed.charAt(max))) {
|
||||||
|
// Two spaces at end of paste: remove one
|
||||||
|
((Editable) mText).replace(max, max + 1, "");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (max < mText.length() && !Character.isSpaceChar(mTransformed.charAt(max))) {
|
||||||
|
// No space at end of paste: add one
|
||||||
|
((Editable) mText).replace(max, max, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Selection.setSelection((Spannable) mText, max);
|
Selection.setSelection((Spannable) mText, max);
|
||||||
((Editable) mText).replace(min, max, paste);
|
((Editable) mText).replace(min, max, paste);
|
||||||
stopTextSelectionMode();
|
stopTextSelectionMode();
|
||||||
|
|||||||
Reference in New Issue
Block a user