зеркало из https://github.com/mozilla/gecko-dev.git
Use a state machine in the PanZoomController to fix some weird touch behaviours.
This commit is contained in:
Родитель
a99dc9d139
Коммит
ad5c52346c
|
@ -452,13 +452,18 @@ public class GeckoAppShell
|
|||
|
||||
layerController.setOnTouchListener(new View.OnTouchListener() {
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
float origX = event.getX();
|
||||
float origY = event.getY();
|
||||
/* Transform the point to the layer offset. */
|
||||
IntPoint eventPoint = new IntPoint((int)Math.round(event.getX()),
|
||||
(int)Math.round(event.getY()));
|
||||
IntPoint eventPoint = new IntPoint((int)Math.round(origX),
|
||||
(int)Math.round(origY));
|
||||
IntPoint geckoPoint = layerController.convertViewPointToLayerPoint(eventPoint);
|
||||
event.setLocation(geckoPoint.x, geckoPoint.y);
|
||||
|
||||
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
|
||||
|
||||
/* Restore the view coordinates in case the caller further processes this event */
|
||||
event.setLocation(origX, origY);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -83,29 +83,39 @@ public class PanZoomController
|
|||
// The surface is snapping back into place after overscrolling.
|
||||
private static final int FLING_STATE_SNAPPING = 1;
|
||||
|
||||
private boolean mTouchMoved, mStopped;
|
||||
private long mLastTimestamp;
|
||||
private Timer mFlingTimer;
|
||||
private Axis mX, mY;
|
||||
private float mInitialZoomSpan;
|
||||
/* The span at the first zoom event (in unzoomed page coordinates). */
|
||||
private IntPoint mInitialZoomFocus;
|
||||
private float mInitialZoomSpan;
|
||||
/* The zoom focus at the first zoom event (in unzoomed page coordinates). */
|
||||
private boolean mTracking, mZooming;
|
||||
private IntPoint mInitialZoomFocus;
|
||||
|
||||
private enum PanZoomState {
|
||||
NOTHING, /* no touch-start events received */
|
||||
FLING, /* all touches removed, but we're still scrolling page */
|
||||
TOUCHING, /* one touch-start event received */
|
||||
PANNING, /* touch-start followed by move */
|
||||
PANNING_HOLD, /* in panning, but haven't moved. similar to TOUCHING but after starting a pan */
|
||||
PINCHING, /* nth touch-start, where n > 1. this mode allows pan and zoom */
|
||||
}
|
||||
|
||||
private PanZoomState mState;
|
||||
|
||||
public PanZoomController(LayerController controller) {
|
||||
mController = controller;
|
||||
mX = new Axis(); mY = new Axis();
|
||||
mStopped = true;
|
||||
mState = PanZoomState.NOTHING;
|
||||
|
||||
populatePositionAndLength();
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN: return onTouchStart(event);
|
||||
case MotionEvent.ACTION_MOVE: return onTouchMove(event);
|
||||
case MotionEvent.ACTION_UP: return onTouchEnd(event);
|
||||
case MotionEvent.ACTION_CANCEL: return onTouchCancel(event);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +131,18 @@ public class PanZoomController
|
|||
* a pan or a zoom is taking place.
|
||||
*/
|
||||
public boolean getRedrawHint() {
|
||||
return mStopped && !mTracking && !mZooming;
|
||||
switch (mState) {
|
||||
case NOTHING:
|
||||
case TOUCHING:
|
||||
case PANNING_HOLD:
|
||||
return true;
|
||||
case FLING:
|
||||
case PANNING:
|
||||
case PINCHING:
|
||||
return false;
|
||||
}
|
||||
Log.e(LOG_NAME, "Unhandled case " + mState + " in getRedrawHint");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -129,52 +150,92 @@ public class PanZoomController
|
|||
*/
|
||||
|
||||
private boolean onTouchStart(MotionEvent event) {
|
||||
/* If there is more than one finger down, we bail out to avoid misinterpreting a zoom
|
||||
* gesture as a pan gesture. */
|
||||
if (event.getPointerCount() > 1 || mZooming) {
|
||||
mZooming = true;
|
||||
mTouchMoved = false;
|
||||
mStopped = true;
|
||||
switch (mState) {
|
||||
case FLING:
|
||||
if (mFlingTimer != null) {
|
||||
mFlingTimer.cancel();
|
||||
mFlingTimer = null;
|
||||
}
|
||||
// fall through
|
||||
case NOTHING:
|
||||
mState = PanZoomState.TOUCHING;
|
||||
mX.touchPos = event.getX(0);
|
||||
mY.touchPos = event.getY(0);
|
||||
return false;
|
||||
case TOUCHING:
|
||||
case PANNING:
|
||||
case PANNING_HOLD:
|
||||
case PINCHING:
|
||||
mState = PanZoomState.PINCHING;
|
||||
return false;
|
||||
}
|
||||
|
||||
mX.touchPos = event.getX(0); mY.touchPos = event.getY(0);
|
||||
mTouchMoved = mStopped = false;
|
||||
mTracking = true;
|
||||
// TODO: Hold timeout
|
||||
return true;
|
||||
Log.e(LOG_NAME, "Unhandled case " + mState + " in onTouchStart");
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean onTouchMove(MotionEvent event) {
|
||||
if (event.getPointerCount() > 1 || mZooming) {
|
||||
mZooming = true;
|
||||
mTouchMoved = false;
|
||||
mStopped = true;
|
||||
switch (mState) {
|
||||
case NOTHING:
|
||||
case FLING:
|
||||
// should never happen
|
||||
Log.e(LOG_NAME, "Received impossible touch move while in " + mState);
|
||||
return false;
|
||||
case TOUCHING:
|
||||
mLastTimestamp = System.currentTimeMillis();
|
||||
// fall through
|
||||
case PANNING_HOLD:
|
||||
mState = PanZoomState.PANNING;
|
||||
// fall through
|
||||
case PANNING:
|
||||
track(event, System.currentTimeMillis());
|
||||
return true;
|
||||
case PINCHING:
|
||||
// scale gesture listener will handle this
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mTouchMoved)
|
||||
mLastTimestamp = System.currentTimeMillis();
|
||||
mTouchMoved = true;
|
||||
|
||||
// TODO: Clear hold timeout
|
||||
track(event, System.currentTimeMillis());
|
||||
|
||||
if (mFlingTimer != null) {
|
||||
mFlingTimer.cancel();
|
||||
mFlingTimer = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
Log.e(LOG_NAME, "Unhandled case " + mState + " in onTouchMove");
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean onTouchEnd(MotionEvent event) {
|
||||
if (mZooming)
|
||||
mZooming = false;
|
||||
switch (mState) {
|
||||
case NOTHING:
|
||||
case FLING:
|
||||
// should never happen
|
||||
Log.e(LOG_NAME, "Received impossible touch end while in " + mState);
|
||||
return false;
|
||||
case TOUCHING:
|
||||
mState = PanZoomState.NOTHING;
|
||||
// TODO: send click to gecko
|
||||
return false;
|
||||
case PANNING:
|
||||
case PANNING_HOLD:
|
||||
mState = PanZoomState.FLING;
|
||||
fling(System.currentTimeMillis());
|
||||
return true;
|
||||
case PINCHING:
|
||||
int points = event.getPointerCount();
|
||||
if (points == 1) {
|
||||
// last touch up
|
||||
mState = PanZoomState.NOTHING;
|
||||
} else if (points == 2) {
|
||||
int pointRemovedIndex = event.getActionIndex();
|
||||
int pointRemainingIndex = 1 - pointRemovedIndex; // kind of a hack
|
||||
mState = PanZoomState.TOUCHING;
|
||||
mX.touchPos = event.getX(pointRemainingIndex);
|
||||
mY.touchPos = event.getY(pointRemainingIndex);
|
||||
} else {
|
||||
// still pinching, do nothing
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Log.e(LOG_NAME, "Unhandled case " + mState + " in onTouchEnd");
|
||||
return false;
|
||||
}
|
||||
|
||||
mTracking = false;
|
||||
fling(System.currentTimeMillis());
|
||||
return true;
|
||||
private boolean onTouchCancel(MotionEvent event) {
|
||||
mState = PanZoomState.NOTHING;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void track(MotionEvent event, long timestamp) {
|
||||
|
@ -188,7 +249,8 @@ public class PanZoomController
|
|||
|
||||
float absVelocity = (float)Math.sqrt(mX.velocity * mX.velocity +
|
||||
mY.velocity * mY.velocity);
|
||||
mStopped = absVelocity < STOPPED_THRESHOLD;
|
||||
if (absVelocity < STOPPED_THRESHOLD)
|
||||
mState = PanZoomState.PANNING_HOLD;
|
||||
|
||||
mX.applyEdgeResistance(); mX.displace();
|
||||
mY.applyEdgeResistance(); mY.displace();
|
||||
|
@ -199,7 +261,7 @@ public class PanZoomController
|
|||
long timeStep = timestamp - mLastTimestamp;
|
||||
mLastTimestamp = timestamp;
|
||||
|
||||
if (mStopped)
|
||||
if (mState != PanZoomState.FLING)
|
||||
mX.velocity = mY.velocity = 0.0f;
|
||||
|
||||
mX.displace(); mY.displace();
|
||||
|
@ -273,7 +335,7 @@ public class PanZoomController
|
|||
}
|
||||
|
||||
private void stop() {
|
||||
mStopped = true;
|
||||
mState = PanZoomState.NOTHING;
|
||||
if (mFlingTimer != null) {
|
||||
mFlingTimer.cancel();
|
||||
mFlingTimer = null;
|
||||
|
@ -488,6 +550,7 @@ public class PanZoomController
|
|||
*/
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector detector) {
|
||||
mState = PanZoomState.PINCHING;
|
||||
float newZoom = detector.getCurrentSpan() / mInitialZoomSpan;
|
||||
|
||||
IntSize screenSize = mController.getScreenSize();
|
||||
|
@ -498,11 +561,13 @@ public class PanZoomController
|
|||
mController.setVisibleRect((int)Math.round(x), (int)Math.round(y),
|
||||
(int)Math.round(width), (int)Math.round(height));
|
||||
mController.notifyLayerClientOfGeometryChange();
|
||||
populatePositionAndLength();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||
mState = PanZoomState.PINCHING;
|
||||
IntRect initialZoomRect = (IntRect)mController.getVisibleRect().clone();
|
||||
float initialZoom = mController.getZoomFactor();
|
||||
|
||||
|
@ -514,7 +579,10 @@ public class PanZoomController
|
|||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
// TODO
|
||||
mState = PanZoomState.PANNING_HOLD;
|
||||
mLastTimestamp = System.currentTimeMillis();
|
||||
mX.touchPos = detector.getFocusX();
|
||||
mY.touchPos = detector.getFocusY();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Загрузка…
Ссылка в новой задаче