19#include <QtCore/qmath.h>
20#include <QtGui/qpointingdevice.h>
21#include <QtGui/private/qguiapplication_p.h>
22#include <QtGui/private/qwindow_p.h>
23#include <QtGui/private/qapplekeymapper_p.h>
24#include <QtGui/private/qpointingdevice_p.h>
25#include <qpa/qwindowsysteminterface_p.h>
28Q_LOGGING_CATEGORY(lcQpaTablet,
"qt.qpa.input.tablet")
29Q_LOGGING_CATEGORY(lcQpaInputEvents,
"qt.qpa.input.events")
32inline ulong getTimeStamp(UIEvent *event)
34 bool useEvent = event != nil;
36#if TARGET_OS_SIMULATOR == 1
49#if defined(Q_PROCESSOR_ARM)
50 #warning The timestamp work-around for x86_64 can (probably) be removed when building for ARM
55 return ulong(useEvent ? event.timestamp : NSProcessInfo.processInfo.systemUptime) * 1000;
59#if QT_CONFIG(tabletevent)
60@protocol TabletEventSender
61- (CGPoint)locationInView:(UIView *)view;
62- (CGVector)azimuthUnitVectorInView:(UIView *)view;
63@property (nonatomic, readonly) CGFloat altitudeAngle;
64@property (nonatomic, readonly) CGFloat rollAngle;
66@interface UIGestureRecognizer (TabletEventSender) <TabletEventSender> @end
67@interface UITouch (TabletEventSender) <TabletEventSender> @end
70@implementation QUIView {
71 QHash<NSUInteger, QWindowSystemInterface::TouchPoint> m_activeTouches;
72 UITouch *m_activePencilTouch;
73 NSMutableArray<UIAccessibilityElement *> *m_accessibleElements;
74 CGPoint m_lastScrollCursorPos;
75 CGPoint m_lastScrollDelta;
81 return [CAEAGLLayer
class];
83 return [super layerClass];
86- (instancetype)initWithQIOSWindow:(QT_PREPEND_NAMESPACE(QIOSWindow) *)window
88 if (self = [self initWithFrame:window->geometry().toCGRect()]) {
89 self.platformWindow = window;
91 if (isQtApplication())
94 m_accessibleElements = [[NSMutableArray<UIAccessibilityElement *> alloc] init];
97 self.multipleTouchEnabled = YES;
100 auto scrollGestureRecognizer = [[UIPanGestureRecognizer alloc]
101 initWithTarget:self action:@selector(handleScroll:)];
106 scrollGestureRecognizer.allowedTouchTypes = @[];
107 scrollGestureRecognizer.allowedScrollTypesMask = UIScrollTypeMaskAll;
108 scrollGestureRecognizer.maximumNumberOfTouches = 0;
109 m_lastScrollDelta = CGPointZero;
110 m_lastScrollCursorPos = CGPointZero;
111 [self addGestureRecognizer:[scrollGestureRecognizer autorelease]];
113 auto mouseHoverGestureRecognizer = [[UIHoverGestureRecognizer alloc]
114 initWithTarget:self action:@selector(handleMouseHover:)];
115 mouseHoverGestureRecognizer.allowedTouchTypes = @[ @(UITouchTypeIndirectPointer) ];
116 [self addGestureRecognizer:[mouseHoverGestureRecognizer autorelease]];
118#if QT_CONFIG(tabletevent)
119 auto pencilHoverGestureRecognizer = [[UIHoverGestureRecognizer alloc]
120 initWithTarget:self action:@selector(handlePencilHover:)];
121 pencilHoverGestureRecognizer.allowedTouchTypes = @[ @(UITouchTypePencil) ];
122 [self addGestureRecognizer:[pencilHoverGestureRecognizer autorelease]];
126 if ([self.layer isKindOfClass:CAMetalLayer.
class]) {
127 QWindow *window = self.platformWindow->window();
128 if (QColorSpace colorSpace = window->format().colorSpace(); colorSpace.isValid()) {
129 QCFType<CFDataRef> iccData = colorSpace.iccProfile().toCFData();
130 QCFType<CGColorSpaceRef> cgColorSpace = CGColorSpaceCreateWithICCData(iccData);
131 CAMetalLayer *metalLayer =
static_cast<CAMetalLayer *>(self.layer);
132 metalLayer.colorspace = cgColorSpace;
133 qCDebug(lcQpaWindow) <<
"Set" << self <<
"color space to" << metalLayer.colorspace;
137 else if ([self.layer isKindOfClass:[CAEAGLLayer
class]]) {
138 CAEAGLLayer *eaglLayer =
static_cast<CAEAGLLayer *>(self.layer);
139 eaglLayer.opaque = TRUE;
140 eaglLayer.drawableProperties = @{
141 kEAGLDrawablePropertyRetainedBacking: @(YES),
142 kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8
147#if defined(Q_OS_VISIONOS)
152 self.contentScaleFactor = self.platformWindow->screen()->devicePixelRatio();
161 [m_accessibleElements release];
168 NSMutableString *description = [NSMutableString stringWithString:[super description]];
170#ifndef QT_NO_DEBUG_STREAM
171 QString platformWindowDescription;
172 QDebug debug(&platformWindowDescription);
173 debug.nospace() <<
"; " << self.platformWindow <<
">";
174 NSRange lastCharacter = [description rangeOfComposedCharacterSequenceAtIndex:description.length - 1];
175 [description replaceCharactersInRange:lastCharacter withString:platformWindowDescription.toNSString()];
181#if !defined(Q_OS_VISIONOS)
182- (
void)willMoveToWindow:(UIWindow *)newWindow
186 self.contentScaleFactor = newWindow && newWindow.screen ?
187 newWindow.screen.scale : [[UIScreen mainScreen] scale];
193- (
void)didAddSubview:(UIView *)subview
195 if ([subview isKindOfClass:[QUIView
class]])
196 self.clipsToBounds = YES;
199- (
void)willRemoveSubview:(UIView *)subview
201 for (UIView *view in self.subviews) {
202 if (view != subview && [view isKindOfClass:[QUIView
class]])
206 self.clipsToBounds = NO;
209- (
void)setNeedsDisplay
211 [super setNeedsDisplay];
215 [self.layer setNeedsDisplay];
218- (
void)layoutSubviews
226 if (!CGAffineTransformIsIdentity(self.transform))
227 qWarning() << self <<
"has a transform set. This is not supported.";
229 QWindow *window = self.platformWindow->window();
230 QRect lastReportedGeometry = qt_window_private(window)->geometry;
231 QRect currentGeometry = QRectF::fromCGRect(self.frame).toRect();
232 qCDebug(lcQpaWindow) << self.platformWindow <<
"new geometry is" << currentGeometry;
233 QWindowSystemInterface::handleGeometryChange(window, currentGeometry);
235 if (currentGeometry.size() != lastReportedGeometry.size()) {
237 [self setNeedsDisplay];
244- (
void)displayLayer:(CALayer *)layer
247 Q_ASSERT(layer == self.layer);
249 if (!self.platformWindow)
252 [self sendUpdatedExposeEvent];
255- (
void)sendUpdatedExposeEvent
259 if (self.platformWindow->isExposed()) {
260 QSize bounds = QRectF::fromCGRect(self.layer.bounds).toRect().size();
262 Q_ASSERT(self.platformWindow->geometry().size() == bounds);
263 Q_ASSERT(self.hidden == !self.platformWindow->window()->isVisible());
265 region = QRect(QPoint(), bounds);
268 qCDebug(lcQpaWindow) << self.platformWindow << region <<
"isExposed" << self.platformWindow->isExposed();
269 QWindowSystemInterface::handleExposeEvent(self.platformWindow->window(), region);
272- (
void)safeAreaInsetsDidChange
274 QWindowSystemInterface::handleSafeAreaMarginsChanged(self.platformWindow->window());
279- (BOOL)canBecomeFirstResponder
281 return !(self.platformWindow->window()->flags() & (Qt::WindowDoesNotAcceptFocus
282 | Qt::WindowTransparentForInput));
285- (BOOL)becomeFirstResponder
291 FirstResponderCandidate firstResponderCandidate(self);
293 qImDebug() <<
"self:" << self <<
"first:" << [UIResponder qt_currentFirstResponder];
295 if (![super becomeFirstResponder]) {
296 qImDebug() << self <<
"was not allowed to become first responder";
300 qImDebug() << self <<
"became first responder";
303 if (qGuiApp->focusWindow() != self.platformWindow->window())
304 QWindowSystemInterface::handleFocusWindowChanged(self.platformWindow->window(), Qt::ActiveWindowFocusReason);
306 qImDebug() << self.platformWindow->window() <<
"already active, not sending window activation";
311- (BOOL)responderShouldTriggerWindowDeactivation:(UIResponder *)responder
315 if ([responder isKindOfClass:[QUIView
class]])
320 if ([responder isKindOfClass:[QIOSTextResponder
class]]) {
321 while ((responder = [responder nextResponder])) {
322 if ([responder isKindOfClass:[QUIView
class]])
330- (BOOL)resignFirstResponder
332 qImDebug() <<
"self:" << self <<
"first:" << [UIResponder qt_currentFirstResponder];
334 if (![super resignFirstResponder])
337 qImDebug() << self <<
"resigned first responder";
340 UIResponder *newResponder = FirstResponderCandidate::currentCandidate();
341 if ([self responderShouldTriggerWindowDeactivation:newResponder])
342 QWindowSystemInterface::handleFocusWindowChanged(
nullptr, Qt::ActiveWindowFocusReason);
348- (BOOL)isActiveWindow
354 if ([self isFirstResponder])
357 UIResponder *firstResponder = [UIResponder qt_currentFirstResponder];
358 if ([firstResponder isKindOfClass:[QIOSTextInputResponder
class]]
359 && [firstResponder nextResponder] == self)
367- (
void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
369 [super traitCollectionDidChange: previousTraitCollection];
371 QPointingDevice *touchDevice = QIOSIntegration::instance()->touchDevice();
372 auto *devicePriv = QPointingDevicePrivate::get(touchDevice);
374 auto capabilities = touchDevice->capabilities();
375 capabilities.setFlag(QPointingDevice::Capability::Pressure,
376 (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable));
377 devicePriv->setCapabilities(capabilities);
380-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
382 if (self.platformWindow->window()->flags() & Qt::WindowTransparentForInput)
384 return [super pointInside:point withEvent:event];
387- (
void)handleTouches:(NSSet *)touches withEvent:(UIEvent *)event withState:(QEventPoint::State)state
389 QIOSIntegration *iosIntegration = QIOSIntegration::instance();
390 const ulong timeStamp = getTimeStamp(event);
392#if QT_CONFIG(tabletevent)
393 if (m_activePencilTouch && [touches containsObject:m_activePencilTouch]) {
394 NSArray<UITouch *> *cTouches = [event coalescedTouchesForTouch:m_activePencilTouch];
395 for (UITouch *cTouch in cTouches) {
396 QEvent::Type eventType = [&]{
397 switch (cTouch.phase) {
398 case UITouchPhaseBegan:
399 return QEvent::TabletPress;
400 case UITouchPhaseEnded:
401 return QEvent::TabletRelease;
402 case UITouchPhaseMoved:
403 case UITouchPhaseStationary:
404 return QEvent::TabletMove;
409 [self handleTabletEvent:eventType withSender:cTouch andTimestamp:timeStamp];
414 if (m_activeTouches.isEmpty())
416 for (
auto it = m_activeTouches.begin(); it != m_activeTouches.end(); ++it) {
417 auto hash = it.key();
418 QWindowSystemInterface::TouchPoint &touchPoint = it.value();
419 UITouch *uiTouch = nil;
420 for (UITouch *touch in touches) {
421 if (touch.hash == hash) {
427 touchPoint.state = QEventPoint::State::Stationary;
429 touchPoint.state = state;
435 QPoint localViewPosition = QPointF::fromCGPoint([uiTouch locationInView:self]).toPoint();
436 QPoint globalScreenPosition = self.platformWindow->mapToGlobal(localViewPosition);
438 touchPoint.area = QRectF(globalScreenPosition, QSize(0, 0));
441 QSize screenSize = self.platformWindow->screen()->geometry().size();
442 touchPoint.normalPosition = QPointF(globalScreenPosition.x() / screenSize.width(),
443 globalScreenPosition.y() / screenSize.height());
445 touchPoint.pressure = [self pressureForTouch:uiTouch];
449 if ([self.window isKindOfClass:[QUIWindow
class]] &&
450 !
static_cast<QUIWindow *>(self.window).sendingEvent) {
459 QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::AsynchronousDelivery>(
460 self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values());
465 QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::AsynchronousDelivery>(
466 self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values());
470- (
void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
476 for (UITouch *touch in touches) {
477#if QT_CONFIG(tabletevent)
478 if (touch.type == UITouchTypePencil) {
479 if (Q_UNLIKELY(m_activePencilTouch)) {
480 qWarning(
"ignoring additional Pencil while first is still active");
483 m_activePencilTouch = touch;
486 Q_ASSERT(!m_activeTouches.contains(touch.hash));
490 static quint16 nextTouchId = 0;
491 m_activeTouches[touch.hash].id = nextTouchId++;
492#if QT_CONFIG(tabletevent)
497 if (self.platformWindow->shouldAutoActivateWindow() && m_activeTouches.size() == 1) {
498 QPlatformWindow *topLevel = self.platformWindow;
499 while (QPlatformWindow *p = topLevel->parent())
501 if (topLevel->window() != QGuiApplication::focusWindow())
502 topLevel->requestActivateWindow();
505 [self handleTouches:touches withEvent:event withState:QEventPoint::State::Pressed];
508- (
void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
510 [self handleTouches:touches withEvent:event withState:QEventPoint::State::Updated];
513- (
void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
515 [self handleTouches:touches withEvent:event withState:QEventPoint::State::Released];
519 for (UITouch *touch in touches) {
520#if QT_CONFIG(tabletevent)
521 if (touch.type == UITouchTypePencil) {
522 m_activePencilTouch = nil;
526 m_activeTouches.remove(touch.hash);
531 m_activeTouches.clear();
535- (
void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
537 if (m_activeTouches.isEmpty() && !m_activePencilTouch)
560 NSInteger count =
static_cast<NSInteger>([touches count]);
561 if (count != 0 && count != m_activeTouches.count() && !m_activePencilTouch)
562 qWarning(
"Subset of active touches cancelled by UIKit");
564 m_activeTouches.clear();
565 m_activePencilTouch = nil;
567 QIOSIntegration *iosIntegration =
static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration());
572 QWindowSystemInterface::handleTouchCancelEvent<QWindowSystemInterface::AsynchronousDelivery>(
573 self.platformWindow->window(), getTimeStamp(event), iosIntegration->touchDevice());
576- (
void)handleMouseHover:(UIHoverGestureRecognizer *)recognizer
578 if (!self.platformWindow)
581 auto *window = self.platformWindow->window();
582 auto localPosition = QPointF::fromCGPoint([recognizer locationInView:self]);
583 auto globalPosition = self.platformWindow->mapToGlobalF(localPosition);
585 switch (recognizer.state) {
586 case UIGestureRecognizerStateBegan:
587 qCDebug(lcQpaMouse) <<
"🖱️ enter" << window <<
"local =" << localPosition;
588 QWindowSystemInterface::handleEnterEvent(window, localPosition, globalPosition);
590 case UIGestureRecognizerStateEnded:
591 qCDebug(lcQpaMouse) <<
"🖱️ leave" << window;
592 QWindowSystemInterface::handleLeaveEvent(window);
594 case UIGestureRecognizerStateChanged:
595 qCDebug(lcQpaMouse) <<
"🖱️ move" <<
"local =" << localPosition <<
"global =" << globalPosition;
596 QWindowSystemInterface::handleMouseEvent(window, localPosition, globalPosition,
597 Qt::NoButton, Qt::NoButton, QEvent::MouseMove);
600 qCWarning(lcQpaMouse) <<
"Unknown hover state for" << recognizer;
605
606
607
608
609
610
611- (qreal)pressureForTouch:(UITouch*)touch
616 if (touch.maximumPossibleForce) {
621 return touch.force / touch.maximumPossibleForce;
626 return (touch.phase == UITouchPhaseMoved || touch.phase == UITouchPhaseStationary) ? 1.0 : 0.0;
632#if QT_CONFIG(tabletevent)
633- (
void)handlePencilHover:(UIHoverGestureRecognizer *)recognizer
635 if (!self.platformWindow)
642 if (m_activePencilTouch)
645 QEvent::Type eventType = [&]{
646 switch (recognizer.state) {
647 case UIGestureRecognizerStateBegan:
return QEvent::TabletEnterProximity;
648 case UIGestureRecognizerStateEnded:
return QEvent::TabletLeaveProximity;
649 case UIGestureRecognizerStateChanged:
return QEvent::TabletMove;
650 default:
return QEvent::None;
655 qCWarning(lcQpaTablet) <<
"Unknown hover state for" << recognizer;
659 [self handleTabletEvent:eventType withSender:recognizer andTimestamp:getTimeStamp(nil)];
662- (
void)handleTabletEvent:(QEvent::Type)eventType withSender:(id<TabletEventSender>)sender andTimestamp:(ulong)timeStamp
664 QWindow *window = self.platformWindow->window();
666 auto localViewPosition = QPointF::fromCGPoint([sender locationInView:self]);
667 auto globalScreenPosition = self.platformWindow->mapToGlobalF(localViewPosition);
670 CGVector azimuth = [sender azimuthUnitVectorInView:self];
673 CGFloat altitudeAngleRadians = sender.altitudeAngle;
675 qreal altitudeAngleDegrees = 90 - qRadiansToDegrees(altitudeAngleRadians);
676 qreal xTilt = qBound(-60.0, altitudeAngleDegrees * azimuth.dx, 60.0);
677 qreal yTilt = qBound(-60.0, altitudeAngleDegrees * azimuth.dy, 60.0);
679 auto zOffset = qt_objc_cast<UIHoverGestureRecognizer*>(sender).zOffset;
680 auto pressure = [self pressureForTouch:qt_objc_cast<UITouch*>(sender)];
683 auto rotation = -qRadiansToDegrees(sender.rollAngle);
687 static const int tangentialPressure = 0;
691 Qt::MouseButtons buttons = qt_objc_cast<UITouch*>(sender) &&
692 eventType != QEvent::TabletRelease ? Qt::LeftButton : Qt::NoButton;
693 static const auto modifiers = Qt::NoModifier;
695 QPointingDevice *pencilDevice = QIOSIntegration::instance()->pencilDevice();
697 qCDebug(lcQpaTablet) <<
"✏️" << eventType
698 <<
"local =" << localViewPosition <<
"global =" << globalScreenPosition
699 <<
"z =" << zOffset <<
"pressure =" << pressure
700 <<
"rotation =" << rotation <<
"tilt =" << QPointF(xTilt, yTilt);
703 case QEvent::TabletEnterProximity:
704 case QEvent::TabletLeaveProximity: {
705 const bool inProximity = eventType == QEvent::TabletEnterProximity;
706 QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, timeStamp,
707 pencilDevice, inProximity, localViewPosition, globalScreenPosition,
708 buttons, xTilt, yTilt, tangentialPressure, rotation, zOffset, modifiers);
711 case QEvent::TabletPress:
712 case QEvent::TabletMove:
713 case QEvent::TabletRelease: {
714 QWindowSystemInterface::handleTabletEvent(window, timeStamp,
715 pencilDevice, localViewPosition, globalScreenPosition,
716 buttons, pressure, xTilt, yTilt, tangentialPressure, rotation, zOffset, modifiers);
720 qCWarning(lcQpaTablet) <<
"Unknown tablet event type" << eventType <<
"for" << sender;
727- (
int)mapPressTypeToKey:(UIPress*)press withModifiers:(Qt::KeyboardModifiers)qtModifiers text:(QString &)text
729 switch (press.type) {
730 case UIPressTypeUpArrow:
return Qt::Key_Up;
731 case UIPressTypeDownArrow:
return Qt::Key_Down;
732 case UIPressTypeLeftArrow:
return Qt::Key_Left;
733 case UIPressTypeRightArrow:
return Qt::Key_Right;
734 case UIPressTypeSelect:
return Qt::Key_Select;
735 case UIPressTypeMenu:
return Qt::Key_Menu;
736 case UIPressTypePlayPause:
return Qt::Key_MediaTogglePlayPause;
738 Qt::Key key = QAppleKeyMapper::fromUIKitKey(press.key.keyCode);
739 if (key != Qt::Key_unknown)
741 NSString *charactersIgnoringModifiers = press.key.charactersIgnoringModifiers;
742 key = QAppleKeyMapper::fromUIKitKey(charactersIgnoringModifiers);
743 if (key != Qt::Key_unknown)
745 key = QAppleKeyMapper::fromNSString(qtModifiers, press.key.characters,
746 charactersIgnoringModifiers, text);
747 if (key != Qt::Key_unknown)
749 return Qt::Key_unknown;
752- (
bool)isControlKey:(Qt::Key)key
767- (
bool)handlePresses:(NSSet<UIPress *> *)presses eventType:(QEvent::Type)type
773 if (!qApp->focusWindow())
776 bool eventHandled =
false;
777 const bool imEnabled = QIOSInputContext::instance()->inputMethodAccepted();
779 for (UIPress* press in presses) {
780 Qt::KeyboardModifiers qtModifiers = QAppleKeyMapper::fromUIKitModifiers(press.key.modifierFlags);
782 int key = [self mapPressTypeToKey:press withModifiers:qtModifiers text:text];
783 if (key == Qt::Key_unknown)
785 if (imEnabled && ![self isControlKey:Qt::Key(key)])
788 bool keyHandled = QWindowSystemInterface::handleKeyEvent(
789 self.platformWindow->window(), type, key, qtModifiers, text);
790 eventHandled = eventHandled || keyHandled;
796- (
void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
798 if (![self handlePresses:presses eventType:QEvent::KeyPress])
799 [super pressesBegan:presses withEvent:event];
802- (
void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
804 if (![self handlePresses:presses eventType:QEvent::KeyPress])
805 [super pressesChanged:presses withEvent:event];
806 [super pressesChanged:presses withEvent:event];
809- (
void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(UIPressesEvent *)event
811 if (![self handlePresses:presses eventType:QEvent::KeyRelease])
812 [super pressesEnded:presses withEvent:event];
813 [super pressesEnded:presses withEvent:event];
816- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
818#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS)
820 return [QIOSMenu::menuActionTarget() targetForAction:action withSender:sender] != 0;
828- (
id)forwardingTargetForSelector:(SEL)selector
831#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS)
832 return QIOSMenu::menuActionTarget();
838- (
void)addInteraction:(id<UIInteraction>)interaction
840 if ([NSStringFromClass(interaction.
class) isEqualToString:@
"UITextInteraction"])
843 [super addInteraction:interaction];
846- (UIEditingInteractionConfiguration)editingInteractionConfiguration
854 return QIOSInputContext::instance()->inputMethodAccepted() ?
855 UIEditingInteractionConfigurationDefault : UIEditingInteractionConfigurationNone;
858#if QT_CONFIG(wheelevent)
859- (
void)handleScroll:(UIPanGestureRecognizer *)recognizer
861 if (!self.platformWindow->window())
864 if (!self.canBecomeFirstResponder)
867 CGPoint translation = [recognizer translationInView:self];
868 CGFloat deltaX = translation.x - m_lastScrollDelta.x;
869 CGFloat deltaY = translation.y - m_lastScrollDelta.y;
877 const int pixelsToDegrees = 2;
878 angleDelta.setX(deltaX * pixelsToDegrees);
879 angleDelta.setY(deltaY * pixelsToDegrees);
882 pixelDelta.setX(deltaX);
883 pixelDelta.setY(deltaY);
885 Qt::KeyboardModifiers qt_modifierFlags =
886 QAppleKeyMapper::fromUIKitModifiers(recognizer.modifierFlags);
888 if (recognizer.state == UIGestureRecognizerStateBegan)
892 m_lastScrollCursorPos = [recognizer locationInView:self];
894 if (recognizer.state != UIGestureRecognizerStateEnded) {
895 m_lastScrollDelta.x = translation.x;
896 m_lastScrollDelta.y = translation.y;
898 m_lastScrollDelta = CGPointZero;
901 QPoint qt_local = QPointF::fromCGPoint(m_lastScrollCursorPos).toPoint();
902 QPoint qt_global = self.platformWindow->mapToGlobal(qt_local);
904 qCInfo(lcQpaInputEvents).nospace() <<
"wheel event" <<
" at " << qt_local
905 <<
" pixelDelta=" << pixelDelta <<
" angleDelta=" << angleDelta;
907 QWindowSystemInterface::handleWheelEvent(self.platformWindow->window(),
908 getTimeStamp(nil), qt_local, qt_global, pixelDelta, angleDelta, qt_modifierFlags);
915@implementation UIView (QtHelpers)
919 if ([self isKindOfClass:[QUIView
class]]) {
920 if (QT_PREPEND_NAMESPACE(QIOSWindow) *w =
static_cast<QUIView *>(self).platformWindow)
929 while ((responder = [responder nextResponder])) {
930 if ([responder isKindOfClass:UIViewController.
class])
936- (QIOSViewController*)qtViewController
938 UIViewController *vc = self.viewController;
939 if ([vc isKindOfClass:QIOSViewController.
class])
940 return static_cast<QIOSViewController *>(vc);
948@implementation QUIMetalView
952 return [CAMetalLayer
class];
958#if QT_CONFIG(accessibility)
960#include "quiview_accessibility.mm"
Q_FORWARD_DECLARE_OBJC_CLASS(NSString)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
Q_FORWARD_DECLARE_OBJC_CLASS(UIViewController)