From 2429c73cc9b421c09e47e1dc59007d1fdc394057 Mon Sep 17 00:00:00 2001 From: HIDAKA Takahiro Date: Sat, 11 Jan 2025 01:50:09 +0900 Subject: [PATCH] iOS: Add support for hover feature for Apple Pencil 2nd gen or later MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add UIHoverGestureRecognizer to support Hover feature. Task-number: QTBUG-128473 Change-Id: I73bbd1b8cca1ffcb8fcc27f6c26cb4ab8830c690 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/ios/quiview.mm | 92 +++++++++++++++++++++------- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm index 734105a6443..9b17eef07b0 100644 --- a/src/plugins/platforms/ios/quiview.mm +++ b/src/plugins/platforms/ios/quiview.mm @@ -59,6 +59,9 @@ inline ulong getTimeStamp(UIEvent *event) UIPanGestureRecognizer *m_scrollGestureRecognizer; CGPoint m_lastScrollCursorPos; CGPoint m_lastScrollDelta; +#if QT_CONFIG(tabletevent) + UIHoverGestureRecognizer *m_hoverGestureRecognizer; +#endif } + (Class)layerClass @@ -99,6 +102,13 @@ inline ulong getTimeStamp(UIEvent *event) m_lastScrollCursorPos = CGPointZero; [self addGestureRecognizer:m_scrollGestureRecognizer]; +#if QT_CONFIG(tabletevent) + m_hoverGestureRecognizer = [[UIHoverGestureRecognizer alloc] + initWithTarget:self + action:@selector(handleHover:)]; + [self addGestureRecognizer:m_hoverGestureRecognizer]; +#endif + // Set up layer if ([self.layer isKindOfClass:CAMetalLayer.class]) { QWindow *window = self.platformWindow->window(); @@ -362,6 +372,39 @@ inline ulong getTimeStamp(UIEvent *event) return [super pointInside:point withEvent:event]; } +#if QT_CONFIG(tabletevent) +- (void)handlePencilEventForLocationInView:(CGPoint)locationInView withState:(QEventPoint::State)state withTimestamp:(ulong)timeStamp + withForce:(CGFloat)force withMaximumPossibleForce:(CGFloat)maximumPossibleForce withZOffset:(CGFloat)zOffset + withAzimuthUnitVector:(CGVector)azimuth withAltitudeAngleRadian:(CGFloat)altitudeAngleRadian +{ + QIOSIntegration *iosIntegration = QIOSIntegration::instance(); + + QPointF localViewPosition = QPointF::fromCGPoint(locationInView); + QPoint localViewPositionI = localViewPosition.toPoint(); + QPointF globalScreenPosition = self.platformWindow->mapToGlobal(localViewPositionI) + + (localViewPosition - localViewPositionI); + qreal pressure = 0; + if (force != 0 && maximumPossibleForce != 0) + pressure = force / maximumPossibleForce; + // azimuth unit vector: +x to the right, +y going downwards + // altitudeAngleRadian given in radians, pi / 2 is with the stylus perpendicular to the iPad, smaller values mean more tilted, but never negative. + // Convert to degrees with zero being perpendicular. + qreal altitudeAngle = 90 - qRadiansToDegrees(altitudeAngleRadian); + qreal xTilt = qBound(-60.0, altitudeAngle * azimuth.dx, 60.0); + qreal yTilt = qBound(-60.0, altitudeAngle * azimuth.dy, 60.0); + + qCDebug(lcQpaTablet) << ":" << timeStamp << localViewPosition << pressure << state << "azimuth" << azimuth.dx << azimuth.dy + << "altitude" << altitudeAngleRadian << "xTilt" << xTilt << "yTilt" << yTilt; + QWindowSystemInterface::handleTabletEvent(self.platformWindow->window(), timeStamp, + // device, local, global + iosIntegration->pencilDevice(), localViewPosition, globalScreenPosition, + // buttons + state == QEventPoint::State::Released ? Qt::NoButton : Qt::LeftButton, + // pressure, xTilt, yTilt, tangentialPressure, rotation, z, modifiers + pressure, xTilt, yTilt, 0, 0, zOffset, Qt::NoModifier); +} +#endif + - (void)handleTouches:(NSSet *)touches withEvent:(UIEvent *)event withState:(QEventPoint::State)state withTimestamp:(ulong)timeStamp { QIOSIntegration *iosIntegration = QIOSIntegration::instance(); @@ -370,30 +413,11 @@ inline ulong getTimeStamp(UIEvent *event) #if QT_CONFIG(tabletevent) if (m_activePencilTouch && [touches containsObject:m_activePencilTouch]) { NSArray *cTouches = [event coalescedTouchesForTouch:m_activePencilTouch]; - int i = 0; for (UITouch *cTouch in cTouches) { - QPointF localViewPosition = QPointF::fromCGPoint([cTouch preciseLocationInView:self]); - QPoint localViewPositionI = localViewPosition.toPoint(); - QPointF globalScreenPosition = self.platformWindow->mapToGlobal(localViewPositionI) + - (localViewPosition - localViewPositionI); - qreal pressure = cTouch.force / cTouch.maximumPossibleForce; - // azimuth unit vector: +x to the right, +y going downwards - CGVector azimuth = [cTouch azimuthUnitVectorInView:self]; - // altitudeAngle given in radians, pi / 2 is with the stylus perpendicular to the iPad, smaller values mean more tilted, but never negative. - // Convert to degrees with zero being perpendicular. - qreal altitudeAngle = 90 - qRadiansToDegrees(cTouch.altitudeAngle); - qreal xTilt = qBound(-60.0, altitudeAngle * azimuth.dx, 60.0); - qreal yTilt = qBound(-60.0, altitudeAngle * azimuth.dy, 60.0); - qCDebug(lcQpaTablet) << i << ":" << timeStamp << localViewPosition << pressure << state << "azimuth" << azimuth.dx << azimuth.dy - << "altitude" << cTouch.altitudeAngle << "xTilt" << xTilt << "yTilt" << yTilt; - QWindowSystemInterface::handleTabletEvent(self.platformWindow->window(), timeStamp, - // device, local, global - iosIntegration->pencilDevice(), localViewPosition, globalScreenPosition, - // buttons - state == QEventPoint::State::Released ? Qt::NoButton : Qt::LeftButton, - // pressure, xTilt, yTilt, tangentialPressure, rotation, z, modifiers - pressure, xTilt, yTilt, 0, 0, 0, Qt::NoModifier); - ++i; + [self handlePencilEventForLocationInView:[cTouch preciseLocationInView:self] withState:state withTimestamp:timeStamp + withForce:cTouch.force withMaximumPossibleForce:cTouch.maximumPossibleForce withZOffset:0 + withAzimuthUnitVector:[cTouch azimuthUnitVectorInView:self] + withAltitudeAngleRadian:cTouch.altitudeAngle]; } } #endif @@ -759,6 +783,28 @@ inline ulong getTimeStamp(UIEvent *event) } #endif // QT_CONFIG(wheelevent) +#if QT_CONFIG(tabletevent) +- (void)handleHover:(UIHoverGestureRecognizer *)recognizer +{ + ulong timeStamp = [[NSProcessInfo processInfo] systemUptime] * 1000; + + CGFloat zOffset = 0; + if (@available(ios 16.1, *)) + zOffset = [recognizer zOffset]; + + CGVector azimuth; + CGFloat altitudeAngleRadian = 0; + if (@available(ios 16.4, *)) { + azimuth = [recognizer azimuthUnitVectorInView:self]; + altitudeAngleRadian = recognizer.altitudeAngle; + } + + [self handlePencilEventForLocationInView:[recognizer locationInView:self] withState:QEventPoint::State::Released + withTimestamp:timeStamp withForce:0 withMaximumPossibleForce:0 withZOffset:zOffset + withAzimuthUnitVector:azimuth withAltitudeAngleRadian:altitudeAngleRadian]; +} +#endif + @end @implementation UIView (QtHelpers)