summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBradley T. Hughes <bradley.hughes@nokia.com>2009-05-04 15:17:34 (GMT)
committerBradley T. Hughes <bradley.hughes@nokia.com>2009-05-04 15:17:34 (GMT)
commit2d0f49df3a3c44601bf2e41ea722c2384b71aab0 (patch)
tree41720f7885f1a3c0c2720fa0d0c388956001bb35
parent36c97c3e530969d172179d715805804ffb96e792 (diff)
downloadQt-2d0f49df3a3c44601bf2e41ea722c2384b71aab0.zip
Qt-2d0f49df3a3c44601bf2e41ea722c2384b71aab0.tar.gz
Qt-2d0f49df3a3c44601bf2e41ea722c2384b71aab0.tar.bz2
Support sending touch events (with multiple touch points) to multiple widgets simultaneously
This is a first attempt, and it works, but it will need to be cleaned up to remove as much state from QWidgetPrivate as possible.
-rw-r--r--src/gui/kernel/qapplication.cpp9
-rw-r--r--src/gui/kernel/qapplication_p.h11
-rw-r--r--src/gui/kernel/qapplication_win.cpp191
-rw-r--r--src/gui/kernel/qevent.h2
-rw-r--r--src/gui/kernel/qevent_p.h1
-rw-r--r--src/gui/kernel/qwidget_p.h3
6 files changed, 141 insertions, 76 deletions
diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp
index 81c02e3..3a25abb 100644
--- a/src/gui/kernel/qapplication.cpp
+++ b/src/gui/kernel/qapplication.cpp
@@ -3997,6 +3997,7 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
// Note: TouchUpdate and TouchEnd events are sent to d->currentMultitouchWidget and never propagated
{
QWidget *widget = static_cast<QWidget *>(receiver);
+ QWidget *origin = widget;
QTouchEvent *touchEvent = static_cast<QTouchEvent *>(e);
bool eventAccepted = touchEvent->isAccepted();
@@ -4017,7 +4018,13 @@ bool QApplication::notify(QObject *receiver, QEvent *e)
touchEvent->spont = false;
if (res && eventAccepted) {
// the first widget to accept the TouchBegin gets an implicit grab.
- d->currentMultitouchWidget = widget;
+ for (int i = 0; i < touchEvent->_touchPoints.count(); ++i) {
+ QTouchEvent::TouchPoint *touchPoint = touchEvent->_touchPoints.at(i);
+ touchPoint->d->widget = widget;
+ }
+ if (origin != widget)
+ origin->d_func()->currentTouchPoints.clear();
+ widget->d_func()->currentTouchPoints = touchEvent->_touchPoints;
break;
} else if (widget->isWindow() || widget->testAttribute(Qt::WA_NoMousePropagation)) {
break;
diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h
index ef375d3..1a2bad2 100644
--- a/src/gui/kernel/qapplication_p.h
+++ b/src/gui/kernel/qapplication_p.h
@@ -428,7 +428,6 @@ public:
void sendSyntheticEnterLeave(QWidget *widget);
#endif
- QPointer<QWidget> currentMultitouchWidget;
static void updateTouchPointsForWidget(QWidget *widget, QTouchEvent *touchEvent);
#if defined(Q_WS_WIN)
@@ -437,12 +436,14 @@ public:
static qt_CloseTouchInputHandlePtr CloseTouchInputHandle;
QMap<DWORD, int> touchInputIDToTouchPointID;
- QVector<QTouchEvent::TouchPoint *> allTouchPoints;
- QList<QTouchEvent::TouchPoint *> currentTouchPoints;
+ QVector<QTouchEvent::TouchPoint *> appAllTouchPoints;
+ QList<QTouchEvent::TouchPoint *> appCurrentTouchPoints;
void initializeMultitouch();
- QEvent::Type insertActiveTouch(QTouchEvent::TouchPoint *touchPoint);
- QEvent::Type removeActiveTouch(QTouchEvent::TouchPoint *touchPoint);
+ static QTouchEvent::TouchPoint *findClosestTouchPoint(const QList<QTouchEvent::TouchPoint *> &activeTouchPoints,
+ const QPointF &pos);
+ QEvent::Type appendTouchPoint(QTouchEvent::TouchPoint *touchPoint);
+ QEvent::Type removeTouchPoint(QTouchEvent::TouchPoint *touchPoint);
bool translateTouchEvent(const MSG &msg);
#endif
diff --git a/src/gui/kernel/qapplication_win.cpp b/src/gui/kernel/qapplication_win.cpp
index fbedd19..f13ab5d 100644
--- a/src/gui/kernel/qapplication_win.cpp
+++ b/src/gui/kernel/qapplication_win.cpp
@@ -75,6 +75,7 @@ extern void qt_wince_hide_taskbar(HWND hwnd); //defined in qguifunctions_wince.c
#include "qcolormap.h"
#include "qlayout.h"
#include "qtooltip.h"
+#include "qset.h"
#include "qt_windows.h"
#if defined(QT_NON_COMMERCIAL)
#include "qnc_win.h"
@@ -3999,45 +4000,76 @@ void QApplicationPrivate::initializeMultitouch()
GetTouchInputInfo = static_cast<qt_GetTouchInputInfoPtr>(library.resolve("GetTouchInputInfo"));
CloseTouchInputHandle = static_cast<qt_CloseTouchInputHandlePtr>(library.resolve("CloseTouchInputHandle"));
- currentMultitouchWidget = 0;
touchInputIDToTouchPointID.clear();
- allTouchPoints.clear();
- currentTouchPoints.clear();
+ appAllTouchPoints.clear();
}
-QEvent::Type QApplicationPrivate::insertActiveTouch(QTouchEvent::TouchPoint *touchPoint)
+QTouchEvent::TouchPoint *QApplicationPrivate::findClosestTouchPoint(const QList<QTouchEvent::TouchPoint *> &appActiveTouchPoints,
+ const QPointF &pos)
{
- QEvent::Type eventType = currentTouchPoints.isEmpty()
+ QTouchEvent::TouchPoint *closestTouchPoint = 0;
+ qreal closestDistance;
+ for (int i = 0; i < appActiveTouchPoints.count(); ++i) {
+ QTouchEvent::TouchPoint *touchPoint = appActiveTouchPoints.at(i);
+ qreal distance = QLineF(pos, touchPoint->d->globalPos).length();
+ if (!closestTouchPoint || distance < closestDistance) {
+ closestTouchPoint = touchPoint;
+ closestDistance = distance;
+ }
+ }
+ return closestTouchPoint;
+}
+
+QEvent::Type QApplicationPrivate::appendTouchPoint(QTouchEvent::TouchPoint *touchPoint)
+{
+ QWidget *widget = touchPoint->d->widget;
+ QEvent::Type eventType = widget->d_func()->currentTouchPoints.isEmpty()
? QEvent::TouchBegin
: QEvent::TouchUpdate;
- // insort touch point
+ // insort touch point (for the app)
int at = 0;
- for (; at < currentTouchPoints.count(); ++at) {
- if (currentTouchPoints.at(at)->id() > touchPoint->id())
+ for (; at < appCurrentTouchPoints.count(); ++at) {
+ if (appCurrentTouchPoints.at(at)->id() > touchPoint->id())
break;
}
- currentTouchPoints.insert(at, touchPoint);
+ appCurrentTouchPoints.insert(at, touchPoint);
+ // again, for the widget's currentTouchPoints
+ for (at = 0; at < widget->d_func()->currentTouchPoints.count(); ++at) {
+ if (widget->d_func()->currentTouchPoints.at(at)->id() > touchPoint->id())
+ break;
+ }
+ widget->d_func()->currentTouchPoints.insert(at, touchPoint);
- if (currentTouchPoints.count() > allTouchPoints.count()) {
- qFatal("Qt: INTERNAL ERROR: currentTouchPoints.count() (%d) > allTouchPoints.count() (%d)",
- currentTouchPoints.count(),
- allTouchPoints.count());
+ if (appCurrentTouchPoints.count() > appAllTouchPoints.count()) {
+ qFatal("Qt: INTERNAL ERROR: appCurrentTouchPoints.count() (%d) > appAllTouchPoints.count() (%d)",
+ appCurrentTouchPoints.count(),
+ appAllTouchPoints.count());
}
return eventType;
}
-QEvent::Type QApplicationPrivate::removeActiveTouch(QTouchEvent::TouchPoint *touchPoint)
+QEvent::Type QApplicationPrivate::removeTouchPoint(QTouchEvent::TouchPoint *touchPoint)
{
- for (int i = qMin(currentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) {
- if (currentTouchPoints.at(i) == touchPoint) {
- currentTouchPoints.removeAt(i);
+ QWidget *widget = touchPoint->d->widget;
+
+ // remove touch point from all known touch points
+ for (int i = qMin(appCurrentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) {
+ if (appCurrentTouchPoints.at(i) == touchPoint) {
+ appCurrentTouchPoints.removeAt(i);
+ break;
+ }
+ }
+ // again, for the widget's currentTouchPoints
+ for (int i = qMin(widget->d_func()->currentTouchPoints.count() - 1, touchPoint->id()); i >= 0; --i) {
+ if (widget->d_func()->currentTouchPoints.at(i) == touchPoint) {
+ widget->d_func()->currentTouchPoints.removeAt(i);
break;
}
}
- return currentTouchPoints.isEmpty() ? QEvent::TouchEnd : QEvent::TouchUpdate;
+ return widget->d_func()->currentTouchPoints.isEmpty() ? QEvent::TouchEnd : QEvent::TouchUpdate;
}
bool QApplicationPrivate::translateTouchEvent(const MSG &msg)
@@ -4048,8 +4080,9 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg)
if (!widgetForHwnd)
return false;
- QEvent::Type eventType = QEvent::None;
- QList<QTouchEvent::TouchPoint *> activeTouchPoints;
+ QList<QTouchEvent::TouchPoint *> appActiveTouchPoints = appCurrentTouchPoints;
+
+ QSet<QWidget *> widgetsNeedingEvents;
QVector<TOUCHINPUT> winTouchInputs(msg.wParam);
memset(winTouchInputs.data(), 0, sizeof(TOUCHINPUT) * winTouchInputs.count());
QApplicationPrivate::GetTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT));
@@ -4062,36 +4095,56 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg)
touchInputIDToTouchPointID.insert(touchInput.dwID, touchPointID);
}
- if (allTouchPoints.count() <= touchPointID)
- allTouchPoints.resize(touchPointID + 1);
+ if (appAllTouchPoints.count() <= touchPointID)
+ appAllTouchPoints.resize(touchPointID + 1);
- QTouchEvent::TouchPoint *touchPoint = allTouchPoints.at(touchPointID);
+ QTouchEvent::TouchPoint *touchPoint = appAllTouchPoints.at(touchPointID);
if (!touchPoint)
- touchPoint = allTouchPoints[touchPointID] = new QTouchEvent::TouchPoint(touchPointID);
+ touchPoint = appAllTouchPoints[touchPointID] = new QTouchEvent::TouchPoint(touchPointID);
// update state
bool down = touchPoint->d->state != Qt::TouchPointReleased;
QPointF globalPos(qreal(touchInput.x) / qreal(100.), qreal(touchInput.y) / qreal(100.));
if (!down && (touchInput.dwFlags & TOUCHEVENTF_DOWN)) {
- eventType = insertActiveTouch(touchPoint);
+ // determine which widget this event will go to
+ QWidget *w = widgetForHwnd->childAt(widgetForHwnd->mapFromGlobal(globalPos.toPoint()));
+ if (!w)
+ w = widgetForHwnd;
+
+ QTouchEvent::TouchPoint *closestTouchPoint = findClosestTouchPoint(appActiveTouchPoints, globalPos);
+ if (closestTouchPoint
+ && (w->isAncestorOf(closestTouchPoint->d->widget)
+ || closestTouchPoint->d->widget->isAncestorOf(w))) {
+ w = closestTouchPoint->d->widget;
+ }
+ touchPoint->d->widget = w;
+
+ w->d_func()->touchEventType = appendTouchPoint(touchPoint);
// make sure new points are added to activeTouchPoints as well
- activeTouchPoints = currentTouchPoints;
+ appActiveTouchPoints = appCurrentTouchPoints;
+ w->d_func()->activeTouchPoints = w->d_func()->currentTouchPoints;
touchPoint->d->state = Qt::TouchPointPressed;
- touchPoint->d->globalPos = touchPoint->d->startGlobalPos = touchPoint->d->lastGlobalPos = globalPos;
+ touchPoint->d->globalPos
+ = touchPoint->d->startGlobalPos
+ = touchPoint->d->lastGlobalPos
+ = globalPos;
touchPoint->d->pressure = qreal(1.);
} else if (down && (touchInput.dwFlags & TOUCHEVENTF_UP)) {
- activeTouchPoints = currentTouchPoints;
- eventType = removeActiveTouch(touchPoint);
+ QWidget *w = touchPoint->d->widget;
+ appActiveTouchPoints = appCurrentTouchPoints;
+ w->d_func()->activeTouchPoints = w->d_func()->currentTouchPoints;
+ w->d_func()->touchEventType = removeTouchPoint(touchPoint);
touchPoint->d->state = Qt::TouchPointReleased;
touchPoint->d->lastGlobalPos = touchPoint->d->globalPos;
touchPoint->d->globalPos = globalPos;
touchPoint->d->pressure = qreal(0.);
} else if (down) {
- if (activeTouchPoints.isEmpty())
- activeTouchPoints = currentTouchPoints;
- eventType = QEvent::TouchUpdate;
+ QWidget *w = touchPoint->d->widget;
+ appActiveTouchPoints = appCurrentTouchPoints;
+ w->d_func()->activeTouchPoints = w->d_func()->currentTouchPoints;
+ w->d_func()->touchEventType = QEvent::TouchUpdate;
touchPoint->d->state = globalPos == touchPoint->d->globalPos
? Qt::TouchPointStationary
: Qt::TouchPointMoved;
@@ -4099,58 +4152,56 @@ bool QApplicationPrivate::translateTouchEvent(const MSG &msg)
touchPoint->d->globalPos = globalPos;
// pressure should still be 1.
}
+
+ if (touchPoint->d->state != Qt::TouchPointStationary)
+ widgetsNeedingEvents.insert(touchPoint->d->widget);
}
QApplicationPrivate::CloseTouchInputHandle((HANDLE) msg.lParam);
- Q_ASSERT(eventType != QEvent::None);
- if (eventType == QEvent::None || activeTouchPoints.isEmpty())
+ if (widgetsNeedingEvents.isEmpty())
return false;
- if (eventType == QEvent::TouchBegin) {
- // the window under the first touch point gets the touch event
- int firstTouchId = activeTouchPoints.first()->id();
- const QPoint &globalPos = allTouchPoints.at(firstTouchId)->d->globalPos.toPoint();
- QWidget *child = widgetForHwnd->childAt(widgetForHwnd->mapFromGlobal(globalPos));
- if (!child)
- child = widgetForHwnd;
- currentMultitouchWidget = child;
- // if the TouchBegin handler recurses, we assume that means the event
- // has been implicitly accepted and continue to send touch events
- currentMultitouchWidget->setAttribute(Qt::WA_AcceptedTouchBeginEvent);
- }
+ bool returnValue = false;
- QWidget *widget = q->activePopupWidget();
- if (!widget)
- widget = currentMultitouchWidget;
-
- if (eventType == QEvent::TouchEnd) {
- // reset currentMultitouchWindow when the last touch is released
- currentMultitouchWidget = 0;
- if (!currentTouchPoints.isEmpty()) {
- qFatal("Qt: INTERNAL ERROR, currentTouchPoints should be empty!");
- }
- }
+ QSet<QWidget *>::ConstIterator it = widgetsNeedingEvents.constBegin();
+ const QSet<QWidget *>::ConstIterator end = widgetsNeedingEvents.constEnd();
+ for (; it != end; ++it) {
+ QWidget *widget = *it;
+ if (!QApplicationPrivate::tryModalHelper(widget, 0))
+ continue;
- // deliver the event
- if (widget && QApplicationPrivate::tryModalHelper(widget, 0)) {
- QTouchEvent touchEvent(eventType, q->keyboardModifiers(), activeTouchPoints);
+ QTouchEvent touchEvent(widget->d_func()->touchEventType, q->keyboardModifiers(), widget->d_func()->activeTouchPoints);
updateTouchPointsForWidget(widget, &touchEvent);
- if (eventType == QEvent::TouchBegin) {
+ switch (widget->d_func()->touchEventType) {
+ case QEvent::TouchBegin:
+ {
+ // if the TouchBegin handler recurses, we assume that means the event
+ // has been implicitly accepted and continue to send touch events
+ widget->setAttribute(Qt::WA_AcceptedTouchBeginEvent);
bool res = QApplication::sendSpontaneousEvent(widget, &touchEvent)
&& touchEvent.isAccepted();
- qt_tabletChokeMouse = res;
- } else if (widget->testAttribute(Qt::WA_AcceptedTouchBeginEvent)) {
- (void) QApplication::sendSpontaneousEvent(widget, &touchEvent);
- qt_tabletChokeMouse = true;
- } else {
- qt_tabletChokeMouse = false;
+ returnValue = returnValue || (qt_tabletChokeMouse = res);
+ break;
+ }
+ case QEvent::TouchEnd:
+ if (!widget->d_func()->currentTouchPoints.isEmpty()) {
+ qFatal("Qt: INTERNAL ERROR, the widget's currentTouchPoints should be empty!");
+ }
+ // fall-through intended
+ default:
+ if (widget->testAttribute(Qt::WA_AcceptedTouchBeginEvent)) {
+ (void) QApplication::sendSpontaneousEvent(widget, &touchEvent);
+ qt_tabletChokeMouse = true;
+ } else {
+ qt_tabletChokeMouse = false;
+ }
+ returnValue = returnValue || qt_tabletChokeMouse;
+ break;
}
-
- return qt_tabletChokeMouse;
}
- return false;
+ return returnValue;
}
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h
index 506e0c1..e9a1386 100644
--- a/src/gui/kernel/qevent.h
+++ b/src/gui/kernel/qevent.h
@@ -746,6 +746,7 @@ public:
private:
QTouchEventTouchPointPrivate *d;
+ friend class QApplication;
friend class QApplicationPrivate;
};
@@ -759,6 +760,7 @@ public:
protected:
QList<TouchPoint *> _touchPoints;
+ friend class QApplication;
friend class QApplicationPrivate;
};
diff --git a/src/gui/kernel/qevent_p.h b/src/gui/kernel/qevent_p.h
index 5e24eeb..df7143e 100644
--- a/src/gui/kernel/qevent_p.h
+++ b/src/gui/kernel/qevent_p.h
@@ -98,6 +98,7 @@ public:
pressure(qreal(-1.))
{ }
+ QPointer<QWidget> widget;
int id;
Qt::TouchPointState state;
QPointF pos, startPos, lastPos;
diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h
index 8731551..df17566 100644
--- a/src/gui/kernel/qwidget_p.h
+++ b/src/gui/kernel/qwidget_p.h
@@ -663,6 +663,9 @@ public:
}
QSize adjustedSize() const;
+
+ QEvent::Type touchEventType;
+ QList<QTouchEvent::TouchPoint *> currentTouchPoints, activeTouchPoints;
};
inline QWExtra *QWidgetPrivate::extraData() const