iOS: don't send ended touch events to QPA
The current implementation kept a list of TouchPoints that was reused when sending active touces to QPA. This list was never cleaned up, so if you pressed three fingers, and released one, we would still continue to sendt three touches to QPA. Especially, since this list was not cleaned up when receiving a touch cancel, mouse events sometimes stopped working when trigging a system gesture (like a four finger swipe). This can be seen by using the fingerpaint example. Since we cannot rely on TouchPoints having IDs that corresponds to their index in the touch point list, it ends up being simpler (and results in less code) to rewrite the implementation to use a hash table of UITouch to TouchPoints instead. Change-Id: I5b32f57a8d72a0b8759a64ac7cdfa6700109d2b3 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@digia.com>
This commit is contained in:
parent
53e01c4329
commit
758f064558
@ -82,15 +82,8 @@ public:
|
||||
|
||||
WId winId() const { return WId(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;
|
||||
int m_windowLevel;
|
||||
|
@ -68,6 +68,8 @@
|
||||
UIReturnKeyType returnKeyType;
|
||||
BOOL secureTextEntry;
|
||||
QIOSWindow *m_qioswindow;
|
||||
QHash<UITouch *, QWindowSystemInterface::TouchPoint> m_activeTouches;
|
||||
int m_nextTouchId;
|
||||
}
|
||||
|
||||
@property(nonatomic) UITextAutocapitalizationType autocapitalizationType;
|
||||
@ -113,6 +115,7 @@
|
||||
keyboardType = UIKeyboardTypeDefault;
|
||||
returnKeyType = UIReturnKeyDone;
|
||||
secureTextEntry = NO;
|
||||
m_nextTouchId = 0;
|
||||
|
||||
if (isQtApplication())
|
||||
self.hidden = YES;
|
||||
@ -145,45 +148,25 @@
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
{
|
||||
QList<QWindowSystemInterface::TouchPoint> &touchPoints = m_qioswindow->touchPoints();
|
||||
QHash<UITouch *, int> &activeTouches = m_qioswindow->activeTouches();
|
||||
foreach (UITouch *uiTouch, m_activeTouches.keys()) {
|
||||
QWindowSystemInterface::TouchPoint &touchPoint = m_activeTouches[uiTouch];
|
||||
if (![touches containsObject:uiTouch]) {
|
||||
touchPoint.state = Qt::TouchPointStationary;
|
||||
} else {
|
||||
touchPoint.state = state;
|
||||
touchPoint.pressure = (state == Qt::TouchPointReleased) ? 0.0 : 1.0;
|
||||
|
||||
// Mark all touch points as stationary
|
||||
for (QList<QWindowSystemInterface::TouchPoint>::iterator it = touchPoints.begin(); it != touchPoints.end(); ++it)
|
||||
it->state = Qt::TouchPointStationary;
|
||||
// Set position
|
||||
QRect viewGeometry = fromCGRect(self.frame);
|
||||
QPoint touchViewLocation = fromCGPoint([uiTouch locationInView:self]);
|
||||
QPoint touchScreenLocation = touchViewLocation + viewGeometry.topLeft();
|
||||
touchPoint.area = QRectF(touchScreenLocation , QSize(0, 0));
|
||||
|
||||
// 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
|
||||
QRect viewGeometry = fromCGRect(self.frame);
|
||||
QPoint touchViewLocation = fromCGPoint([touch locationInView:self]);
|
||||
QPoint touchScreenLocation = touchViewLocation + viewGeometry.topLeft();
|
||||
touchPoint.area = QRectF(touchScreenLocation , QSize(0, 0));
|
||||
|
||||
CGSize fullscreenSize = self.window.rootViewController.view.bounds.size;
|
||||
touchPoint.normalPosition = QPointF(touchScreenLocation.x() / fullscreenSize.width, touchScreenLocation.y() / fullscreenSize.height);
|
||||
CGSize fullscreenSize = self.window.rootViewController.view.bounds.size;
|
||||
touchPoint.normalPosition = QPointF(touchScreenLocation.x() / fullscreenSize.width, touchScreenLocation.y() / fullscreenSize.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,8 +174,7 @@
|
||||
{
|
||||
// Send touch event synchronously
|
||||
QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration());
|
||||
QWindowSystemInterface::handleTouchEvent(m_qioswindow->window(), timeStamp,
|
||||
iosIntegration->touchDevice(), m_qioswindow->touchPoints());
|
||||
QWindowSystemInterface::handleTouchEvent(m_qioswindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values());
|
||||
QWindowSystemInterface::flushWindowSystemEvents();
|
||||
}
|
||||
|
||||
@ -204,19 +186,13 @@
|
||||
if (window != QGuiApplication::focusWindow())
|
||||
m_qioswindow->requestActivateWindow();
|
||||
|
||||
// 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);
|
||||
// UIKit generates [Began -> Moved -> Ended] event sequences for
|
||||
// each touch point. Internally we keep a hashmap of active UITouch
|
||||
// points to QWindowSystemInterface::TouchPoints, and assigns each TouchPoint
|
||||
// an id for use by Qt.
|
||||
for (UITouch *touch in touches) {
|
||||
Q_ASSERT(!m_activeTouches.contains(touch));
|
||||
m_activeTouches[touch].id = m_nextTouchId++;
|
||||
}
|
||||
|
||||
[self updateTouchList:touches withState:Qt::TouchPointPressed];
|
||||
@ -234,19 +210,11 @@
|
||||
[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;
|
||||
// Remove ended touch points from the active set:
|
||||
for (UITouch *touch in touches)
|
||||
m_activeTouches.remove(touch);
|
||||
if (m_activeTouches.isEmpty())
|
||||
m_nextTouchId = 0;
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
@ -254,8 +222,8 @@
|
||||
Q_UNUSED(touches) // ### can a subset of the active touches be cancelled?
|
||||
|
||||
// Clear current touch points
|
||||
m_qioswindow->activeTouches().clear();
|
||||
m_qioswindow->touchId() = 0;
|
||||
m_activeTouches.clear();
|
||||
m_nextTouchId = 0;
|
||||
|
||||
// Send cancel touch event synchronously
|
||||
QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration());
|
||||
@ -322,7 +290,6 @@ QT_BEGIN_NAMESPACE
|
||||
QIOSWindow::QIOSWindow(QWindow *window)
|
||||
: QPlatformWindow(window)
|
||||
, m_view([[EAGLView alloc] initWithQIOSWindow:this])
|
||||
, m_touchId(0)
|
||||
, m_requestedGeometry(QPlatformWindow::geometry())
|
||||
, m_windowLevel(0)
|
||||
, m_devicePixelRatio(1.0)
|
||||
|
Loading…
Reference in New Issue
Block a user