5#include <AppKit/AppKit.h>
10#include <private/qpointingdevice_p.h>
14using namespace Qt::StringLiterals;
16NSMutableDictionary<NSObject<NSCopying> *, NSValue *> *QCocoaTouch::_currentTouches = [NSMutableDictionary
new];
17Q_CONSTINIT QHash<quint64, QPointingDevice*> QCocoaTouch::_touchDevices;
18Q_CONSTINIT QPointF QCocoaTouch::_screenReferencePos;
19Q_CONSTINIT QPointF QCocoaTouch::_trackpadReferencePos;
20int QCocoaTouch::_idAssignmentCount = 0;
21NSUInteger QCocoaTouch::_touchCount = 0;
22bool QCocoaTouch::_updateInternalStateOnly =
true;
24QCocoaTouch::QCocoaTouch(NSTouch *nstouch)
26 if (_currentTouches.count == 0)
27 _idAssignmentCount = 0;
29 _touchPoint.id = _idAssignmentCount++;
30 _touchPoint.pressure = 1.0;
31 _identity = [nstouch.identity retain];
32 _currentTouches[_identity] = [NSValue valueWithPointer:
this];
33 updateTouchData(nstouch, NSTouchPhaseBegan);
36QCocoaTouch::~QCocoaTouch()
38 [_currentTouches removeObjectForKey:_identity];
42void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase)
44 _touchPoint.state = toTouchPointState(phase);
49 NSPoint npos = [nstouch normalizedPosition];
50 QPointF qnpos = QPointF(npos.x, 1 - npos.y);
52 if (_touchPoint.id == 0 && phase == NSTouchPhaseBegan) {
53 _trackpadReferencePos = qnpos;
54 _screenReferencePos = QCocoaScreen::mapFromNative([NSEvent mouseLocation]);
57 QPointF screenPos = _screenReferencePos;
59 NSSize dsize = [nstouch deviceSize];
60 float ppiX = (qnpos.x() - _trackpadReferencePos.x()) * dsize.width;
61 float ppiY = (qnpos.y() - _trackpadReferencePos.y()) * dsize.height;
62 QPointF relativePos = _trackpadReferencePos - QPointF(ppiX, ppiY);
63 screenPos -= relativePos;
67 screenPos.rx() -= 0.5;
68 screenPos.ry() -= 0.5;
69 _touchPoint.area = QRectF(screenPos, QSize(1, 1));
72QCocoaTouch *QCocoaTouch::findQCocoaTouch(NSTouch *nstouch)
74 if (NSValue *touch = _currentTouches[nstouch.identity])
75 return static_cast<QCocoaTouch *>(touch.pointerValue);
79QEventPoint::State QCocoaTouch::toTouchPointState(NSTouchPhase nsState)
81 QEventPoint::State qtState = QEventPoint::State::Released;
83 case NSTouchPhaseBegan:
84 qtState = QEventPoint::State::Pressed;
86 case NSTouchPhaseMoved:
87 qtState = QEventPoint::State::Updated;
89 case NSTouchPhaseStationary:
90 qtState = QEventPoint::State::Stationary;
92 case NSTouchPhaseEnded:
93 case NSTouchPhaseCancelled:
94 qtState = QEventPoint::State::Released;
102QList<QWindowSystemInterface::TouchPoint>
103QCocoaTouch::getCurrentTouchPointList(NSEvent *event,
bool acceptSingleTouch)
105 QMap<
int, QWindowSystemInterface::TouchPoint> touchPoints;
106 NSSet *ended = [event touchesMatchingPhase:NSTouchPhaseEnded | NSTouchPhaseCancelled inView:nil];
107 NSSet *active = [event
108 touchesMatchingPhase:NSTouchPhaseBegan | NSTouchPhaseMoved | NSTouchPhaseStationary
110 _touchCount = [active count];
117 for (
int i=0; i<
int([ended count]); ++i) {
118 NSTouch *touch = [[ended allObjects] objectAtIndex:i];
119 QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
121 qcocoaTouch->updateTouchData(touch, [touch phase]);
122 if (!_updateInternalStateOnly)
123 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
128 bool wasUpdateInternalStateOnly = _updateInternalStateOnly;
129 _updateInternalStateOnly = !acceptSingleTouch && _touchCount < 2;
135 for (
int i=0; i<
int([active count]); ++i) {
136 NSTouch *touch = [[active allObjects] objectAtIndex:i];
137 QCocoaTouch *qcocoaTouch = findQCocoaTouch(touch);
139 qcocoaTouch =
new QCocoaTouch(touch);
141 qcocoaTouch->updateTouchData(touch, wasUpdateInternalStateOnly ? NSTouchPhaseBegan : [touch phase]);
142 if (!_updateInternalStateOnly)
143 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
150 if (_touchCount != _currentTouches.count) {
155 NSArray<NSValue *> *currentTouchesSnapshot = _currentTouches.allValues;
156 for (NSValue *touch in currentTouchesSnapshot) {
157 QCocoaTouch *qcocoaTouch =
static_cast<QCocoaTouch *>(touch.pointerValue);
158 if (!_updateInternalStateOnly) {
159 qcocoaTouch->_touchPoint.state = QEventPoint::State::Released;
160 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
164 [_currentTouches removeAllObjects];
165 _updateInternalStateOnly = !acceptSingleTouch;
166 return touchPoints.values();
173 if (_updateInternalStateOnly && !wasUpdateInternalStateOnly && _currentTouches.count > 0) {
174 QCocoaTouch *qcocoaTouch =
static_cast<QCocoaTouch *>(_currentTouches.allValues.firstObject.pointerValue);
175 qcocoaTouch->_touchPoint.state = QEventPoint::State::Released;
176 touchPoints.insert(qcocoaTouch->_touchPoint.id, qcocoaTouch->_touchPoint);
180 qcocoaTouch->_touchPoint.id = 0;
181 _idAssignmentCount = 1;
184 return touchPoints.values();
187QPointingDevice *QCocoaTouch::getTouchDevice(QInputDevice::DeviceType type, quint64 id)
189 QPointingDevice *ret = _touchDevices.value(id);
191 ret =
new QPointingDevice(type == QInputDevice::DeviceType::TouchScreen ?
"touchscreen"_L1 :
"trackpad"_L1,
192 id, type, QPointingDevice::PointerType::Finger,
193 QInputDevice::Capability::Position |
194 QInputDevice::Capability::MouseEmulation,
196 QWindowSystemInterface::registerInputDevice(ret);
197 _touchDevices.insert(id, ret);
Combined button and popup list for selecting options.