From 627a3b8928157bb7f25af406f61a2f9c5cb41e06 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Mon, 22 Jun 2009 13:20:03 +0200 Subject: Multitouch, Cocoa: Mask mouse hover from the touch set After a lot of thinking, the conclusion is that we really need to to this. Lets see if we can add a flag to control it next. --- src/gui/kernel/qcocoaview_mac.mm | 18 ++--- src/gui/kernel/qmultitouch_mac.mm | 140 +++++++++++++++++++++---------------- src/gui/kernel/qmultitouch_mac_p.h | 10 +-- 3 files changed, 88 insertions(+), 80 deletions(-) diff --git a/src/gui/kernel/qcocoaview_mac.mm b/src/gui/kernel/qcocoaview_mac.mm index 9aa4dd2..e8e52f9 100644 --- a/src/gui/kernel/qcocoaview_mac.mm +++ b/src/gui/kernel/qcocoaview_mac.mm @@ -704,11 +704,6 @@ extern "C" { - (void)mouseUp:(NSEvent *)theEvent { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) - QCocoaTouch::setMouseInDraggingState(false); -#endif - bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::LeftButton); if (!mouseOK) @@ -752,11 +747,6 @@ extern "C" { - (void)mouseDragged:(NSEvent *)theEvent { -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) - QCocoaTouch::setMouseInDraggingState(true); -#endif - qMacDnDParams()->view = self; qMacDnDParams()->theEvent = theEvent; bool mouseOK = qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::LeftButton); @@ -883,22 +873,22 @@ extern "C" { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - (void)touchesBeganWithEvent:(NSEvent *)event; { - qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event), qwidget); + qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event, true), qwidget); } - (void)touchesMovedWithEvent:(NSEvent *)event; { - qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event), qwidget); + qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event, true), qwidget); } - (void)touchesEndedWithEvent:(NSEvent *)event; { - qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event), qwidget); + qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event, true), qwidget); } - (void)touchesCancelledWithEvent:(NSEvent *)event; { - qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event), qwidget); + qt_translateRawTouchEvent(QCocoaTouch::getCurrentTouchPointList(event, true), qwidget); } - (void)magnifyWithEvent:(NSEvent *)event; diff --git a/src/gui/kernel/qmultitouch_mac.mm b/src/gui/kernel/qmultitouch_mac.mm index 89cfc24..0f43299 100644 --- a/src/gui/kernel/qmultitouch_mac.mm +++ b/src/gui/kernel/qmultitouch_mac.mm @@ -39,11 +39,11 @@ ** ****************************************************************************/ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - #include #include +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + QT_BEGIN_NAMESPACE #ifdef QT_MAC_USE_COCOA @@ -51,33 +51,28 @@ QT_BEGIN_NAMESPACE QHash QCocoaTouch::_currentTouches; QPointF QCocoaTouch::_screenReferencePos; QPointF QCocoaTouch::_trackpadReferencePos; -bool QCocoaTouch::_inMouseDraggingState = false; int QCocoaTouch::_idAssignmentCount = 0; +int QCocoaTouch::_touchCount = 0; +bool QCocoaTouch::_maskMouseHover = true; QCocoaTouch::QCocoaTouch(NSTouch *nstouch) { + // Keep the identity that Cocoa assigns the + // touches, and use it as key in the touch hash: _identity = int([nstouch identity]); if (_currentTouches.size() == 0){ - // A new touch sequence is about to begin: - _touchPoint.setState(Qt::TouchPointPressed | Qt::TouchPointPrimary); - NSPoint npos = [nstouch normalizedPosition]; - _trackpadPos = QPointF(npos.x, npos.y); - // This touch will act as reference for all subsequent - // touches, so they appear next to each other on screen: - _trackpadReferencePos = _trackpadPos; - _screenReferencePos = QCursor::pos(); - _touchPoint.setScreenPos(_screenReferencePos); - // The first touch should always have 0 as id: + // The first touch should always have + // 0 as id that we use in Qt: _touchPoint.setId(0); _idAssignmentCount = 0; - _currentTouches.insert(_identity, this); } else { - // We are already tracking at least one touch point. _touchPoint.setId(++_idAssignmentCount); - _currentTouches.insert(_identity, this); - updateTouchData(nstouch, NSTouchPhaseBegan); } + + _touchPoint.setPressure(1.0); + _currentTouches.insert(_identity, this); + updateTouchData(nstouch, NSTouchPhaseBegan); } QCocoaTouch::~QCocoaTouch() @@ -87,10 +82,7 @@ QCocoaTouch::~QCocoaTouch() void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase) { - NSPoint npos = [nstouch normalizedPosition]; - _trackpadPos = QPointF(npos.x, npos.y); - - if (_inMouseDraggingState || _currentTouches.size() == 1) + if (_touchCount == 1) _touchPoint.setState(toTouchPointState(phase) | Qt::TouchPointPrimary); else _touchPoint.setState(toTouchPointState(phase)); @@ -98,6 +90,14 @@ void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase) // From the normalized position on the trackpad, calculate // where on screen the touchpoint should be according to the // reference position: + NSPoint npos = [nstouch normalizedPosition]; + _trackpadPos = QPointF(npos.x, npos.y); + + if (_touchPoint.id() == 0 && phase == NSTouchPhaseBegan) { + _trackpadReferencePos = _trackpadPos; + _screenReferencePos = QCursor::pos(); + } + NSSize dsize = [nstouch deviceSize]; float ppiX = (_trackpadPos.x() - _trackpadReferencePos.x()) * dsize.width; float ppiY = (_trackpadPos.y() - _trackpadReferencePos.y()) * dsize.height; @@ -136,65 +136,83 @@ Qt::TouchPointState QCocoaTouch::toTouchPointState(NSTouchPhase nsState) return qtState; } -void QCocoaTouch::setMouseInDraggingState(bool inDraggingState) -{ - // In mouse dragging state, _all_ fingers control the mouse. - // As such, all fingers should have the primary flag. - _inMouseDraggingState = inDraggingState; -} - -void QCocoaTouch::validateCurrentTouchList(NSEvent *event) -{ - // If the application shows the task switcher during a touch sequence - // we get into an inconsistant state. We try to detect that here: - NSTouchPhase p = NSTouchPhaseAny & ~NSTouchPhaseBegan; - NSSet *s = [event touchesMatchingPhase:p inView:nil]; - if ([s count] == 0 && !_currentTouches.isEmpty()) { - // Remove all instances, and basically start from scratch: - QList list = _currentTouches.values(); - foreach (QCocoaTouch *t, _currentTouches.values()) - delete t; - _currentTouches.clear(); - } -} - -QList QCocoaTouch::getCurrentTouchPointList(NSEvent *event) +QList QCocoaTouch::getCurrentTouchPointList(NSEvent *event, bool maskMouseHover) { - validateCurrentTouchList(event); - // Go through all the touchpoints in cocoa, find, or create, a QCocoaTouch for - // them, update them, and return the result: QList touchPoints; - NSSet *ended = [event touchesMatchingPhase:NSTouchPhaseEnded | NSTouchPhaseCancelled inView:nil]; + NSSet *active = [event + touchesMatchingPhase:NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary + inView:nil]; + _touchCount = [active count]; + + // First: remove touches that were ended by the user. If we are + // currently masking the mouse hover touch, a corresponding 'begin' + // has never been posted to the app. So we should not send + // the following remove either. + for (int i=0; iupdateTouchData(touch, [touch phase]); - touchPoints.append(qcocoaTouch->_touchPoint); + if (!_maskMouseHover) + touchPoints.append(qcocoaTouch->_touchPoint); delete qcocoaTouch; } } - NSSet *began = [event touchesMatchingPhase:NSTouchPhaseBegan inView:nil]; - for (int i=0; iupdateTouchData(touch, [touch phase]); - touchPoints.append(qcocoaTouch->_touchPoint); + qcocoaTouch->updateTouchData(touch, wasMaskingMouseHover ? NSTouchPhaseBegan : [touch phase]); + if (!_maskMouseHover) + touchPoints.append(qcocoaTouch->_touchPoint); } - NSSet *active = [event touchesMatchingPhase:NSTouchPhaseMoved | NSTouchPhaseStationary inView:nil]; - for (int i=0; iupdateTouchData(touch, [touch phase]); - touchPoints.append(qcocoaTouch->_touchPoint); + // Next: sadly, we need to check that our touch hash is in + // sync with cocoa. This is typically not the case after a system + // gesture happend (like a four-finger-swipe to show expose). + + if (_touchCount != _currentTouches.size()) { + // Remove all instances, and basically start from scratch: + touchPoints.clear(); + QList list = _currentTouches.values(); + foreach (QCocoaTouch *qcocoaTouch, _currentTouches.values()) { + if (!_maskMouseHover) { + qcocoaTouch->_touchPoint.setState(Qt::TouchPointReleased); + touchPoints.append(qcocoaTouch->_touchPoint); + } + delete qcocoaTouch; } + _currentTouches.clear(); + _maskMouseHover = maskMouseHover; + return touchPoints; + } + + // Finally: If we in this call _started_ to mask the mouse + // hover touch, we need to fake a relase for it now (and refake + // a begin for it later, if needed). + + if (_maskMouseHover && !wasMaskingMouseHover && !_currentTouches.isEmpty()) { + // If one touch is still active, fake a release event for it. + QCocoaTouch *qcocoaTouch = _currentTouches.values().first(); + qcocoaTouch->_touchPoint.setState(Qt::TouchPointReleased); + touchPoints.append(qcocoaTouch->_touchPoint); + // Since this last touch also will end up beeing the first + // touch (if the user adds a second finger without lifting + // the first), we promote it to be the primary touch: + qcocoaTouch->_touchPoint.setId(0); + _idAssignmentCount = 0; } return touchPoints; diff --git a/src/gui/kernel/qmultitouch_mac_p.h b/src/gui/kernel/qmultitouch_mac_p.h index 92ed11a..9ec256a 100644 --- a/src/gui/kernel/qmultitouch_mac_p.h +++ b/src/gui/kernel/qmultitouch_mac_p.h @@ -53,8 +53,6 @@ #ifndef QMULTITOUCH_MAC_P_H #define QMULTITOUCH_MAC_P_H -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - #ifdef QT_MAC_USE_COCOA #import #endif @@ -63,6 +61,8 @@ #include #include +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + QT_BEGIN_NAMESPACE #ifdef QT_MAC_USE_COCOA @@ -70,15 +70,16 @@ QT_BEGIN_NAMESPACE class QCocoaTouch { public: - static QList getCurrentTouchPointList(NSEvent *event); + static QList getCurrentTouchPointList(NSEvent *event, bool maskMouseHover); static void setMouseInDraggingState(bool inDraggingState); private: static QHash _currentTouches; static QPointF _screenReferencePos; static QPointF _trackpadReferencePos; - static bool _inMouseDraggingState; static int _idAssignmentCount; + static int _touchCount; + static bool _maskMouseHover; QTouchEvent::TouchPoint _touchPoint; QPointF _trackpadPos; @@ -90,7 +91,6 @@ class QCocoaTouch void updateTouchData(NSTouch *nstouch, NSTouchPhase phase); static QCocoaTouch *findQCocoaTouch(NSTouch *nstouch); static Qt::TouchPointState toTouchPointState(NSTouchPhase nsState); - static void validateCurrentTouchList(NSEvent *event); }; #endif // QT_MAC_USE_COCOA -- cgit v0.12