From 77fcd47febec58eaf35610c72f2d9bdfb1ee4174 Mon Sep 17 00:00:00 2001 From: Doris Verria Date: Wed, 15 Jun 2022 14:57:04 +0200 Subject: iOS plugin: Add support for wheel events Use UIPanGestureRecognizer to handle mouse and trackpad scroll. Task-number: QTBUG-99795 Change-Id: I5db7fabf55b6f2e6f7ba90a0b90ecb08734e2329 Reviewed-by: Shawn Rutledge --- src/plugins/platforms/ios/quiview.mm | 81 ++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) (limited to 'src') diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm index 32f27587c6..cd4e900d89 100644 --- a/src/plugins/platforms/ios/quiview.mm +++ b/src/plugins/platforms/ios/quiview.mm @@ -22,12 +22,16 @@ #include Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") +Q_LOGGING_CATEGORY(lcQpaInputEvents, "qt.qpa.input.events") @implementation QUIView { QHash m_activeTouches; UITouch *m_activePencilTouch; int m_nextTouchId; NSMutableArray *m_accessibleElements; + UIPanGestureRecognizer *m_scrollGestureRecognizer; + CGPoint m_lastScrollCursorPos; + CGPoint m_lastScrollDelta; } + (void)load @@ -60,6 +64,23 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") if (self = [self initWithFrame:window->geometry().toCGRect()]) { self.platformWindow = window; m_accessibleElements = [[NSMutableArray alloc] init]; + m_scrollGestureRecognizer = [[UIPanGestureRecognizer alloc] + initWithTarget:self + action:@selector(handleScroll:)]; + // The gesture recognizer should only care about scroll gestures (for now) + // Set allowedTouchTypes to empty array here to not interfere with touch events + // handled by the UIView. Scroll gestures, even those coming from touch devices, + // such as trackpads will still be received as they are not touch events + m_scrollGestureRecognizer.allowedTouchTypes = [NSArray array]; +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_13_4) + if (@available(ios 13.4, *)) { + m_scrollGestureRecognizer.allowedScrollTypesMask = UIScrollTypeMaskAll; + } +#endif + m_scrollGestureRecognizer.maximumNumberOfTouches = 0; + m_lastScrollDelta = CGPointZero; + m_lastScrollCursorPos = CGPointZero; + [self addGestureRecognizer:m_scrollGestureRecognizer]; } return self; @@ -117,6 +138,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") - (void)dealloc { [m_accessibleElements release]; + [m_scrollGestureRecognizer release]; [super dealloc]; } @@ -672,6 +694,65 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") UIEditingInteractionConfigurationDefault : UIEditingInteractionConfigurationNone; } +#if QT_CONFIG(wheelevent) +- (void)handleScroll:(UIPanGestureRecognizer *)recognizer +{ + if (!self.platformWindow->window()) + return; + + if (!self.canBecomeFirstResponder) + return; + + CGPoint translation = [recognizer translationInView:self]; + CGFloat deltaX = translation.x - m_lastScrollDelta.x; + CGFloat deltaY = translation.y - m_lastScrollDelta.y; + + QPoint angleDelta; + // From QNSView implementation: + // "Since deviceDelta is delivered as pixels rather than degrees, we need to + // convert from pixels to degrees in a sensible manner. + // It looks like 1/4 degrees per pixel behaves most native. + // (NB: Qt expects the unit for delta to be 8 per degree):" + const int pixelsToDegrees = 2; // 8 * 1/4 + angleDelta.setX(deltaX * pixelsToDegrees); + angleDelta.setY(deltaY * pixelsToDegrees); + + QPoint pixelDelta; + pixelDelta.setX(deltaX); + pixelDelta.setY(deltaY); + + NSTimeInterval time_stamp = [[NSProcessInfo processInfo] systemUptime]; + ulong qt_timestamp = time_stamp * 1000; + + Qt::KeyboardModifiers qt_modifierFlags = Qt::NoModifier; +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_13_4) + if (@available(ios 13.4, *)) + qt_modifierFlags = QAppleKeyMapper::fromUIKitModifiers(recognizer.modifierFlags); +#endif + + if (recognizer.state == UIGestureRecognizerStateBegan) + // locationInView: doesn't return the cursor position at the time of the wheel event, + // but rather gives us the position with the deltas applied, so we need to save the + // cursor position at the beginning of the gesture + m_lastScrollCursorPos = [recognizer locationInView:self]; + + if (recognizer.state != UIGestureRecognizerStateEnded) { + m_lastScrollDelta.x = translation.x; + m_lastScrollDelta.y = translation.y; + } else { + m_lastScrollDelta = CGPointZero; + } + + QPoint qt_local = QPointF::fromCGPoint(m_lastScrollCursorPos).toPoint(); + QPoint qt_global = self.platformWindow->mapToGlobal(qt_local); + + qCInfo(lcQpaInputEvents).nospace() << "wheel event" << " at " << qt_local + << " pixelDelta=" << pixelDelta << " angleDelta=" << angleDelta; + + QWindowSystemInterface::handleWheelEvent(self.platformWindow->window(), qt_timestamp, qt_local, qt_global, pixelDelta, angleDelta, qt_modifierFlags); +} +#endif // QT_CONFIG(wheelevent) + @end @implementation UIView (QtHelpers) -- cgit v1.2.3