From e7742431e7b65f515694b5882e9161ee4160a2c0 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Wed, 25 Aug 2010 22:52:08 -0700 Subject: [PATCH] Bug 590246 - Multitouch swipe gestures for Android. r=blassey, a=blocking-fennec --- widget/src/android/nsWindow.cpp | 93 ++++++++++++++++++++++++++------- widget/src/android/nsWindow.h | 10 +++- 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/widget/src/android/nsWindow.cpp b/widget/src/android/nsWindow.cpp index b547b4cab92c..532962cde16a 100644 --- a/widget/src/android/nsWindow.cpp +++ b/widget/src/android/nsWindow.cpp @@ -36,6 +36,7 @@ * ***** END LICENSE BLOCK ***** */ #include +#include #include "nsAppShell.h" #include "nsIdleService.h" @@ -43,6 +44,7 @@ #include "nsIDeviceContext.h" #include "nsIRenderingContext.h" +#include "nsIDOMSimpleGestureEvent.h" #include "nsWidgetAtoms.h" #include "nsWidgetsCID.h" @@ -81,7 +83,10 @@ static nsTArray gTopLevelWindows; static nsWindow* gFocusedWindow = nsnull; static nsRefPtr sGLContext; -static PRBool sFailedToCreateGLContext = PR_FALSE; + +// Multitouch swipe thresholds (in screen pixels) +static const double SWIPE_MAX_PINCH_DELTA = 100; +static const double SWIPE_MIN_DISTANCE = 150; static nsWindow* TopWindow() @@ -779,8 +784,6 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) { AndroidBridge::AutoLocalJNIFrame jniFrame; - static bool firstDraw = true; - ALOG(">> OnDraw"); AndroidGeckoSurfaceView& sview(AndroidBridge::Bridge()->SurfaceView()); @@ -970,48 +973,98 @@ send_again: } static double -getDistance(int x1, int y1, int x2, int y2) +getDistance(const nsIntPoint &p1, const nsIntPoint &p2) { - double deltaX = x2 - x1; - double deltaY = y2 - y1; + double deltaX = p2.x - p1.x; + double deltaY = p2.y - p1.y; return sqrt(deltaX*deltaX + deltaY*deltaY); } void nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae) { - double dist = getDistance(ae->P0().x, ae->P0().y, ae->P1().x, ae->P1().y); + PRUint32 msg = 0; + + nsIntPoint midPoint; + midPoint.x = ((ae->P0().x + ae->P1().x) / 2); + midPoint.y = ((ae->P0().y + ae->P1().y) / 2); + nsIntPoint refPoint = midPoint - WidgetToScreenOffset(); + + double pinchDist = getDistance(ae->P0(), ae->P1()); + double pinchDelta = 0; - PRUint32 msg; switch (ae->Action() & AndroidMotionEvent::ACTION_MASK) { - case AndroidMotionEvent::ACTION_MOVE: - msg = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE; - break; case AndroidMotionEvent::ACTION_POINTER_DOWN: msg = NS_SIMPLE_GESTURE_MAGNIFY_START; - mStartDist = dist; + mStartPoint = new nsIntPoint(midPoint); + mStartDist = mLastDist = pinchDist; + mGestureFinished = false; + break; + case AndroidMotionEvent::ACTION_MOVE: + msg = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE; + pinchDelta = pinchDist - mLastDist; + mLastDist = pinchDist; break; case AndroidMotionEvent::ACTION_POINTER_UP: msg = NS_SIMPLE_GESTURE_MAGNIFY; + pinchDelta = pinchDist - mStartDist; + mStartPoint = nsnull; break; - default: return; } - nsIntPoint offset = WidgetToScreenOffset(); - nsSimpleGestureEvent event(PR_TRUE, msg, this, 0, dist - mStartDist); + if (!mGestureFinished) { + DispatchGestureEvent(msg, 0, pinchDelta, refPoint, ae->Time()); + + // If the cumulative pinch delta goes past the threshold, treat this + // as a pinch only, and not a swipe. + if (fabs(pinchDist - mStartDist) > SWIPE_MAX_PINCH_DELTA) + mStartPoint = nsnull; + + // If we have traveled more than SWIPE_MIN_DISTANCE from the start + // point, stop the pinch gesture and fire a swipe event. + if (mStartPoint) { + double swipeDistance = getDistance(midPoint, *mStartPoint); + if (swipeDistance > SWIPE_MIN_DISTANCE) { + PRUint32 direction = 0; + nsIntPoint motion = midPoint - *mStartPoint; + + if (motion.x < -swipeDistance/2) + direction |= nsIDOMSimpleGestureEvent::DIRECTION_LEFT; + if (motion.x > swipeDistance/2) + direction |= nsIDOMSimpleGestureEvent::DIRECTION_RIGHT; + if (motion.y < -swipeDistance/2) + direction |= nsIDOMSimpleGestureEvent::DIRECTION_UP; + if (motion.y > swipeDistance/2) + direction |= nsIDOMSimpleGestureEvent::DIRECTION_DOWN; + + // Finish the pinch gesture, then fire the swipe event: + msg = NS_SIMPLE_GESTURE_MAGNIFY; + DispatchGestureEvent(msg, 0, pinchDist - mStartDist, refPoint, ae->Time()); + msg = NS_SIMPLE_GESTURE_SWIPE; + DispatchGestureEvent(msg, direction, 0, refPoint, ae->Time()); + + // Don't generate any more gesture events for this touch. + mGestureFinished = true; + } + } + } +} + +void +nsWindow::DispatchGestureEvent(PRUint32 msg, PRUint32 direction, double delta, + const nsIntPoint &refPoint, PRUint64 time) +{ + nsSimpleGestureEvent event(PR_TRUE, msg, this, direction, delta); event.isShift = gLeftShift || gRightShift; event.isControl = gSym; event.isMeta = PR_FALSE; event.isAlt = gLeftAlt || gRightAlt; - event.time = ae->Time(); - event.refPoint.x = ((ae->P0().x + ae->P1().x) / 2) - offset.x; - event.refPoint.y = ((ae->P0().y + ae->P1().y) / 2) - offset.y; + event.time = time; + event.refPoint = refPoint; DispatchEvent(&event); - - mStartDist = dist; } void diff --git a/widget/src/android/nsWindow.h b/widget/src/android/nsWindow.h index 46aeb3ff5561..5190770096df 100644 --- a/widget/src/android/nsWindow.h +++ b/widget/src/android/nsWindow.h @@ -176,9 +176,14 @@ protected: PRPackedBool mIsVisible; nsTArray mChildren; nsWindow* mParent; + + bool mGestureFinished; double mStartDist; + double mLastDist; + nsAutoPtr mStartPoint; + nsCOMPtr mIdleService; - + PRUint32 mIMEEnabled; PRBool mIMEComposing; nsString mIMEComposingText; @@ -190,6 +195,9 @@ protected: private: void InitKeyEvent(nsKeyEvent& event, mozilla::AndroidGeckoEvent& key); + void DispatchGestureEvent(mozilla::AndroidGeckoEvent *ae); + void DispatchGestureEvent(PRUint32 msg, PRUint32 direction, double delta, + const nsIntPoint &refPoint, PRUint64 time); void HandleSpecialKey(mozilla::AndroidGeckoEvent *ae); };