am bb107bd6: Many fixes to text input in the browser, particularly when moving to a new field.

Merge commit 'bb107bd6f7981cd2b2e31a0afc6f6bf2a6d5118f' into eclair-mr2-plus-aosp

* commit 'bb107bd6f7981cd2b2e31a0afc6f6bf2a6d5118f':
  Many fixes to text input in the browser, particularly when moving to a new field.
This commit is contained in:
Leon Scroggins
2009-10-27 13:20:33 -07:00
committed by Android Git Automerger
2 changed files with 86 additions and 85 deletions

View File

@ -84,14 +84,24 @@ import java.util.ArrayList;
// True if the most recent drag event has caused either the TextView to // True if the most recent drag event has caused either the TextView to
// scroll or the web page to scroll. Gets reset after a touch down. // scroll or the web page to scroll. Gets reset after a touch down.
private boolean mScrolled; private boolean mScrolled;
// Gets set to true when the the IME jumps to the next textfield. When this // Gets set to true any time the WebTextView has focus, but the navigation
// happens, the next time the user hits a key it is okay for the focus // cache does not yet know that the focus has been changed. This happens
// pointer to not match the WebTextView's node pointer // if the user presses "Next", if the user moves the cursor to a textfield
// and starts typing or clicks the trackball/center key, and when the user
// touches a textfield.
boolean mOkayForFocusNotToMatch; boolean mOkayForFocusNotToMatch;
boolean mResendKeyDown;
// Whether or not a selection change was generated from webkit. If it was, // Whether or not a selection change was generated from webkit. If it was,
// we do not need to pass the selection back to webkit. // we do not need to pass the selection back to webkit.
private boolean mFromWebKit; private boolean mFromWebKit;
// Whether or not a selection change was generated from the WebTextView
// gaining focus. If it is, we do not want to pass it to webkit. This
// selection comes from the MovementMethod, but we behave differently. If
// WebTextView gained focus from a touch, webkit will determine the
// selection.
private boolean mFromFocusChange;
// Whether or not a selection change was generated from setInputType. We
// do not want to pass this change to webkit.
private boolean mFromSetInputType;
private boolean mGotTouchDown; private boolean mGotTouchDown;
private boolean mInSetTextAndKeepSelection; private boolean mInSetTextAndKeepSelection;
// Array to store the final character added in onTextChanged, so that its // Array to store the final character added in onTextChanged, so that its
@ -137,22 +147,23 @@ import java.util.ArrayList;
isArrowKey = true; isArrowKey = true;
break; break;
} }
if (!isArrowKey && !mOkayForFocusNotToMatch && !mResendKeyDown
&& mWebView.nativeFocusNodePointer() != mNodePointer) { if (down) {
if (mWebView.nativeFocusNodePointer() != 0) { if (mOkayForFocusNotToMatch) {
if (mWebView.nativeFocusNodePointer() == mNodePointer) {
mOkayForFocusNotToMatch = false;
}
} else if (mWebView.nativeFocusNodePointer() != mNodePointer
&& !isArrowKey) {
mWebView.nativeClearCursor(); mWebView.nativeClearCursor();
// Do not call remove() here, which hides the soft keyboard. If
// the soft keyboard is being displayed, the user will still want
// it there.
mWebView.removeView(this);
mWebView.requestFocus();
return mWebView.dispatchKeyEvent(event);
} }
// Do not call remove() here, which hides the soft keyboard. If
// the soft keyboard is being displayed, the user will still want
// it there.
mWebView.removeView(this);
mWebView.requestFocus();
return mWebView.dispatchKeyEvent(event);
} }
// After a jump to next textfield and the first key press, the cursor
// and focus will once again match, so reset this value.
mOkayForFocusNotToMatch = false;
mResendKeyDown = false;
Spannable text = (Spannable) getText(); Spannable text = (Spannable) getText();
int oldLength = text.length(); int oldLength = text.length();
// Normally the delete key's dom events are sent via onTextChanged. // Normally the delete key's dom events are sent via onTextChanged.
@ -306,15 +317,19 @@ import java.util.ArrayList;
public void onEditorAction(int actionCode) { public void onEditorAction(int actionCode) {
switch (actionCode) { switch (actionCode) {
case EditorInfo.IME_ACTION_NEXT: case EditorInfo.IME_ACTION_NEXT:
mWebView.nativeMoveCursorToNextTextInput();
// Preemptively rebuild the WebTextView, so that the action will
// be set properly.
mWebView.rebuildWebTextView();
// Since the cursor will no longer be in the same place as the // Since the cursor will no longer be in the same place as the
// focus, set the focus controller back to inactive // focus, set the focus controller back to inactive
mWebView.setFocusControllerInactive(); mWebView.setFocusControllerInactive();
mWebView.invalidate(); mWebView.nativeMoveCursorToNextTextInput();
mOkayForFocusNotToMatch = true; mOkayForFocusNotToMatch = true;
// Pass the click to set the focus to the textfield which will now
// have the cursor.
mWebView.centerKeyPressOnTextField();
// Preemptively rebuild the WebTextView, so that the action will
// be set properly.
mWebView.rebuildWebTextView();
setDefaultSelection();
mWebView.invalidate();
break; break;
case EditorInfo.IME_ACTION_DONE: case EditorInfo.IME_ACTION_DONE:
super.onEditorAction(actionCode); super.onEditorAction(actionCode);
@ -333,6 +348,14 @@ import java.util.ArrayList;
} }
} }
@Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
mFromFocusChange = true;
super.onFocusChanged(focused, direction, previouslyFocusedRect);
mFromFocusChange = false;
}
@Override @Override
protected void onSelectionChanged(int selStart, int selEnd) { protected void onSelectionChanged(int selStart, int selEnd) {
// This code is copied from TextView.onDraw(). That code does not get // This code is copied from TextView.onDraw(). That code does not get
@ -345,7 +368,8 @@ import java.util.ArrayList;
int candEnd = EditableInputConnection.getComposingSpanEnd(sp); int candEnd = EditableInputConnection.getComposingSpanEnd(sp);
imm.updateSelection(this, selStart, selEnd, candStart, candEnd); imm.updateSelection(this, selStart, selEnd, candStart, candEnd);
} }
if (!mFromWebKit && mWebView != null) { if (!mFromWebKit && !mFromFocusChange && !mFromSetInputType
&& mWebView != null) {
if (DebugFlags.WEB_TEXT_VIEW) { if (DebugFlags.WEB_TEXT_VIEW) {
Log.v(LOGTAG, "onSelectionChanged selStart=" + selStart Log.v(LOGTAG, "onSelectionChanged selStart=" + selStart
+ " selEnd=" + selEnd); + " selEnd=" + selEnd);
@ -593,6 +617,17 @@ import java.util.ArrayList;
} }
} }
/**
* Sets the selection when the user clicks on a textfield or textarea with
* the trackball or center key, or starts typing into it without clicking on
* it.
*/
/* package */ void setDefaultSelection() {
Spannable text = (Spannable) getText();
int selection = mSingle ? text.length() : 0;
Selection. setSelection(text, selection, selection);
}
/** /**
* Determine whether to use the system-wide password disguising method, * Determine whether to use the system-wide password disguising method,
* or to use none. * or to use none.
@ -663,6 +698,13 @@ import java.util.ArrayList;
setTextColor(Color.BLACK); setTextColor(Color.BLACK);
} }
@Override
public void setInputType(int type) {
mFromSetInputType = true;
super.setInputType(type);
mFromSetInputType = false;
}
/* package */ void setMaxLength(int maxLength) { /* package */ void setMaxLength(int maxLength) {
mMaxLength = maxLength; mMaxLength = maxLength;
if (-1 == maxLength) { if (-1 == maxLength) {
@ -763,32 +805,6 @@ import java.util.ArrayList;
setInputType(inputType); setInputType(inputType);
} }
/**
* Set the text for this WebTextView, and set the selection to (start, end)
* @param text Text to go into this WebTextView.
* @param start Beginning of the selection.
* @param end End of the selection.
*/
/* package */ void setText(CharSequence text, int start, int end) {
mPreChange = text.toString();
setText(text);
Spannable span = (Spannable) getText();
int length = span.length();
if (end > length) {
end = length;
}
if (start < 0) {
start = 0;
} else if (start > length) {
start = length;
}
if (DebugFlags.WEB_TEXT_VIEW) {
Log.v(LOGTAG, "setText start=" + start
+ " end=" + end);
}
Selection.setSelection(span, start, end);
}
/** /**
* Set the text to the new string, but use the old selection, making sure * Set the text to the new string, but use the old selection, making sure
* to keep it within the new string. * to keep it within the new string.

View File

@ -3095,6 +3095,16 @@ public class WebView extends AbsoluteLayout
imm.hideSoftInputFromWindow(this.getWindowToken(), 0); imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
} }
/**
* Only for calling from JNI. Allows a click on an unfocused textfield to
* put the textfield in focus.
*/
private void setOkayNotToMatch() {
if (inEditingMode()) {
mWebTextView.mOkayForFocusNotToMatch = true;
}
}
/* /*
* This method checks the current focus and cursor and potentially rebuilds * This method checks the current focus and cursor and potentially rebuilds
* mWebTextView to have the appropriate properties, such as password, * mWebTextView to have the appropriate properties, such as password,
@ -3150,6 +3160,7 @@ public class WebView extends AbsoluteLayout
&& nativeTextGeneration() == mTextGeneration) { && nativeTextGeneration() == mTextGeneration) {
mWebTextView.setTextAndKeepSelection(text); mWebTextView.setTextAndKeepSelection(text);
} else { } else {
// FIXME: Determine whether this is necessary.
Selection.setSelection(spannable, start, end); Selection.setSelection(spannable, start, end);
} }
} else { } else {
@ -3182,34 +3193,12 @@ public class WebView extends AbsoluteLayout
mWebTextView.setSingleLine(isTextField); mWebTextView.setSingleLine(isTextField);
mWebTextView.setInPassword(nativeFocusCandidateIsPassword()); mWebTextView.setInPassword(nativeFocusCandidateIsPassword());
if (null == text) { if (null == text) {
mWebTextView.setText("", 0, 0);
if (DebugFlags.WEB_VIEW) { if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "rebuildWebTextView null == text"); Log.v(LOGTAG, "rebuildWebTextView null == text");
} }
} else { text = "";
// Change to true to enable the old style behavior, where
// entering a textfield/textarea always set the selection to the
// whole field. This was desirable for the case where the user
// intends to scroll past the field using the trackball.
// However, it causes a problem when replying to emails - the
// user expects the cursor to be at the beginning of the
// textarea. Testing out a new behavior, where textfields set
// selection at the end, and textareas at the beginning.
if (false) {
mWebTextView.setText(text, 0, text.length());
} else if (isTextField) {
int length = text.length();
mWebTextView.setText(text, length, length);
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "rebuildWebTextView length=" + length);
}
} else {
mWebTextView.setText(text, 0, 0);
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "rebuildWebTextView !isTextField");
}
}
} }
mWebTextView.setTextAndKeepSelection(text);
mWebTextView.requestFocus(); mWebTextView.requestFocus();
} }
} }
@ -3365,15 +3354,16 @@ public class WebView extends AbsoluteLayout
rebuildWebTextView(); rebuildWebTextView();
// Now we need to pass the event to it // Now we need to pass the event to it
if (inEditingMode()) { if (inEditingMode()) {
mWebTextView.mResendKeyDown = true; mWebTextView.setDefaultSelection();
return mWebTextView.onKeyDown(keyCode, event); mWebTextView.mOkayForFocusNotToMatch = true;
return mWebTextView.dispatchKeyEvent(event);
} }
} else if (nativeHasFocusNode()) { } else if (nativeHasFocusNode()) {
// In this case, the cursor is not on a text input, but the focus // In this case, the cursor is not on a text input, but the focus
// might be. Check it, and if so, hand over to the WebTextView. // might be. Check it, and if so, hand over to the WebTextView.
rebuildWebTextView(); rebuildWebTextView();
if (inEditingMode()) { if (inEditingMode()) {
return mWebTextView.onKeyDown(keyCode, event); return mWebTextView.dispatchKeyEvent(event);
} }
} }
@ -3460,6 +3450,10 @@ public class WebView extends AbsoluteLayout
if (nativeCursorIsTextInput()) { if (nativeCursorIsTextInput()) {
rebuildWebTextView(); rebuildWebTextView();
centerKeyPressOnTextField(); centerKeyPressOnTextField();
if (inEditingMode()) {
mWebTextView.setDefaultSelection();
mWebTextView.mOkayForFocusNotToMatch = true;
}
return true; return true;
} }
nativeSetFollowedLink(true); nativeSetFollowedLink(true);
@ -4937,15 +4931,6 @@ public class WebView extends AbsoluteLayout
} }
/* package */ void passToJavaScript(String currentText, KeyEvent event) { /* package */ void passToJavaScript(String currentText, KeyEvent event) {
if (nativeCursorWantsKeyEvents() && !nativeCursorMatchesFocus()) {
mWebViewCore.sendMessage(EventHub.CLICK);
if (mWebTextView.mOkayForFocusNotToMatch) {
int select = nativeFocusCandidateIsTextField() ?
nativeFocusCandidateMaxLength() : 0;
setSelection(select, select);
mWebTextView.mOkayForFocusNotToMatch = false; // only once
}
}
WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData(); WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
arg.mEvent = event; arg.mEvent = event;
arg.mCurrentText = currentText; arg.mCurrentText = currentText;