mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 775463: Recognize double tap gestures while still supporting single taps
This commit is contained in:
parent
001d00e5ff
commit
42ecca33c0
@ -4,15 +4,26 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/thread.h"
|
||||
|
||||
#include "GestureEventListener.h"
|
||||
#include "AsyncPanZoomController.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
/**
|
||||
* Maximum time for a touch on the screen and corresponding lift of the finger
|
||||
* to be considered a tap. This also applies to double taps, except that it is
|
||||
* used twice.
|
||||
*/
|
||||
static const int MAX_TAP_TIME = 300;
|
||||
|
||||
GestureEventListener::GestureEventListener(AsyncPanZoomController* aAsyncPanZoomController)
|
||||
: mAsyncPanZoomController(aAsyncPanZoomController),
|
||||
mState(NoGesture)
|
||||
mState(GESTURE_NONE),
|
||||
mLastTouchInput(MultiTouchInput::MULTITOUCH_START, 0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -49,7 +60,13 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
|
||||
}
|
||||
}
|
||||
|
||||
if (mTouches.Length() == 2) {
|
||||
size_t length = mTouches.Length();
|
||||
if (length == 1) {
|
||||
mTapStartTime = event.mTime;
|
||||
if (mState == GESTURE_NONE) {
|
||||
mState = GESTURE_WAITING_SINGLE_TAP;
|
||||
}
|
||||
} else if (length == 2) {
|
||||
// Another finger has been added; it can't be a tap anymore.
|
||||
HandleTapCancel(event);
|
||||
}
|
||||
@ -89,18 +106,37 @@ nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent)
|
||||
|
||||
NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch ended, but not in list");
|
||||
|
||||
if (event.mTime - mTouchStartTime <= MAX_TAP_TIME) {
|
||||
// XXX: Incorrect use of the tap event. In the future, we want to send this
|
||||
// on NS_TOUCH_END, then have a short timer afterwards which sends
|
||||
// SingleTapConfirmed. Since we don't have double taps yet, this is fine for
|
||||
// now.
|
||||
if (HandleSingleTapUpEvent(event) == nsEventStatus_eConsumeNoDefault) {
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
if (event.mTime - mTapStartTime <= MAX_TAP_TIME) {
|
||||
if (mState == GESTURE_WAITING_DOUBLE_TAP) {
|
||||
mDoubleTapTimeoutTask->Cancel();
|
||||
|
||||
// We were waiting for a double tap and it has arrived.
|
||||
HandleDoubleTap(event);
|
||||
mState = GESTURE_NONE;
|
||||
} else if (mState == GESTURE_WAITING_SINGLE_TAP) {
|
||||
HandleSingleTapUpEvent(event);
|
||||
|
||||
// We were not waiting for anything but a single tap has happened that
|
||||
// may turn into a double tap. Wait a while and if it doesn't turn into
|
||||
// a double tap, send a single tap instead.
|
||||
mState = GESTURE_WAITING_DOUBLE_TAP;
|
||||
|
||||
// Cache the current event since it may become the single tap that we
|
||||
// send.
|
||||
mLastTouchInput = event;
|
||||
|
||||
mDoubleTapTimeoutTask =
|
||||
NewRunnableMethod(this, &GestureEventListener::TimeoutDoubleTap);
|
||||
|
||||
MessageLoop::current()->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
mDoubleTapTimeoutTask,
|
||||
MAX_TAP_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
if (HandleSingleTapConfirmedEvent(event) == nsEventStatus_eConsumeNoDefault) {
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
if (mState == GESTURE_WAITING_SINGLE_TAP) {
|
||||
mState = GESTURE_NONE;
|
||||
}
|
||||
|
||||
break;
|
||||
@ -130,7 +166,7 @@ nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInpu
|
||||
float(NS_hypot(firstTouch.x - secondTouch.x,
|
||||
firstTouch.y - secondTouch.y));
|
||||
|
||||
if (mState == NoGesture) {
|
||||
if (mState == GESTURE_NONE) {
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START,
|
||||
aEvent.mTime,
|
||||
focusPoint,
|
||||
@ -139,7 +175,7 @@ nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInpu
|
||||
|
||||
mAsyncPanZoomController->HandleInputEvent(pinchEvent);
|
||||
|
||||
mState = InPinchGesture;
|
||||
mState = GESTURE_PINCH;
|
||||
} else {
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE,
|
||||
aEvent.mTime,
|
||||
@ -153,7 +189,7 @@ nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInpu
|
||||
mPreviousSpan = currentSpan;
|
||||
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
} else if (mState == InPinchGesture) {
|
||||
} else if (mState == GESTURE_PINCH) {
|
||||
PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END,
|
||||
aEvent.mTime,
|
||||
mTouches[0].mScreenPoint,
|
||||
@ -162,7 +198,7 @@ nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInpu
|
||||
|
||||
mAsyncPanZoomController->HandleInputEvent(pinchEvent);
|
||||
|
||||
mState = NoGesture;
|
||||
mState = GESTURE_NONE;
|
||||
|
||||
rv = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
@ -177,29 +213,49 @@ nsEventStatus GestureEventListener::HandlePinchGestureEvent(const MultiTouchInpu
|
||||
nsEventStatus GestureEventListener::HandleSingleTapUpEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_UP, aEvent.mTime, aEvent.mTouches[0].mScreenPoint);
|
||||
mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
|
||||
return nsEventStatus_eConsumeDoDefault;
|
||||
return mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleSingleTapConfirmedEvent(const MultiTouchInput& aEvent)
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_CONFIRMED, aEvent.mTime, aEvent.mTouches[0].mScreenPoint);
|
||||
mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
|
||||
return nsEventStatus_eConsumeDoDefault;
|
||||
return mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleTapCancel(const MultiTouchInput& aEvent)
|
||||
{
|
||||
// XXX: In the future we will have to actually send a cancel notification to
|
||||
// Gecko, but for now since we're doing both the "SingleUp" and
|
||||
// "SingleConfirmed" notifications together, there's no need to cancel either
|
||||
// one.
|
||||
mTouchStartTime = 0;
|
||||
mTapStartTime = 0;
|
||||
|
||||
switch (mState)
|
||||
{
|
||||
case GESTURE_WAITING_SINGLE_TAP:
|
||||
case GESTURE_WAITING_DOUBLE_TAP:
|
||||
mState = GESTURE_NONE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return nsEventStatus_eConsumeDoDefault;
|
||||
}
|
||||
|
||||
nsEventStatus GestureEventListener::HandleDoubleTap(const MultiTouchInput& aEvent)
|
||||
{
|
||||
TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_DOUBLE, aEvent.mTime, aEvent.mTouches[0].mScreenPoint);
|
||||
return mAsyncPanZoomController->HandleInputEvent(tapEvent);
|
||||
}
|
||||
|
||||
void GestureEventListener::TimeoutDoubleTap()
|
||||
{
|
||||
// If we haven't gotten another tap by now, reset the state and treat it as a
|
||||
// single tap. It couldn't have been a double tap.
|
||||
if (mState == GESTURE_WAITING_DOUBLE_TAP) {
|
||||
mState = GESTURE_NONE;
|
||||
|
||||
HandleSingleTapConfirmedEvent(mLastTouchInput);
|
||||
}
|
||||
}
|
||||
|
||||
AsyncPanZoomController* GestureEventListener::GetAsyncPanZoomController() {
|
||||
return mAsyncPanZoomController;
|
||||
}
|
||||
|
@ -55,16 +55,18 @@ public:
|
||||
|
||||
protected:
|
||||
enum GestureState {
|
||||
NoGesture = 0,
|
||||
InPinchGesture
|
||||
// There's no gesture going on, and we don't think we're about to enter one.
|
||||
GESTURE_NONE,
|
||||
// There's a pinch happening, which occurs when there are two touch inputs.
|
||||
GESTURE_PINCH,
|
||||
// A touch start has happened and it may turn into a tap. We use this
|
||||
// because, if we put down two fingers and then lift them very quickly, this
|
||||
// may be mistaken for a tap.
|
||||
GESTURE_WAITING_SINGLE_TAP,
|
||||
// A single tap has happened for sure, and we're waiting for a second tap.
|
||||
GESTURE_WAITING_DOUBLE_TAP
|
||||
};
|
||||
|
||||
/**
|
||||
* Maximum time for a touch on the screen and corresponding lift of the finger
|
||||
* to be considered a tap.
|
||||
*/
|
||||
enum { MAX_TAP_TIME = 500 };
|
||||
|
||||
/**
|
||||
* Attempts to handle the event as a pinch event. If it is not a pinch event,
|
||||
* then we simply tell the next consumer to consume the event instead.
|
||||
@ -100,6 +102,24 @@ protected:
|
||||
*/
|
||||
nsEventStatus HandleTapCancel(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Attempts to handle a double tap. This happens when we get two single taps
|
||||
* within a short time. In general, this will not attempt to block the touch
|
||||
* event from being passed along to AsyncPanZoomController since APZC needs to
|
||||
* know about touches ending (and we only know if a touch was a double tap
|
||||
* once it ends).
|
||||
*/
|
||||
nsEventStatus HandleDoubleTap(const MultiTouchInput& aEvent);
|
||||
|
||||
/**
|
||||
* Times out a single tap we think may be turned into a double tap. This will
|
||||
* also send a single tap if we're still in the "WaitingDoubleTap" state when
|
||||
* this is called. This should be called a short time after a single tap is
|
||||
* detected, and the delay on it should be enough that the user has time to
|
||||
* tap again (to make a double tap).
|
||||
*/
|
||||
void TimeoutDoubleTap();
|
||||
|
||||
nsRefPtr<AsyncPanZoomController> mAsyncPanZoomController;
|
||||
|
||||
/**
|
||||
@ -107,6 +127,10 @@ protected:
|
||||
* this array, even if we choose not to handle it. When it ends, we remove it.
|
||||
*/
|
||||
nsTArray<SingleTouchData> mTouches;
|
||||
|
||||
/**
|
||||
* Current gesture we're dealing with.
|
||||
*/
|
||||
GestureState mState;
|
||||
|
||||
/**
|
||||
@ -117,9 +141,28 @@ protected:
|
||||
|
||||
/**
|
||||
* Stores the time a touch started, used for detecting a tap gesture. Only
|
||||
* valid when there's exactly one touch in mTouches.
|
||||
* valid when there's exactly one touch in mTouches. This is the time that the
|
||||
* first touch was inserted into the array. This is a PRUint64 because it is
|
||||
* initialized from interactions with InputData, which stores its timestamps as
|
||||
* a PRUint64.
|
||||
*/
|
||||
PRUint64 mTouchStartTime;
|
||||
PRUint64 mTapStartTime;
|
||||
|
||||
/**
|
||||
* Cached copy of the last touch input, only valid when in the
|
||||
* "WaitingDoubleTap" state. This is used to forward along to
|
||||
* AsyncPanZoomController if a single tap needs to be sent (since it is sent
|
||||
* shortly after the user actually taps, since we need to wait for a double
|
||||
* tap).
|
||||
*/
|
||||
MultiTouchInput mLastTouchInput;
|
||||
|
||||
/**
|
||||
* Task used to timeout a double tap. This gets posted to the UI thread such
|
||||
* that it runs a short time after a single tap happens. We cache it so that
|
||||
* we can cancel it if a double tap actually comes in.
|
||||
*/
|
||||
CancelableTask *mDoubleTapTimeoutTask;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user