~dandrader/unity8/ddaImprovements

« back to all changes in this revision

Viewing changes to plugins/Ubuntu/Gestures/TouchDispatcher.cpp

  • Committer: Daniel d'Andrada
  • Date: 2015-04-10 21:16:37 UTC
  • Revision ID: daniel.dandrada@canonical.com-20150410211637-g552hmbma17s7t2b
DirectionalDragArea & friends: various improvements & API grooming

-Simplified DirectionalDragArea gesture recognition
 * The widening angle property was dropped. Didn't really work
   as in real life swipes might start up in very different directions
   before finally turning to the final overall direction. That's specially
   bad when you have to conclude the recognition in a short amount
   of time so that you can still play animations to follow the user's finger
 * minSpeed as well. Went for a simpler way of expressing and evaluating it
   which is having maxTime and maxDistance.

-EdgeDragArea got absorbed by DirectionalDragArea

-Privatized all gesture recognition parameters.

-Privatized DirectionalDragArea.status property. All apps need is
 DirectionalDragArea.dragging. Modified qml code using DDA accordingly.

-Modified DirectionalDragArea.dragging semantics a bit. It goes to true only
 once a gesture is recognized

-Moved code that smooths out touch movement into DirectionalDragArea so all users
 get it for free.

-Cleaned up DirectionalDragArea.h of all implementation details, getting it ready
 to be moved out of unity8, into the SDK.

-Removed DirectionalDragArea.tapped() signal as it doesn't belong to it.

-Fine-tuned gesture recognition parameters and made them based on physical size.

-Added tests with input from real gestures to catch those annoying
  false-negatives and false-positives introduced by badly set recognition parameters

-Improved debug output

-UnownedTouchEvents are no longer sent to interim owners

-Fixed issue when TouchGate got disabled while holding an active touch

-Made "make tryDirectionalDragArea" and "make tryDragHandle" work with mouse again.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * Copyright (C) 2014 Canonical, Ltd.
 
2
 * Copyright (C) 2014-2015 Canonical, Ltd.
3
3
 *
4
4
 * This program is free software; you can redistribute it and/or modify
5
5
 * it under the terms of the GNU General Public License as published by
28
28
#define TOUCHDISPATCHER_DEBUG 0
29
29
 
30
30
#if TOUCHDISPATCHER_DEBUG
 
31
#define ugDebug(params) qDebug().nospace() << "[TouchDispatcher(" << this << ")] " << params
31
32
#include <DebugHelpers.h>
32
 
#endif
 
33
#else // TOUCHDISPATCHER_DEBUG
 
34
#define ugDebug(params) ((void)0)
 
35
#endif // TOUCHDISPATCHER_DEBUG
33
36
 
34
37
TouchDispatcher::TouchDispatcher()
35
38
    : m_status(NoActiveTouch)
44
47
        m_targetItem = target;
45
48
        if (m_status != NoActiveTouch) {
46
49
            qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");
47
 
            m_status = TargetRejectedTouches;
 
50
            setStatus(TargetRejectedTouches);
48
51
        }
49
52
    }
50
53
}
51
54
 
52
 
void TouchDispatcher::dispatch(QEvent::Type eventType,
53
 
                               QTouchDevice *device,
 
55
void TouchDispatcher::dispatch(QTouchDevice *device,
54
56
                               Qt::KeyboardModifiers modifiers,
55
57
                               const QList<QTouchEvent::TouchPoint> &touchPoints,
56
58
                               QWindow *window,
61
63
        return;
62
64
    }
63
65
 
 
66
    QEvent::Type eventType = resolveEventType(touchPoints);
 
67
 
64
68
    if (eventType == QEvent::TouchBegin) {
65
69
        dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);
66
70
 
72
76
            dispatchAsMouse(device, modifiers, touchPoints, timestamp);
73
77
        } else {
74
78
            Q_ASSERT(m_status == TargetRejectedTouches);
75
 
            #if TOUCHDISPATCHER_DEBUG
76
 
            qDebug() << "[TouchDispatcher] Not dispatching touch event to" << m_targetItem.data()
77
 
                << "because it already rejected the touch stream.";
78
 
            #endif
 
79
            ugDebug("Not dispatching touch event to " << m_targetItem.data()
 
80
                << "because it already rejected the touch stream.");
79
81
            // Do nothing
80
82
        }
81
83
 
82
84
        if (eventType == QEvent::TouchEnd) {
83
 
            m_status = NoActiveTouch;
 
85
            setStatus(NoActiveTouch);
84
86
            m_touchMouseId = -1;
85
87
        }
86
88
 
103
105
    QQuickItem *targetItem = m_targetItem.data();
104
106
 
105
107
    if (!targetItem->isEnabled() || !targetItem->isVisible()) {
106
 
        #if TOUCHDISPATCHER_DEBUG
107
 
        qDebug() << "[TouchDispatcher] Cannot dispatch touch event to" << targetItem
108
 
            << "because it's disabled or invisible.";
109
 
        #endif
 
108
        ugDebug("Cannot dispatch touch event to " << targetItem << " because it's disabled or invisible.");
110
109
        return;
111
110
    }
112
111
 
118
117
            createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));
119
118
 
120
119
 
121
 
    #if TOUCHDISPATCHER_DEBUG
122
 
    qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(touchEvent.data()))
123
 
            << "to" << targetItem;
124
 
    #endif
 
120
    ugDebug("dispatching " << qPrintable(touchEventToString(touchEvent.data()))
 
121
            << " to " << targetItem);
125
122
    QCoreApplication::sendEvent(targetItem, touchEvent.data());
126
123
 
127
124
 
128
125
    if (touchEvent->isAccepted()) {
129
 
        #if TOUCHDISPATCHER_DEBUG
130
 
        qDebug() << "[TouchDispatcher] Item accepted the touch event.";
131
 
        #endif
132
 
        m_status = DeliveringTouchEvents;
 
126
        ugDebug("Item accepted the touch event.");
 
127
        setStatus(DeliveringTouchEvents);
133
128
    } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {
134
 
        #if TOUCHDISPATCHER_DEBUG
135
 
        qDebug() << "[TouchDispatcher] Item rejected the touch event. Trying a QMouseEvent";
136
 
        #endif
 
129
        ugDebug("Item rejected the touch event. Trying a QMouseEvent");
137
130
        // NB: Arbitrarily chose the first touch point to emulate the mouse pointer
138
131
        QScopedPointer<QMouseEvent> mouseEvent(
139
132
                touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,
140
133
                                  modifiers, false /* transformNeeded */));
141
134
        Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);
142
135
 
143
 
        #if TOUCHDISPATCHER_DEBUG
144
 
        qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
145
 
                << "to" << m_targetItem.data();
146
 
        #endif
 
136
        ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
 
137
                << " to " << m_targetItem.data());
147
138
        QCoreApplication::sendEvent(targetItem, mouseEvent.data());
148
139
        if (mouseEvent->isAccepted()) {
149
 
            #if TOUCHDISPATCHER_DEBUG
150
 
            qDebug() << "[TouchDispatcher] Item accepted the QMouseEvent.";
151
 
            #endif
152
 
            m_status = DeliveringMouseEvents;
 
140
            ugDebug("Item accepted the QMouseEvent.");
 
141
            setStatus(DeliveringMouseEvents);
153
142
            m_touchMouseId = targetTouchPoints.at(0).id();
154
143
 
155
144
            if (checkIfDoubleClicked(timestamp)) {
156
145
                QScopedPointer<QMouseEvent> doubleClickEvent(
157
146
                        touchToMouseEvent(QEvent::MouseButtonDblClick, targetTouchPoints.at(0), timestamp,
158
147
                                          modifiers, false /* transformNeeded */));
159
 
                #if TOUCHDISPATCHER_DEBUG
160
 
                qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(doubleClickEvent.data()))
161
 
                        << "to" << m_targetItem.data();
162
 
                #endif
 
148
                ugDebug("dispatching " << qPrintable(mouseEventToString(doubleClickEvent.data()))
 
149
                        << " to " << m_targetItem.data());
163
150
                QCoreApplication::sendEvent(targetItem, doubleClickEvent.data());
164
151
            }
165
152
 
166
153
        } else {
167
 
            #if TOUCHDISPATCHER_DEBUG
168
 
            qDebug() << "[TouchDispatcher] Item rejected the QMouseEvent.";
169
 
            #endif
170
 
            m_status = TargetRejectedTouches;
 
154
            ugDebug("Item rejected the QMouseEvent.");
 
155
            setStatus(TargetRejectedTouches);
171
156
        }
172
157
    } else {
173
 
        #if TOUCHDISPATCHER_DEBUG
174
 
        qDebug() << "[TouchDispatcher] Item rejected the touch event and does not accept mouse buttons.";
175
 
        #endif
176
 
        m_status = TargetRejectedTouches;
 
158
        ugDebug("Item rejected the touch event and does not accept mouse buttons.");
 
159
        setStatus(TargetRejectedTouches);
177
160
    }
178
161
}
179
162
 
194
177
            createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));
195
178
 
196
179
 
197
 
    #if TOUCHDISPATCHER_DEBUG
198
 
    qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(eventForTargetItem.data()))
199
 
            << "to" << targetItem;
200
 
    #endif
 
180
    ugDebug("dispatching " << qPrintable(touchEventToString(eventForTargetItem.data()))
 
181
            << " to " << targetItem);
201
182
    QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());
202
183
}
203
184
 
254
235
        QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,
255
236
                    true /* transformNeeded */));
256
237
 
257
 
        #if TOUCHDISPATCHER_DEBUG
258
 
        qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
259
 
                << "to" << m_targetItem.data();
260
 
        #endif
 
238
        ugDebug("dispatching " << qPrintable(mouseEventToString(mouseEvent.data()))
 
239
                << " to " << m_targetItem.data());
261
240
        QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());
262
241
    }
263
242
}
365
344
 
366
345
    return doubleClicked;
367
346
}
 
347
 
 
348
void TouchDispatcher::setStatus(Status status)
 
349
{
 
350
    if (status != m_status) {
 
351
        #if TOUCHDISPATCHER_DEBUG
 
352
        switch (status) {
 
353
        case NoActiveTouch:
 
354
            ugDebug("status = NoActiveTouch");
 
355
            break;
 
356
        case DeliveringTouchEvents:
 
357
            ugDebug("status = DeliveringTouchEvents");
 
358
            break;
 
359
        case DeliveringMouseEvents:
 
360
            ugDebug("status = DeliveringMouseEvents");
 
361
            break;
 
362
        case TargetRejectedTouches:
 
363
            ugDebug("status = TargetRejectedTouches");
 
364
            break;
 
365
        default:
 
366
            ugDebug("status = " << status);
 
367
            break;
 
368
        }
 
369
        #endif
 
370
        m_status = status;
 
371
    }
 
372
}
 
373
 
 
374
void TouchDispatcher::reset()
 
375
{
 
376
    setStatus(NoActiveTouch);
 
377
    m_touchMouseId = -1;
 
378
    m_touchMousePressTimestamp =0;
 
379
}
 
380
 
 
381
QEvent::Type TouchDispatcher::resolveEventType(const QList<QTouchEvent::TouchPoint> &touchPoints)
 
382
{
 
383
    QEvent::Type eventType;
 
384
 
 
385
    Qt::TouchPointStates eventStates = 0;
 
386
    for (int i = 0; i < touchPoints.count(); i++)
 
387
        eventStates |= touchPoints[i].state();
 
388
 
 
389
    switch (eventStates) {
 
390
        case Qt::TouchPointPressed:
 
391
            eventType = QEvent::TouchBegin;
 
392
            break;
 
393
        case Qt::TouchPointReleased:
 
394
            eventType = QEvent::TouchEnd;
 
395
            break;
 
396
        default:
 
397
            eventType = QEvent::TouchUpdate;
 
398
            break;
 
399
    }
 
400
 
 
401
    return eventType;
 
402
}