Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ignore QTapGesture after QTapAndHoldGesture

I want to get QTapAndHoldGesture and QTapGesture in my widget and do different thing as reaction on these gestures. So I override QWidget::event method and add such code:

 bool event(QEvent *event) override {
    if (event->type() == QEvent::Gesture) {
      auto g_event = static_cast<QGestureEvent *>(event);
      qDebug() << "GestureEvent BEGIN: gestures " << g_event->gestures().size() << ", active: " << g_event->activeGestures();
      if (auto g = qobject_cast<QTapGesture *>(g_event->gesture(Qt::TapGesture))) {
        g_event->accept(g);
        return true;
      }
      if (auto g = qobject_cast<QTapAndHoldGesture *>(g_event->gesture(Qt::TapAndHoldGesture))) {
        if (g->state() == Qt::GestureFinished) {
                qDebug("FINISHED!!!");
                g->setGestureCancelPolicy(QGesture::CancelAllInContext);
        }
        g_event->accept(g);
        return true;
      }

The problem is that I get not wanted QTapGesture at the end of QTapAndHoldGesture.

It looks like this:

GestureEvent BEGIN: gestures  1 , active:  (QTapGesture(state=GestureStarted,hotSpot=773.396,492.884,position=773.396,492.884))
GestureEvent BEGIN: gestures  1 , active:  (QTapGesture(state=GestureUpdated,hotSpot=773.396,492.884,position=773.396,492.884))
mouse event x  773 , y  493
GestureEvent BEGIN: gestures  1 , active:  (QTapGesture(state=GestureUpdated,hotSpot=773.396,492.884,position=773.396,492.884))
...
GestureEvent BEGIN: gestures  1 , active:  (QTapGesture(state=GestureUpdated,hotSpot=773.396,492.884,position=773.396,492.884))
GestureEvent BEGIN: gestures  1 , active:  (QTapAndHoldGesture(state=GestureStarted,hotSpot=773,493,position=773,493,timeout=700))
GestureEvent BEGIN: gestures  1 , active:  (QTapAndHoldGesture(state=GestureFinished,hotSpot=773,493,position=773,493,timeout=700))
FINISHED!!!
GestureEvent BEGIN: gestures  1 , active:  (QTapGesture(state=GestureUpdated,hotSpot=773.396,492.884,position=773.396,492.884))
GestureEvent BEGIN: gestures  1 , active:  (QTapGesture(state=GestureUpdated,hotSpot=773.396,492.884,position=773.396,492.884))
GestureEvent BEGIN: gestures  1 , active:  (QTapGesture(state=GestureFinished,hotSpot=773.396,492.884,position=773.396,492.884))

As you see at start I got QTapGesture in started state, then QTapGesture in updated state, after that QTapAndHoldGesture and after that QTabGesture finished.

I need some way to ignore it. But I don't see how without re-implementing of gesture framework: collect position and time of event and filter events based on this information. Because of I receive gestures one by one and can not connect QTapGesture and QTapAndHoldGesture.

So is it possible to ignore QTapGesture after QTapAndHoldGesture without collecting information about position and time of QGestureEvent?

like image 275
user1244932 Avatar asked May 21 '18 22:05

user1244932


1 Answers

Because QTapAndHoldGesture gesture also requires a "Tap", it is expected to receive both:

  • QTapAndHoldGesture first, when the user holds for a certain time (end of a timer)
  • QTapGesture then, when the user releases the touch

Because they will always be received in that order, you can use that fact, to filter or cancel the QTapGesture which will be started, but not finished, when you receive QTapAndHoldGesture is validated (i.e. finished).

No need for time or position information, if you are managing a single touch point (Disclaimer: The following is untested).

bool MyClass::event(QEvent *event) override
{
    // QPointer in case we receive partial events. Should remove "isNull()" keys at some point.
    static QMap<QPointer<QTapGesture*>, bool> tapGestures;
    if (event->type() != QEvent::Gesture)
        return QQuickItem::event(event);

    auto g_event = static_cast<QGestureEvent *>(event);
    if (auto g = qobject_cast<QTapGesture *>(g_event->gesture(Qt::TapGesture))) {
        // A TapAndHold was triggered during that tap... let's ignore it
        if (tapGestures.value(g))
            g_event->ignore(g); // Or handle as you like

        if (g->state() == Qt::GestureFinished || g->state() == Qt::GestureCancelled)
            tapGestures.remove(g);
        else if (!tapGestures.contains(g))
            tapGestures.insert(g, false);

        g_event->accept(g);
        return true;
    }

    if (auto g = qobject_cast<QTapAndHoldGesture *>(g_event->gesture(Qt::TapAndHoldGesture))) {
        // Probably not needed if the gesture handle doesn't conflict with another component
        //if (g->state() == Qt::GestureFinished)
        //    g->setGestureCancelPolicy(QGesture::CancelAllInContext);

        // Mark all QTapGesture in progress to be ignored
        for (auto it = tapGestures.begin(); it != tapGestures.end(); ++it)
            it.value() = true;

        g_event->accept(g);
        return true;
    }
}

All gestures are available in QGestureManager class, so there may be a way to access it.

There is also the GestureOverride event type, but I believe that in your case it will not be triggered.

like image 134
Adrien Leravat Avatar answered Oct 21 '22 06:10

Adrien Leravat