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);
}
Ejemplo n.º 2
0
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);
}