iOS: Implement touch events.

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ø <tor.arne.vestbo@digia.com>
This commit is contained in:
Morten Johan Sørvig 2013-01-22 12:00:19 +01:00 committed by Tor Arne Vestbø
parent 0a9a4e826f
commit f2c52d6560
4 changed files with 119 additions and 12 deletions

View File

@ -44,6 +44,7 @@
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qwindowsysteminterface.h>
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

View File

@ -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

View File

@ -43,6 +43,7 @@
#define QIOSWINDOW_H
#include <qpa/qplatformwindow.h>
#include <qpa/qwindowsysteminterface.h>
#import <UIKit/UIKit.h>
@ -77,8 +78,16 @@ public:
UIView *nativeView() const { return m_view; }
QList<QWindowSystemInterface::TouchPoint> &touchPoints() { return m_touchPoints; }
QHash<UITouch *, int> &activeTouches() { return m_activeTouches; }
int &touchId() { return m_touchId; }
private:
UIView *m_view;
QList<QWindowSystemInterface::TouchPoint> m_touchPoints;
QHash<UITouch *, int> m_activeTouches;
int m_touchId;
QRect m_requestedGeometry;
qreal m_devicePixelRatio;

View File

@ -46,6 +46,7 @@
#include "qiosscreen.h"
#include "qiosapplicationdelegate.h"
#include "qiosviewcontroller.h"
#include "qiosintegration.h"
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
@ -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
{
UITouch *touch = [touches anyObject];
CGPoint locationInView = [touch locationInView:self];
QPoint p(locationInView.x , locationInView.y);
/*
Touch handling:
// TODO handle global touch point? for status bar?
QWindowSystemInterface::handleMouseEvent(m_qioswindow->window(), (ulong)(event.timestamp*1000), p, p, buttons);
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
{
QList<QWindowSystemInterface::TouchPoint> &touchPoints = m_qioswindow->touchPoints();
QHash<UITouch *, int> &activeTouches = m_qioswindow->activeTouches();
// Mark all touch points as stationary
for (QList<QWindowSystemInterface::TouchPoint>::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());
}
}
- (void) sendTouchEventWithTimestamp:(ulong)timeStamp
{
// Send touch event synchronously
QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(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<UITouch *, int> &activeTouches = m_qioswindow->activeTouches();
QList<QWindowSystemInterface::TouchPoint> &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<UITouch *, int> &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<QIOSIntegration *>(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)
{