From f2c52d65608d238ad35ca91099a8751e0c37ef52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 22 Jan 2013 12:00:19 +0100 Subject: iOS: Implement touch events. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Track touch events during the standard [Began -> Moved -> Ended] event sequence based on the UITouch pointer which stays constant. Enable multiTouch on Qt's UIView. Mouse events should now be automatically created from (unhanded) touch events by QGuiApplication. Reviewed by: Ada Sørvig (fingerpaint app approved) Change-Id: I2aeb48c962c697d8b8337f8ceab062070c2a4240 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/ios/qiosintegration.h | 3 + src/plugins/platforms/ios/qiosintegration.mm | 11 +++ src/plugins/platforms/ios/qioswindow.h | 9 +++ src/plugins/platforms/ios/qioswindow.mm | 106 ++++++++++++++++++++++++--- 4 files changed, 118 insertions(+), 11 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index 5ba97bff6e..054933ea44 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -44,6 +44,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -71,10 +72,12 @@ public: void *nativeResourceForWindow(const QByteArray &resource, QWindow *window); + QTouchDevice *touchDevice(); private: QPlatformFontDatabase *m_fontDatabase; QPlatformInputContext *m_inputContext; QPlatformScreen *m_screen; + QTouchDevice *m_touchDevice; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 8008c5c0b0..cbe2717c34 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -72,10 +72,16 @@ QIOSIntegration::QIOSIntegration() } screenAdded(m_screen); + + m_touchDevice = new QTouchDevice; + m_touchDevice->setType(QTouchDevice::TouchScreen); + m_touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition); + QWindowSystemInterface::registerTouchDevice(m_touchDevice); } QIOSIntegration::~QIOSIntegration() { + delete m_touchDevice; } QPlatformWindow *QIOSIntegration::createPlatformWindow(QWindow *window) const @@ -157,4 +163,9 @@ void *QIOSIntegration::nativeResourceForWindow(const QByteArray &resource, QWind return 0; } +QTouchDevice *QIOSIntegration::touchDevice() +{ + return m_touchDevice; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h index 01c1978a56..b3a94a8d0e 100644 --- a/src/plugins/platforms/ios/qioswindow.h +++ b/src/plugins/platforms/ios/qioswindow.h @@ -43,6 +43,7 @@ #define QIOSWINDOW_H #include +#include #import @@ -77,8 +78,16 @@ public: UIView *nativeView() const { return m_view; } + QList &touchPoints() { return m_touchPoints; } + QHash &activeTouches() { return m_activeTouches; } + int &touchId() { return m_touchId; } + private: UIView *m_view; + QList m_touchPoints; + QHash m_activeTouches; + int m_touchId; + QRect m_requestedGeometry; qreal m_devicePixelRatio; diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index c5d9cbe323..91e75bc660 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -46,6 +46,7 @@ #include "qiosscreen.h" #include "qiosapplicationdelegate.h" #include "qiosviewcontroller.h" +#include "qiosintegration.h" #include #include @@ -115,6 +116,8 @@ if (isQtApplication()) self.hidden = YES; + + self.multipleTouchEnabled = YES; } return self; @@ -142,39 +145,119 @@ [super layoutSubviews]; } -- (void)sendMouseEventForTouches:(NSSet *)touches withEvent:(UIEvent *)event fakeButtons:(Qt::MouseButtons)buttons +/* + Touch handling: + + UIKit generates [Began -> Moved -> Ended] event sequences for + each touch point. The iOS plugin tracks each individual + touch and assigns it an id for use by Qt. The id counter is + incremented on each began and decrement as follows: + 1) by one when the most recent touch ends. + 2) to zero when all touches ends. + + The TouchPoint list is reused between events. +*/ +- (void)updateTouchList:(NSSet *)touches withState:(Qt::TouchPointState)state { - UITouch *touch = [touches anyObject]; - CGPoint locationInView = [touch locationInView:self]; - QPoint p(locationInView.x , locationInView.y); + QList &touchPoints = m_qioswindow->touchPoints(); + QHash &activeTouches = m_qioswindow->activeTouches(); + + // Mark all touch points as stationary + for (QList::iterator it = touchPoints.begin(); it != touchPoints.end(); ++it) + it->state = Qt::TouchPointStationary; + + // Update changed touch points with the new state + for (UITouch *touch in touches) { + const int touchId = activeTouches.value(touch); + QWindowSystemInterface::TouchPoint &touchPoint = touchPoints[touchId]; + touchPoint.state = state; + if (state == Qt::TouchPointPressed) + touchPoint.pressure = 1.0; + else if (state == Qt::TouchPointReleased) + touchPoint.pressure = 0.0; + + // Set position + CGPoint location = [touch locationInView:self]; + touchPoint.area = QRectF(location.x, location.y, 0, 0); + QSize viewSize = fromCGRect(self.frame).size(); + touchPoint.normalPosition = QPointF(location.x / viewSize.width(), location.y / viewSize.height()); + } +} - // TODO handle global touch point? for status bar? - QWindowSystemInterface::handleMouseEvent(m_qioswindow->window(), (ulong)(event.timestamp*1000), p, p, buttons); +- (void) sendTouchEventWithTimestamp:(ulong)timeStamp +{ + // Send touch event synchronously + QIOSIntegration *iosIntegration = static_cast(QGuiApplicationPrivate::platformIntegration()); + QWindowSystemInterface::handleTouchEvent(m_qioswindow->window(), timeStamp, + iosIntegration->touchDevice(), m_qioswindow->touchPoints()); + QWindowSystemInterface::flushWindowSystemEvents(); } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - // Transfer focus to the touched window: QWindow *window = m_qioswindow->window(); + + // Transfer focus to the touched window: if (window != QGuiApplication::focusWindow()) m_qioswindow->requestActivateWindow(); - [self sendMouseEventForTouches:touches withEvent:event fakeButtons:Qt::LeftButton]; + // Track Cocoa touch id to Qt touch id. The UITouch pointer is constant + // for the touch duration. + QHash &activeTouches = m_qioswindow->activeTouches(); + QList &touchPoints = m_qioswindow->touchPoints(); + for (UITouch *touch in touches) + activeTouches.insert(touch, m_qioswindow->touchId()++); + + // Create new touch points if needed. + int newTouchPointsNeeded = m_qioswindow->touchId() - touchPoints.count(); + for (int i = 0; i < newTouchPointsNeeded; ++i) { + QWindowSystemInterface::TouchPoint touchPoint; + touchPoint.id = touchPoints.count(); // id is the index in the touchPoints list. + touchPoints.append(touchPoint); + } + + [self updateTouchList:touches withState:Qt::TouchPointPressed]; + [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { - [self sendMouseEventForTouches:touches withEvent:event fakeButtons:Qt::LeftButton]; + [self updateTouchList:touches withState:Qt::TouchPointMoved]; + [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - [self sendMouseEventForTouches:touches withEvent:event fakeButtons:Qt::NoButton]; + [self updateTouchList:touches withState:Qt::TouchPointReleased]; + [self sendTouchEventWithTimestamp:ulong(event.timestamp * 1000)]; + + // Remove ended touch points from the active set (event processing has completed at this point) + QHash &activeTouches = m_qioswindow->activeTouches(); + for (UITouch *touch in touches) { + int id = activeTouches.take(touch); + + // If this touch is the most recent touch we can reuse its id + if (id == m_qioswindow->touchId() - 1) + --m_qioswindow->touchId(); + } + + // Reset the touch id when there are no more active touches + if (activeTouches.isEmpty()) + m_qioswindow->touchId() = 0; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { - [self sendMouseEventForTouches:touches withEvent:event fakeButtons:Qt::NoButton]; + Q_UNUSED(touches) // ### can a subset of the active touches be cancelled? + + // Clear current touch points + m_qioswindow->activeTouches().clear(); + m_qioswindow->touchId() = 0; + + // Send cancel touch event synchronously + QIOSIntegration *iosIntegration = static_cast(QGuiApplicationPrivate::platformIntegration()); + QWindowSystemInterface::handleTouchCancelEvent(m_qioswindow->window(), ulong(event.timestamp * 1000), iosIntegration->touchDevice()); + QWindowSystemInterface::flushWindowSystemEvents(); } @synthesize autocapitalizationType; @@ -236,6 +319,7 @@ QT_BEGIN_NAMESPACE QIOSWindow::QIOSWindow(QWindow *window) : QPlatformWindow(window) , m_view([[EAGLView alloc] initWithQIOSWindow:this]) + , m_touchId(0) , m_requestedGeometry(QPlatformWindow::geometry()) , m_devicePixelRatio(1.0) { -- cgit v1.2.3