in the browser, make the trackball more like a mouse

Older code treated the trackball as a four way dpad with
equivalents to moving up, down, left and right by generating
arrow key events. This change makes the trackball solely
generate mousemove events.

The old arrow keys in turn were mapped to be as close as
possible to tab-key events that moved the focus. The new
model leaves focus-changes to the DOM.

Clicking the dpad is distinguished from pressing the enter
key to be more compatible with desktop-authored web pages.
This commit is contained in:
Cary Clark
2009-05-29 11:02:22 -04:00
parent 0c66b4bcfb
commit d6982c9bb4
4 changed files with 470 additions and 563 deletions

View File

@@ -121,6 +121,10 @@ import java.util.ArrayList;
if (isPopupShowing()) {
return super.dispatchKeyEvent(event);
}
if (!mWebView.nativeCursorMatchesFocus()) {
return down ? mWebView.onKeyDown(keyCode, event) : mWebView
.onKeyUp(keyCode, event);
}
// Center key should be passed to a potential onClick
if (!down) {
mWebView.shortPressOnTextField();
@@ -128,6 +132,20 @@ import java.util.ArrayList;
// Pass to super to handle longpress.
return super.dispatchKeyEvent(event);
}
boolean isArrowKey = false;
switch(keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
if (!mWebView.nativeCursorMatchesFocus()) {
return down ? mWebView.onKeyDown(keyCode, event) : mWebView
.onKeyUp(keyCode, event);
}
isArrowKey = true;
break;
}
// Ensure there is a layout so arrow keys are handled properly.
if (getLayout() == null) {
@@ -157,22 +175,11 @@ import java.util.ArrayList;
// so do not pass down to javascript, and instead
// return true. If it is an arrow key or a delete key, we can go
// ahead and pass it down.
boolean isArrowKey;
switch(keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
isArrowKey = true;
break;
case KeyEvent.KEYCODE_ENTER:
// For multi-line text boxes, newlines will
// trigger onTextChanged for key down (which will send both
// key up and key down) but not key up.
mGotEnterDown = true;
default:
isArrowKey = false;
break;
if (KeyEvent.KEYCODE_ENTER == keyCode) {
// For multi-line text boxes, newlines will
// trigger onTextChanged for key down (which will send both
// key up and key down) but not key up.
mGotEnterDown = true;
}
if (maxedOut && !isArrowKey && keyCode != KeyEvent.KEYCODE_DEL) {
if (oldEnd == oldStart) {
@@ -216,10 +223,7 @@ import java.util.ArrayList;
return true;
}
// if it is a navigation key, pass it to WebView
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
|| keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
|| keyCode == KeyEvent.KEYCODE_DPAD_UP
|| keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
if (isArrowKey) {
// WebView check the trackballtime in onKeyDown to avoid calling
// native from both trackball and key handling. As this is called
// from TextDialog, we always want WebView to check with native.
@@ -333,6 +337,11 @@ import java.util.ArrayList;
if (event.getAction() != MotionEvent.ACTION_MOVE) {
return false;
}
// If the Cursor is not on the text input, webview should handle the
// trackball
if (!mWebView.nativeCursorMatchesFocus()) {
return mWebView.onTrackballEvent(event);
}
Spannable text = (Spannable) getText();
MovementMethod move = getMovementMethod();
if (move != null && getLayout() != null &&

File diff suppressed because it is too large Load Diff

View File

@@ -96,7 +96,7 @@ final class WebViewCore {
private int mViewportMaximumScale = 0;
private boolean mViewportUserScalable = true;
private int mRestoredScale = 100;
private int mRestoredX = 0;
private int mRestoredY = 0;
@@ -308,15 +308,15 @@ final class WebViewCore {
* Empty the picture set.
*/
private native void nativeClearContent();
/**
* Create a flat picture from the set of pictures.
*/
private native void nativeCopyContentToPicture(Picture picture);
/**
* Draw the picture set with a background color. Returns true
* if some individual picture took too long to draw and can be
* if some individual picture took too long to draw and can be
* split into parts. Called from the UI thread.
*/
private native boolean nativeDrawContent(Canvas canvas, int color);
@@ -325,13 +325,13 @@ final class WebViewCore {
* check to see if picture is blank and in progress
*/
private native boolean nativePictureReady();
/**
* Redraw a portion of the picture set. The Point wh returns the
* width and height of the overall picture.
*/
private native boolean nativeRecordContent(Region invalRegion, Point wh);
/**
* Splits slow parts of the picture set. Called from the webkit
* thread after nativeDrawContent returns true.
@@ -359,10 +359,10 @@ final class WebViewCore {
float scale, int realScreenWidth, int screenHeight);
private native int nativeGetContentMinPrefWidth();
// Start: functions that deal with text editing
private native void nativeReplaceTextfieldText(int frame, int node, int x,
int y, int oldStart, int oldEnd, String replace, int newStart,
private native void nativeReplaceTextfieldText(int frame, int node, int x,
int y, int oldStart, int oldEnd, String replace, int newStart,
int newEnd);
private native void passToJs(int frame, int node, int x, int y, int gen,
@@ -373,31 +373,31 @@ final class WebViewCore {
private native void nativeSaveDocumentState(int frame);
private native void nativeSetFinalFocus(int framePtr, int nodePtr, int x,
int y, boolean block);
private native void nativeMoveMouse(int framePtr, int nodePtr, int x,
int y);
private native void nativeSetKitFocus(int moveGeneration,
int buildGeneration, int framePtr, int nodePtr, int x, int y,
private native void nativeMoveMouseIfLatest(int moveGeneration,
int framePtr, int nodePtr, int x, int y,
boolean ignoreNullFocus);
private native String nativeRetrieveHref(int framePtr, int nodePtr);
private native void nativeTouchUp(int touchGeneration,
int buildGeneration, int framePtr, int nodePtr, int x, int y,
int size, boolean retry);
private native void nativeTouchUp(int touchGeneration,
int framePtr, int nodePtr, int x, int y,
int size);
private native boolean nativeHandleTouchEvent(int action, int x, int y);
private native void nativeUnblockFocus();
private native void nativeUpdateFrameCache();
private native void nativeSetSnapAnchor(int x, int y);
private native void nativeSnapToAnchor();
private native void nativeSetBackgroundColor(int color);
private native void nativeDumpDomTree(boolean useFile);
private native void nativeDumpRenderTree(boolean useFile);
@@ -406,7 +406,7 @@ final class WebViewCore {
/**
* Delete text from start to end in the focused textfield. If there is no
* focus, or if start == end, silently fail. If start and end are out of
* focus, or if start == end, silently fail. If start and end are out of
* order, swap them.
* @param start Beginning of selection to delete.
* @param end End of selection to delete.
@@ -424,7 +424,7 @@ final class WebViewCore {
int start, int end);
private native String nativeGetSelection(Region sel);
// Register a scheme to be treated as local scheme so that it can access
// local asset files for resources
private native void nativeRegisterURLSchemeAsLocal(String scheme);
@@ -485,7 +485,7 @@ final class WebViewCore {
CacheManager.endCacheTransaction();
CacheManager.startCacheTransaction();
sendMessageDelayed(
obtainMessage(CACHE_TICKER),
obtainMessage(CACHE_TICKER),
CACHE_TICKER_INTERVAL);
}
break;
@@ -510,19 +510,15 @@ final class WebViewCore {
}
}
static class FocusData {
FocusData() {}
FocusData(FocusData d) {
mMoveGeneration = d.mMoveGeneration;
mBuildGeneration = d.mBuildGeneration;
mFrame = d.mFrame;
mNode = d.mNode;
mX = d.mX;
mY = d.mY;
mIgnoreNullFocus = d.mIgnoreNullFocus;
static class CursorData {
CursorData() {}
CursorData(int frame, int node, int x, int y) {
mFrame = frame;
mNode = node;
mX = x;
mY = y;
}
int mMoveGeneration;
int mBuildGeneration;
int mFrame;
int mNode;
int mX;
@@ -532,13 +528,11 @@ final class WebViewCore {
static class TouchUpData {
int mMoveGeneration;
int mBuildGeneration;
int mFrame;
int mNode;
int mX;
int mY;
int mSize;
boolean mRetry;
}
static class TouchEventData {
@@ -583,8 +577,8 @@ final class WebViewCore {
"POST_URL", // = 132;
"SPLIT_PICTURE_SET", // = 133;
"CLEAR_CONTENT", // = 134;
"SET_FINAL_FOCUS", // = 135;
"SET_KIT_FOCUS", // = 136;
"SET_MOVE_MOUSE", // = 135;
"SET_MOVE_MOUSE_IF_LATEST", // = 136;
"REQUEST_FOCUS_HREF", // = 137;
"ADD_JS_INTERFACE", // = 138;
"LOAD_DATA", // = 139;
@@ -632,10 +626,10 @@ final class WebViewCore {
static final int POST_URL = 132;
static final int SPLIT_PICTURE_SET = 133;
static final int CLEAR_CONTENT = 134;
// UI nav messages
static final int SET_FINAL_FOCUS = 135;
static final int SET_KIT_FOCUS = 136;
static final int SET_MOVE_MOUSE = 135;
static final int SET_MOVE_MOUSE_IF_LATEST = 136;
static final int REQUEST_FOCUS_HREF = 137;
static final int ADD_JS_INTERFACE = 138;
static final int LOAD_DATA = 139;
@@ -668,7 +662,7 @@ final class WebViewCore {
// private message ids
private static final int DESTROY = 200;
// flag values passed to message SET_FINAL_FOCUS
static final int NO_FOCUS_CHANGE_BLOCK = 0;
static final int BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP = 1;
@@ -701,7 +695,7 @@ final class WebViewCore {
@Override
public void handleMessage(Message msg) {
if (LOGV_ENABLED) {
Log.v(LOGTAG, msg.what < LOAD_URL || msg.what
Log.v(LOGTAG, msg.what < LOAD_URL || msg.what
> SET_ACTIVE ? Integer.toString(msg.what)
: HandlerDebugString[msg.what - LOAD_URL]);
}
@@ -744,7 +738,7 @@ final class WebViewCore {
* we automatically add the scheme of the
* baseUrl for local access as long as it is
* not http(s)/ftp(s)/about/javascript
*/
*/
String scheme = baseUrl.substring(0, i);
if (!scheme.startsWith("http") &&
!scheme.startsWith("ftp") &&
@@ -762,9 +756,9 @@ final class WebViewCore {
break;
case STOP_LOADING:
// If the WebCore has committed the load, but not
// finished the first layout yet, we need to set
// first layout done to trigger the interpreted side sync
// If the WebCore has committed the load, but not
// finished the first layout yet, we need to set
// first layout done to trigger the interpreted side sync
// up with native side
if (mBrowserFrame.committed()
&& !mBrowserFrame.firstLayoutDone()) {
@@ -800,7 +794,7 @@ final class WebViewCore {
// (inv-zoom)
nativeSetScrollOffset(msg.arg1, msg.arg2);
break;
case SET_GLOBAL_BOUNDS:
Rect r = (Rect) msg.obj;
nativeSetGlobalBounds(r.left, r.top, r.width(),
@@ -811,7 +805,7 @@ final class WebViewCore {
// If it is a standard load and the load is not
// committed yet, we interpret BACK as RELOAD
if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
(mBrowserFrame.loadType() ==
(mBrowserFrame.loadType() ==
BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
mBrowserFrame.reload(true);
} else {
@@ -877,13 +871,13 @@ final class WebViewCore {
close(mBrowserFrame.mNativeFrame);
break;
case REPLACE_TEXT:
case REPLACE_TEXT:
HashMap jMap = (HashMap) msg.obj;
FocusData fData = (FocusData) jMap.get("focusData");
CursorData fData = (CursorData) jMap.get("focusData");
String replace = (String) jMap.get("replace");
int newStart =
int newStart =
((Integer) jMap.get("start")).intValue();
int newEnd =
int newEnd =
((Integer) jMap.get("end")).intValue();
nativeReplaceTextfieldText(fData.mFrame,
fData.mNode, fData.mX, fData.mY, msg.arg1,
@@ -892,7 +886,7 @@ final class WebViewCore {
case PASS_TO_JS: {
HashMap jsMap = (HashMap) msg.obj;
FocusData fDat = (FocusData) jsMap.get("focusData");
CursorData fDat = (CursorData) jsMap.get("focusData");
KeyEvent evt = (KeyEvent) jsMap.get("event");
int keyCode = evt.getKeyCode();
int keyValue = evt.getUnicodeChar();
@@ -909,7 +903,7 @@ final class WebViewCore {
}
case SAVE_DOCUMENT_STATE: {
FocusData fDat = (FocusData) msg.obj;
CursorData fDat = (CursorData) msg.obj;
nativeSaveDocumentState(fDat.mFrame);
break;
}
@@ -922,11 +916,9 @@ final class WebViewCore {
case TOUCH_UP:
TouchUpData touchUpData = (TouchUpData) msg.obj;
nativeTouchUp(touchUpData.mMoveGeneration,
touchUpData.mBuildGeneration,
touchUpData.mFrame, touchUpData.mNode,
touchUpData.mX, touchUpData.mY,
touchUpData.mSize,
touchUpData.mRetry);
touchUpData.mX, touchUpData.mY,
touchUpData.mSize);
break;
case TOUCH_EVENT: {
@@ -961,22 +953,20 @@ final class WebViewCore {
mBrowserFrame.documentAsText((Message) msg.obj);
break;
case SET_FINAL_FOCUS:
FocusData finalData = (FocusData) msg.obj;
nativeSetFinalFocus(finalData.mFrame,
finalData.mNode, finalData.mX,
finalData.mY, msg.arg1
!= EventHub.NO_FOCUS_CHANGE_BLOCK);
case SET_MOVE_MOUSE:
CursorData finalData = (CursorData) msg.obj;
nativeMoveMouse(finalData.mFrame,
finalData.mNode, finalData.mX,
finalData.mY);
break;
case UNBLOCK_FOCUS:
nativeUnblockFocus();
break;
case SET_KIT_FOCUS:
FocusData focusData = (FocusData) msg.obj;
nativeSetKitFocus(focusData.mMoveGeneration,
focusData.mBuildGeneration,
case SET_MOVE_MOUSE_IF_LATEST:
CursorData focusData = (CursorData) msg.obj;
nativeMoveMouseIfLatest(focusData.mMoveGeneration,
focusData.mFrame, focusData.mNode,
focusData.mX, focusData.mY,
focusData.mIgnoreNullFocus);
@@ -989,7 +979,7 @@ final class WebViewCore {
hrefMsg.sendToTarget();
break;
}
case UPDATE_CACHE_AND_TEXT_ENTRY:
nativeUpdateFrameCache();
// FIXME: this should provide a minimal rectangle
@@ -1009,21 +999,21 @@ final class WebViewCore {
case SET_SNAP_ANCHOR:
nativeSetSnapAnchor(msg.arg1, msg.arg2);
break;
case DELETE_SELECTION:
FocusData delData = (FocusData) msg.obj;
CursorData delData = (CursorData) msg.obj;
nativeDeleteSelection(delData.mFrame,
delData.mNode, delData.mX,
delData.mNode, delData.mX,
delData.mY, msg.arg1, msg.arg2);
break;
case SET_SELECTION:
FocusData selData = (FocusData) msg.obj;
CursorData selData = (CursorData) msg.obj;
nativeSetSelection(selData.mFrame,
selData.mNode, selData.mX,
selData.mNode, selData.mX,
selData.mY, msg.arg1, msg.arg2);
break;
case LISTBOX_CHOICES:
SparseBooleanArray choices = (SparseBooleanArray)
msg.obj;
@@ -1032,18 +1022,18 @@ final class WebViewCore {
for (int c = 0; c < choicesSize; c++) {
choicesArray[c] = choices.get(c);
}
nativeSendListBoxChoices(choicesArray,
nativeSendListBoxChoices(choicesArray,
choicesSize);
break;
case SINGLE_LISTBOX_CHOICE:
nativeSendListBoxChoice(msg.arg1);
break;
case SET_BACKGROUND_COLOR:
nativeSetBackgroundColor(msg.arg1);
break;
case GET_SELECTION:
String str = nativeGetSelection((Region) msg.obj);
Message.obtain(mWebView.mPrivateHandler
@@ -1072,7 +1062,7 @@ final class WebViewCore {
nativeSplitContent();
mSplitPictureIsScheduled = false;
break;
case CLEAR_CONTENT:
// Clear the view so that onDraw() will draw nothing
// but white background
@@ -1282,7 +1272,7 @@ final class WebViewCore {
&& (w < mViewportWidth || mViewportWidth == -1)) {
int width = mViewportWidth;
if (mViewportWidth == -1) {
if (mSettings.getLayoutAlgorithm() ==
if (mSettings.getLayoutAlgorithm() ==
WebSettings.LayoutAlgorithm.NORMAL) {
width = WebView.ZOOM_OUT_WIDTH;
} else {
@@ -1329,7 +1319,7 @@ final class WebViewCore {
// Used to avoid posting more than one draw message.
private boolean mDrawIsScheduled;
// Used to avoid posting more than one split picture message.
private boolean mSplitPictureIsScheduled;
@@ -1338,7 +1328,7 @@ final class WebViewCore {
// Used to end scale+scroll mode, accessed by both threads
boolean mEndScaleZoom = false;
public class DrawData {
public DrawData() {
mInvalRegion = new Region();
@@ -1348,12 +1338,12 @@ final class WebViewCore {
public Point mViewPoint;
public Point mWidthHeight;
}
private void webkitDraw() {
mDrawIsScheduled = false;
DrawData draw = new DrawData();
if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw start");
if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
== false) {
if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw abort");
return;
@@ -1427,9 +1417,9 @@ final class WebViewCore {
sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
.obtainMessage(WebCoreThread.REDUCE_PRIORITY));
// Note: there is one possible failure mode. If pauseUpdate() is called
// from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
// of the queue and about to be executed. mDrawIsScheduled may be set to
// false in webkitDraw(). So update won't be blocked. But at least the
// from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
// of the queue and about to be executed. mDrawIsScheduled may be set to
// false in webkitDraw(). So update won't be blocked. But at least the
// webcore thread priority is still lowered.
if (core != null) {
synchronized (core) {
@@ -1498,7 +1488,7 @@ final class WebViewCore {
mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
}
}
// called by JNI
private void contentScrollBy(int dx, int dy, boolean animate) {
if (!mBrowserFrame.firstLayoutDone()) {
@@ -1573,14 +1563,6 @@ final class WebViewCore {
}
}
// called by JNI
private void sendNotifyFocusSet() {
if (mWebView != null) {
Message.obtain(mWebView.mPrivateHandler,
WebView.NOTIFY_FOCUS_SET_MSG_ID).sendToTarget();
}
}
// called by JNI
private void sendNotifyProgressFinished() {
sendUpdateTextEntry();
@@ -1617,12 +1599,12 @@ final class WebViewCore {
}
private native void setViewportSettingsFromNative();
// called by JNI
private void didFirstLayout(boolean standardLoad) {
// Trick to ensure that the Picture has the exact height for the content
// by forcing to layout with 0 height after the page is ready, which is
// indicated by didFirstLayout. This is essential to get rid of the
// indicated by didFirstLayout. This is essential to get rid of the
// white space in the GMail which uses WebView for message view.
if (mWebView != null && mWebView.mHeightCanMeasure) {
mWebView.mLastHeightSent = 0;
@@ -1667,7 +1649,7 @@ final class WebViewCore {
mViewportMaximumScale = mViewportInitialScale;
} else if (mViewportInitialScale == 0) {
mViewportInitialScale = mViewportMaximumScale;
}
}
}
if (mViewportWidth < 0 && mViewportInitialScale == 100) {
mViewportWidth = 0;
@@ -1737,7 +1719,7 @@ final class WebViewCore {
String text, int textGeneration) {
if (mWebView != null) {
Message msg = Message.obtain(mWebView.mPrivateHandler,
WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
textGeneration, text);
msg.getData().putBoolean("password", changeToPassword);
msg.sendToTarget();
@@ -1763,7 +1745,7 @@ final class WebViewCore {
if (mWebView != null) {
mWebView.requestListBox(array, enabledArray, selection);
}
}
private native void nativePause();

View File

@@ -503,12 +503,11 @@ android.webkit.WebSyncManager
android.webkit.WebSyncManager$SyncHandler
android.webkit.WebView
android.webkit.WebView$ExtendedZoomControls
android.webkit.WebView$FocusNode
android.webkit.WebView$PrivateHandler
android.webkit.WebViewCore
android.webkit.WebViewCore$CursorData
android.webkit.WebViewCore$EventHub
android.webkit.WebViewCore$EventHub$1
android.webkit.WebViewCore$FocusData
android.webkit.WebViewCore$WebCoreThread
android.webkit.WebViewCore$WebCoreThread$1
android.webkit.WebViewDatabase