nsEventStatus GestureEventListener::HandleInputEvent(const InputData& aEvent) { if (aEvent.mInputType != MULTITOUCH_INPUT) { return nsEventStatus_eIgnore; } const MultiTouchInput& event = static_cast<const MultiTouchInput&>(aEvent); switch (event.mType) { case MultiTouchInput::MULTITOUCH_START: case MultiTouchInput::MULTITOUCH_ENTER: { for (size_t i = 0; i < event.mTouches.Length(); i++) { bool foundAlreadyExistingTouch = false; for (size_t j = 0; j < mTouches.Length(); j++) { if (mTouches[j].mIdentifier == event.mTouches[i].mIdentifier) { foundAlreadyExistingTouch = true; } } NS_WARN_IF_FALSE(!foundAlreadyExistingTouch, "Tried to add a touch that already exists"); // If we didn't find a touch in our list that matches this, then add it. // If it already existed, we don't want to add it twice because that // messes with our touch move/end code. if (!foundAlreadyExistingTouch) { mTouches.AppendElement(event.mTouches[i]); } } if (mTouches.Length() == 2) { // Another finger has been added; it can't be a tap anymore. HandleTapCancel(event); } break; } case MultiTouchInput::MULTITOUCH_MOVE: { // If we move at all, just bail out of the tap. We need to change this so // that there's some tolerance in the future. HandleTapCancel(event); bool foundAlreadyExistingTouch = false; for (size_t i = 0; i < mTouches.Length(); i++) { for (size_t j = 0; j < event.mTouches.Length(); j++) { if (mTouches[i].mIdentifier == event.mTouches[j].mIdentifier) { foundAlreadyExistingTouch = true; mTouches[i] = event.mTouches[j]; } } } NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch moved, but not in list"); break; } case MultiTouchInput::MULTITOUCH_END: case MultiTouchInput::MULTITOUCH_LEAVE: { bool foundAlreadyExistingTouch = false; for (size_t i = 0; i < event.mTouches.Length() && !foundAlreadyExistingTouch; i++) { for (size_t j = 0; j < mTouches.Length() && !foundAlreadyExistingTouch; j++) { if (event.mTouches[i].mIdentifier == mTouches[j].mIdentifier) { foundAlreadyExistingTouch = true; mTouches.RemoveElementAt(j); } } } 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 (HandleSingleTapConfirmedEvent(event) == nsEventStatus_eConsumeNoDefault) { return nsEventStatus_eConsumeNoDefault; } } break; } case MultiTouchInput::MULTITOUCH_CANCEL: // This gets called if there's a touch that has to bail for weird reasons // like pinching and then moving away from the window that the pinch was // started in without letting go of the screen. HandlePinchGestureEvent(event, true); break; } return HandlePinchGestureEvent(event, false); }
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::HandleInputEvent(const InputData& aEvent) { if (aEvent.mInputType != MULTITOUCH_INPUT) { return nsEventStatus_eIgnore; } const MultiTouchInput& event = static_cast<const MultiTouchInput&>(aEvent); // Cache the current event since it may become the single or long tap that we // send. mLastTouchInput = event; switch (event.mType) { case MultiTouchInput::MULTITOUCH_START: case MultiTouchInput::MULTITOUCH_ENTER: { for (size_t i = 0; i < event.mTouches.Length(); i++) { bool foundAlreadyExistingTouch = false; for (size_t j = 0; j < mTouches.Length(); j++) { if (mTouches[j].mIdentifier == event.mTouches[i].mIdentifier) { foundAlreadyExistingTouch = true; } } NS_WARN_IF_FALSE(!foundAlreadyExistingTouch, "Tried to add a touch that already exists"); // If we didn't find a touch in our list that matches this, then add it. // If it already existed, we don't want to add it twice because that // messes with our touch move/end code. if (!foundAlreadyExistingTouch) { mTouches.AppendElement(event.mTouches[i]); } } size_t length = mTouches.Length(); if (length == 1) { mTapStartTime = event.mTime; mTouchStartPosition = event.mTouches[0].mScreenPoint; if (mState == GESTURE_NONE) { mState = GESTURE_WAITING_SINGLE_TAP; mLongTapTimeoutTask = NewRunnableMethod(this, &GestureEventListener::TimeoutLongTap); MessageLoop::current()->PostDelayedTask( FROM_HERE, 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(event); } break; } case MultiTouchInput::MULTITOUCH_MOVE: { // If we move too much, bail out of the tap. nsIntPoint touch = (nsIntPoint&)event.mTouches[0].mScreenPoint; if (mTouches.Length() == 1 && NS_hypot(mTouchStartPosition.x - touch.x, mTouchStartPosition.y - touch.y) > mAsyncPanZoomController->GetDPI() * AsyncPanZoomController::TOUCH_START_TOLERANCE) { HandleTapCancel(event); } bool foundAlreadyExistingTouch = false; for (size_t i = 0; i < mTouches.Length(); i++) { for (size_t j = 0; j < event.mTouches.Length(); j++) { if (mTouches[i].mIdentifier == event.mTouches[j].mIdentifier) { foundAlreadyExistingTouch = true; mTouches[i] = event.mTouches[j]; } } } NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch moved, but not in list"); break; } case MultiTouchInput::MULTITOUCH_END: case MultiTouchInput::MULTITOUCH_LEAVE: { bool foundAlreadyExistingTouch = false; for (size_t i = 0; i < event.mTouches.Length() && !foundAlreadyExistingTouch; i++) { for (size_t j = 0; j < mTouches.Length() && !foundAlreadyExistingTouch; j++) { if (event.mTouches[i].mIdentifier == mTouches[j].mIdentifier) { foundAlreadyExistingTouch = true; mTouches.RemoveElementAt(j); } } } NS_WARN_IF_FALSE(foundAlreadyExistingTouch, "Touch ended, but not in list"); if (event.mTime - mTapStartTime <= MAX_TAP_TIME) { if (mState == GESTURE_WAITING_DOUBLE_TAP && event.mTime - mLastTapEndTime > MAX_TAP_TIME) { // mDoubleTapTimeoutTask wasn't scheduled in time. We need to run the // task synchronously to confirm the last tap. mDoubleTapTimeoutTask->Cancel(); TimeoutDoubleTap(); // Change the state so we can proceed to process the current tap. mState = GESTURE_WAITING_SINGLE_TAP; } 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) { mLongTapTimeoutTask->Cancel(); 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; mDoubleTapTimeoutTask = NewRunnableMethod(this, &GestureEventListener::TimeoutDoubleTap); MessageLoop::current()->PostDelayedTask( FROM_HERE, mDoubleTapTimeoutTask, MAX_TAP_TIME); } mLastTapEndTime = event.mTime; } if (mState == GESTURE_WAITING_SINGLE_TAP) { mState = GESTURE_NONE; } if (!mTouches.Length()) { mSpanChange = 0.0f; } break; } case MultiTouchInput::MULTITOUCH_CANCEL: // This gets called if there's a touch that has to bail for weird reasons // like pinching and then moving away from the window that the pinch was // started in without letting go of the screen. HandlePinchGestureEvent(event, true); break; } return HandlePinchGestureEvent(event, false); }