nsEventStatus GestureEventListener::HandleInputTouchMultiStart() { nsEventStatus rv = nsEventStatus_eIgnore; switch (mState) { case GESTURE_NONE: SetState(GESTURE_MULTI_TOUCH_DOWN); break; case GESTURE_FIRST_SINGLE_TOUCH_DOWN: CancelLongTapTimeoutTask(); CancelMaxTapTimeoutTask(); SetState(GESTURE_MULTI_TOUCH_DOWN); // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event rv = nsEventStatus_eConsumeNoDefault; break; case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: CancelLongTapTimeoutTask(); SetState(GESTURE_MULTI_TOUCH_DOWN); // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event rv = nsEventStatus_eConsumeNoDefault; break; case GESTURE_FIRST_SINGLE_TOUCH_UP: // Cancel wait for double tap CancelMaxTapTimeoutTask(); SetState(GESTURE_MULTI_TOUCH_DOWN); TriggerSingleTapConfirmedEvent(); // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event rv = nsEventStatus_eConsumeNoDefault; break; case GESTURE_SECOND_SINGLE_TOUCH_DOWN: // Cancel wait for single tap CancelMaxTapTimeoutTask(); SetState(GESTURE_MULTI_TOUCH_DOWN); TriggerSingleTapConfirmedEvent(); // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event rv = nsEventStatus_eConsumeNoDefault; break; case GESTURE_LONG_TOUCH_DOWN: SetState(GESTURE_MULTI_TOUCH_DOWN); break; case GESTURE_MULTI_TOUCH_DOWN: case GESTURE_PINCH: // Prevent APZC::OnTouchStart() from handling MULTITOUCH_START event rv = nsEventStatus_eConsumeNoDefault; break; default: NS_WARNING("Unhandled state upon multitouch start"); SetState(GESTURE_NONE); break; } return rv; }
nsEventStatus GestureEventListener::HandleInputTouchCancel() { SetState(GESTURE_NONE); CancelMaxTapTimeoutTask(); CancelLongTapTimeoutTask(); return nsEventStatus_eIgnore; }
nsEventStatus GestureEventListener::HandleTapCancel(const MultiTouchInput& aEvent) { mTapStartTime = 0; switch (mState) { case GESTURE_WAITING_SINGLE_TAP: CancelLongTapTimeoutTask(); mState = GESTURE_NONE; break; case GESTURE_WAITING_DOUBLE_TAP: mState = GESTURE_NONE; break; default: break; } return nsEventStatus_eConsumeDoDefault; }
nsEventStatus GestureEventListener::HandleInputEvent(const MultiTouchInput& aEvent) { // Cache the current event since it may become the single or long tap that we // send. mLastTouchInput = aEvent; switch (aEvent.mType) { case MultiTouchInput::MULTITOUCH_START: case MultiTouchInput::MULTITOUCH_ENTER: { for (size_t i = 0; i < aEvent.mTouches.Length(); i++) { bool foundAlreadyExistingTouch = false; for (size_t j = 0; j < mTouches.Length(); j++) { if (mTouches[j].mIdentifier == aEvent.mTouches[i].mIdentifier) { foundAlreadyExistingTouch = true; break; } } // If we didn't find a touch in our list that matches this, then add it. if (!foundAlreadyExistingTouch) { mTouches.AppendElement(aEvent.mTouches[i]); } } size_t length = mTouches.Length(); if (length == 1) { mTapStartTime = aEvent.mTime; mTouchStartPosition = aEvent.mTouches[0].mScreenPoint; if (mState == GESTURE_NONE) { mState = GESTURE_WAITING_SINGLE_TAP; mLongTapTimeoutTask = NewRunnableMethod(this, &GestureEventListener::TimeoutLongTap); mAsyncPanZoomController->PostDelayedTask( mLongTapTimeoutTask, Preferences::GetInt("ui.click_hold_context_menus.delay", 500)); } } else if (length == 2) { // Another finger has been added; it can't be a tap anymore. HandleTapCancel(aEvent); } break; } case MultiTouchInput::MULTITOUCH_MOVE: { // If we move too much, bail out of the tap. ScreenIntPoint delta = aEvent.mTouches[0].mScreenPoint - mTouchStartPosition; if (mTouches.Length() == 1 && NS_hypot(delta.x, delta.y) > AsyncPanZoomController::GetTouchStartTolerance()) { HandleTapCancel(aEvent); } size_t eventTouchesMatched = 0; for (size_t i = 0; i < mTouches.Length(); i++) { bool isTouchRemoved = true; for (size_t j = 0; j < aEvent.mTouches.Length(); j++) { if (mTouches[i].mIdentifier == aEvent.mTouches[j].mIdentifier) { eventTouchesMatched++; isTouchRemoved = false; mTouches[i] = aEvent.mTouches[j]; } } if (isTouchRemoved) { // this touch point was lifted, so remove it from our list mTouches.RemoveElementAt(i); i--; } } NS_WARN_IF_FALSE(eventTouchesMatched == aEvent.mTouches.Length(), "Touch moved, but not in list"); break; } case MultiTouchInput::MULTITOUCH_END: case MultiTouchInput::MULTITOUCH_LEAVE: { for (size_t i = 0; i < aEvent.mTouches.Length(); i++) { bool foundAlreadyExistingTouch = false; for (size_t j = 0; j < mTouches.Length() && !foundAlreadyExistingTouch; j++) { if (aEvent.mTouches[i].mIdentifier == mTouches[j].mIdentifier) { foundAlreadyExistingTouch = true; mTouches.RemoveElementAt(j); } } NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch ended, but not in list"); } if (mState == GESTURE_WAITING_DOUBLE_TAP) { CancelDoubleTapTimeoutTask(); if (mTapStartTime - mLastTapEndTime > MAX_TAP_TIME || aEvent.mTime - mTapStartTime > MAX_TAP_TIME) { // Either the time between taps or the last tap took too long // confirm previous tap and handle current tap seperately TimeoutDoubleTap(); mState = GESTURE_WAITING_SINGLE_TAP; } else { // We were waiting for a double tap and it has arrived. HandleDoubleTap(aEvent); mState = GESTURE_NONE; } } if (mState == GESTURE_LONG_TAP_UP) { HandleLongTapUpEvent(aEvent); mState = GESTURE_NONE; } else if (mState == GESTURE_WAITING_SINGLE_TAP && aEvent.mTime - mTapStartTime > MAX_TAP_TIME) { // Extended taps are immediately dispatched as single taps CancelLongTapTimeoutTask(); HandleSingleTapConfirmedEvent(aEvent); mState = GESTURE_NONE; } else if (mState == GESTURE_WAITING_SINGLE_TAP) { CancelLongTapTimeoutTask(); nsEventStatus tapupEvent = HandleSingleTapUpEvent(aEvent); if (tapupEvent == nsEventStatus_eIgnore) { // 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; mDoubleTapTimeoutTask = NewRunnableMethod(this, &GestureEventListener::TimeoutDoubleTap); mAsyncPanZoomController->PostDelayedTask( mDoubleTapTimeoutTask, MAX_TAP_TIME); } else if (tapupEvent == nsEventStatus_eConsumeNoDefault) { // We sent the tapup into content without waiting for a double tap mState = GESTURE_NONE; } } mLastTapEndTime = aEvent.mTime; if (!mTouches.Length()) { mSpanChange = 0.0f; } break; } case MultiTouchInput::MULTITOUCH_CANCEL: // FIXME: we should probably clear a bunch of gesture state here break; } return HandlePinchGestureEvent(aEvent); }
nsEventStatus GestureEventListener::HandleInputTouchEnd() { // We intentionally do not pass apzc return statuses up since // it may cause apzc stay in the touching state even after // gestures are completed (please see Bug 1013378 for reference). nsEventStatus rv = nsEventStatus_eIgnore; switch (mState) { case GESTURE_NONE: // GEL doesn't have a dedicated state for PANNING handled in APZC thus ignore. break; case GESTURE_FIRST_SINGLE_TOUCH_DOWN: { CancelLongTapTimeoutTask(); CancelMaxTapTimeoutTask(); nsEventStatus tapupStatus = mAsyncPanZoomController->HandleGestureEvent( CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_UP)); if (tapupStatus == nsEventStatus_eIgnore) { SetState(GESTURE_FIRST_SINGLE_TOUCH_UP); CreateMaxTapTimeoutTask(); } else { // We sent the tapup into content without waiting for a double tap SetState(GESTURE_NONE); } break; } case GESTURE_SECOND_SINGLE_TOUCH_DOWN: { CancelMaxTapTimeoutTask(); SetState(GESTURE_NONE); mAsyncPanZoomController->HandleGestureEvent( CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_DOUBLE)); break; } case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: CancelLongTapTimeoutTask(); SetState(GESTURE_NONE); TriggerSingleTapConfirmedEvent(); break; case GESTURE_LONG_TOUCH_DOWN: { SetState(GESTURE_NONE); mAsyncPanZoomController->HandleGestureEvent( CreateTapEvent(mLastTouchInput, TapGestureInput::TAPGESTURE_LONG_UP)); break; } case GESTURE_MULTI_TOUCH_DOWN: if (mTouches.Length() < 2) { SetState(GESTURE_NONE); } break; case GESTURE_PINCH: if (mTouches.Length() < 2) { SetState(GESTURE_NONE); PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END, mLastTouchInput.mTime, mLastTouchInput.mTimeStamp, ScreenPoint(), 1.0f, 1.0f, mLastTouchInput.modifiers); mAsyncPanZoomController->HandleGestureEvent(pinchEvent); } rv = nsEventStatus_eConsumeNoDefault; break; default: NS_WARNING("Unhandled state upon touch end"); SetState(GESTURE_NONE); break; } return rv; }
nsEventStatus GestureEventListener::HandleInputTouchMove() { nsEventStatus rv = nsEventStatus_eIgnore; switch (mState) { case GESTURE_NONE: // Ignore this input signal as the corresponding events get handled by APZC break; case GESTURE_LONG_TOUCH_DOWN: if (MoveDistanceIsLarge()) { // So that we don't fire a long-tap-up if the user moves around after a // long-tap SetState(GESTURE_NONE); } break; case GESTURE_FIRST_SINGLE_TOUCH_DOWN: case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: case GESTURE_SECOND_SINGLE_TOUCH_DOWN: { // If we move too much, bail out of the tap. if (MoveDistanceIsLarge()) { CancelLongTapTimeoutTask(); CancelMaxTapTimeoutTask(); SetState(GESTURE_NONE); } break; } case GESTURE_MULTI_TOUCH_DOWN: { if (mLastTouchInput.mTouches.Length() < 2) { NS_WARNING("Wrong input: less than 2 moving points in GESTURE_MULTI_TOUCH_DOWN state"); break; } float currentSpan = GetCurrentSpan(mLastTouchInput); mSpanChange += fabsf(currentSpan - mPreviousSpan); if (mSpanChange > PINCH_START_THRESHOLD) { SetState(GESTURE_PINCH); PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START, mLastTouchInput.mTime, mLastTouchInput.mTimeStamp, GetCurrentFocus(mLastTouchInput), currentSpan, currentSpan, mLastTouchInput.modifiers); rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent); } else { // Prevent APZC::OnTouchMove from processing a move event when two // touches are active rv = nsEventStatus_eConsumeNoDefault; } mPreviousSpan = currentSpan; break; } case GESTURE_PINCH: { if (mLastTouchInput.mTouches.Length() < 2) { NS_WARNING("Wrong input: less than 2 moving points in GESTURE_PINCH state"); // Prevent APZC::OnTouchMove() from handling this wrong input rv = nsEventStatus_eConsumeNoDefault; break; } float currentSpan = GetCurrentSpan(mLastTouchInput); PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE, mLastTouchInput.mTime, mLastTouchInput.mTimeStamp, GetCurrentFocus(mLastTouchInput), currentSpan, mPreviousSpan, mLastTouchInput.modifiers); rv = mAsyncPanZoomController->HandleGestureEvent(pinchEvent); mPreviousSpan = currentSpan; break; } default: NS_WARNING("Unhandled state upon touch move"); SetState(GESTURE_NONE); break; } return rv; }
nsEventStatus GestureEventListener::HandleInputTouchEnd() { nsEventStatus rv = nsEventStatus_eIgnore; switch (mState) { case GESTURE_NONE: // GEL doesn't have a dedicated state for PANNING handled in APZC thus ignore. break; case GESTURE_FIRST_SINGLE_TOUCH_DOWN: { CancelLongTapTimeoutTask(); CancelMaxTapTimeoutTask(); TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_UP, mLastTouchInput.mTime, mLastTouchInput.mTouches[0].mScreenPoint, mLastTouchInput.modifiers); nsEventStatus tapupStatus = mAsyncPanZoomController->HandleGestureEvent(tapEvent); if (tapupStatus == nsEventStatus_eIgnore) { SetState(GESTURE_FIRST_SINGLE_TOUCH_UP); CreateMaxTapTimeoutTask(); } else { // We sent the tapup into content without waiting for a double tap SetState(GESTURE_NONE); } break; } case GESTURE_SECOND_SINGLE_TOUCH_DOWN: { CancelMaxTapTimeoutTask(); SetState(GESTURE_NONE); TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_DOUBLE, mLastTouchInput.mTime, mLastTouchInput.mTouches[0].mScreenPoint, mLastTouchInput.modifiers); mAsyncPanZoomController->HandleGestureEvent(tapEvent); break; } case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: CancelLongTapTimeoutTask(); SetState(GESTURE_NONE); TriggerSingleTapConfirmedEvent(); break; case GESTURE_LONG_TOUCH_DOWN: { SetState(GESTURE_NONE); TapGestureInput tapEvent(TapGestureInput::TAPGESTURE_LONG_UP, mLastTouchInput.mTime, mLastTouchInput.mTouches[0].mScreenPoint, mLastTouchInput.modifiers); mAsyncPanZoomController->HandleGestureEvent(tapEvent); break; } case GESTURE_MULTI_TOUCH_DOWN: if (mTouches.Length() < 2) { SetState(GESTURE_NONE); } break; case GESTURE_PINCH: if (mTouches.Length() < 2) { SetState(GESTURE_NONE); PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_END, mLastTouchInput.mTime, ScreenPoint(), 1.0f, 1.0f, mLastTouchInput.modifiers); mAsyncPanZoomController->HandleGestureEvent(pinchEvent); } rv = nsEventStatus_eConsumeNoDefault; break; default: NS_WARNING("Unhandled state upon touch end"); SetState(GESTURE_NONE); break; } return rv; }
nsEventStatus GestureEventListener::HandleInputTouchMove() { nsEventStatus rv = nsEventStatus_eIgnore; switch (mState) { case GESTURE_NONE: case GESTURE_LONG_TOUCH_DOWN: // Ignore this input signal as the corresponding events get handled by APZC break; case GESTURE_FIRST_SINGLE_TOUCH_DOWN: case GESTURE_FIRST_SINGLE_TOUCH_MAX_TAP_DOWN: case GESTURE_SECOND_SINGLE_TOUCH_DOWN: { // If we move too much, bail out of the tap. ScreenIntPoint delta = mLastTouchInput.mTouches[0].mScreenPoint - mTouchStartPosition; if (NS_hypot(delta.x, delta.y) > AsyncPanZoomController::GetTouchStartTolerance()) { CancelLongTapTimeoutTask(); CancelMaxTapTimeoutTask(); SetState(GESTURE_NONE); } break; } case GESTURE_MULTI_TOUCH_DOWN: { if (mLastTouchInput.mTouches.Length() < 2) { NS_WARNING("Wrong input: less than 2 moving points in GESTURE_MULTI_TOUCH_DOWN state"); break; } float currentSpan = GetCurrentSpan(mLastTouchInput); mSpanChange += fabsf(currentSpan - mPreviousSpan); if (mSpanChange > PINCH_START_THRESHOLD) { SetState(GESTURE_PINCH); PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_START, mLastTouchInput.mTime, GetCurrentFocus(mLastTouchInput), currentSpan, currentSpan, mLastTouchInput.modifiers); mAsyncPanZoomController->HandleGestureEvent(pinchEvent); } rv = nsEventStatus_eConsumeNoDefault; mPreviousSpan = currentSpan; break; } case GESTURE_PINCH: { if (mLastTouchInput.mTouches.Length() < 2) { NS_WARNING("Wrong input: less than 2 moving points in GESTURE_PINCH state"); // Prevent APZC::OnTouchMove() from handling this wrong input rv = nsEventStatus_eConsumeNoDefault; break; } float currentSpan = GetCurrentSpan(mLastTouchInput); PinchGestureInput pinchEvent(PinchGestureInput::PINCHGESTURE_SCALE, mLastTouchInput.mTime, GetCurrentFocus(mLastTouchInput), currentSpan, mPreviousSpan, mLastTouchInput.modifiers); mAsyncPanZoomController->HandleGestureEvent(pinchEvent); rv = nsEventStatus_eConsumeNoDefault; mPreviousSpan = currentSpan; break; } default: NS_WARNING("Unhandled state upon touch move"); SetState(GESTURE_NONE); break; } return rv; }