7using namespace Qt::StringLiterals;
15 static const int kDefaultPrimaryPointingDeviceId = 1;
17 deviceID = kDefaultPrimaryPointingDeviceId;
19 if (
const auto *device = QPointingDevicePrivate::pointingDeviceById(deviceID))
22 const auto *primaryDevice = QPointingDevice::primaryPointingDevice();
23 if (primaryDevice->systemId() == kDefaultPrimaryPointingDeviceId) {
25 QPointingDevicePrivate::get(
const_cast<QPointingDevice *>(primaryDevice))->systemId = deviceID;
26 qCDebug(lcQpaInputDevices) <<
"primaryPointingDevice is now" << primaryDevice;
30 const auto *device =
new QPointingDevice(
"mouse"_L1, deviceID,
31 QInputDevice::DeviceType::Mouse, QPointingDevice::PointerType::Generic,
32 QInputDevice::Capability::Scroll | QInputDevice::Capability::Position,
33 1, 3, QString(), QPointingDeviceUniqueId(), QCocoaIntegration::instance());
34 QWindowSystemInterface::registerInputDevice(device);
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55@implementation QNSViewMouseMoveHelper {
59- (instancetype)initWithView:(QNSView *)theView
61 if ((self = [super init]))
67- (
void)mouseMoved:(NSEvent *)theEvent
69 [view mouseMovedImpl:theEvent];
72- (
void)mouseEntered:(NSEvent *)theEvent
74 [view mouseEnteredImpl:theEvent];
77- (
void)mouseExited:(NSEvent *)theEvent
79 [view mouseExitedImpl:theEvent];
82- (
void)cursorUpdate:(NSEvent *)theEvent
84 [view cursorUpdate:theEvent];
89@implementation QNSView (MouseAPI)
91- (
void)resetMouseButtons
93 qCDebug(lcQpaMouse) <<
"Resetting mouse buttons";
94 m_buttons = Qt::NoButton;
95 m_frameStrutButtons = Qt::NoButton;
98- (
void)handleMouseEvent:(NSEvent *)theEvent
100 if (!m_platformWindow)
103#ifndef QT_NO_TABLETEVENT
106 if ([self handleTabletEvent: theEvent])
110 QPointF qtWindowPoint;
111 QPointF qtScreenPoint;
112 QNSView *targetView = self;
113 if (!targetView.platformWindow)
117 [targetView convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
118 ulong timestamp = [theEvent timestamp] * 1000;
120 QCocoaDrag* nativeDrag = QCocoaIntegration::instance()->drag();
121 nativeDrag->setLastInputEvent(theEvent, self);
123 const auto modifiers = QAppleKeyMapper::fromCocoaModifiers(theEvent.modifierFlags);
124 auto button = cocoaButton2QtButton(theEvent);
125 if (button == Qt::LeftButton && m_sendUpAsRightButton)
126 button = Qt::RightButton;
127 const auto eventType = cocoaEvent2QtMouseEvent(theEvent);
129 const QPointingDevice *device = pointingDeviceFor(theEvent.deviceID);
132 if (eventType == QEvent::MouseMove)
133 qCDebug(lcQpaMouse) << eventType <<
"at" << qtWindowPoint <<
"with" << m_buttons;
135 qCInfo(lcQpaMouse) << eventType <<
"of" << button <<
"at" << qtWindowPoint <<
"with" << m_buttons;
137 QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(),
138 timestamp, qtWindowPoint, qtScreenPoint,
139 m_buttons, button, eventType, modifiers);
142- (
void)handleFrameStrutMouseEvent:(NSEvent *)theEvent
144 if (!m_platformWindow)
147 switch (theEvent.type) {
148 case NSEventTypeLeftMouseDown:
149 m_frameStrutButtons |= Qt::LeftButton;
151 case NSEventTypeLeftMouseUp:
152 m_frameStrutButtons &= ~Qt::LeftButton;
154 case NSEventTypeRightMouseDown:
155 m_frameStrutButtons |= Qt::RightButton;
157 case NSEventTypeRightMouseUp:
158 m_frameStrutButtons &= ~Qt::RightButton;
160 case NSEventTypeOtherMouseDown:
161 m_frameStrutButtons |= cocoaButton2QtButton(theEvent.buttonNumber);
163 case NSEventTypeOtherMouseUp:
164 m_frameStrutButtons &= ~cocoaButton2QtButton(theEvent.buttonNumber);
177 m_buttons &= ~m_frameStrutButtons;
179 if (m_buttons != Qt::NoButton) {
185 NSWindow *window = [self window];
186 NSPoint windowPoint = [theEvent locationInWindow];
188 int windowScreenY = [window frame].origin.y + [window frame].size.height;
189 NSPoint windowCoord = [self convertPoint:[self frame].origin toView:nil];
190 int viewScreenY = [window convertRectToScreen:NSMakeRect(windowCoord.x, windowCoord.y, 0, 0)].origin.y;
191 int titleBarHeight = windowScreenY - viewScreenY;
193 NSPoint nsViewPoint = [self convertPoint: windowPoint fromView: nil];
194 QPoint qtWindowPoint = QPoint(nsViewPoint.x, titleBarHeight + nsViewPoint.y);
195 NSPoint screenPoint = [window convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 0, 0)].origin;
196 QPoint qtScreenPoint = QCocoaScreen::mapFromNative(screenPoint).toPoint();
198 ulong timestamp = [theEvent timestamp] * 1000;
200 const auto button = cocoaButton2QtButton(theEvent);
201 auto eventType = [&]() {
202 switch (theEvent.type) {
203 case NSEventTypeLeftMouseDown:
204 case NSEventTypeRightMouseDown:
205 case NSEventTypeOtherMouseDown:
206 return QEvent::NonClientAreaMouseButtonPress;
208 case NSEventTypeLeftMouseUp:
209 case NSEventTypeRightMouseUp:
210 case NSEventTypeOtherMouseUp:
211 return QEvent::NonClientAreaMouseButtonRelease;
213 case NSEventTypeMouseMoved:
214 case NSEventTypeLeftMouseDragged:
215 case NSEventTypeRightMouseDragged:
216 case NSEventTypeOtherMouseDragged:
217 return QEvent::NonClientAreaMouseMove;
224 qCInfo(lcQpaMouse) << eventType <<
"at" << qtWindowPoint <<
"with" << m_frameStrutButtons <<
"in" << self.window;
225 QWindowSystemInterface::handleMouseEvent(m_platformWindow->window(),
226 timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons, button, eventType);
230@implementation QNSView (Mouse)
234 m_buttons = Qt::NoButton;
235 m_acceptedMouseDowns = Qt::NoButton;
236 m_frameStrutButtons = Qt::NoButton;
241 m_sendUpAsRightButton =
false;
242 m_dontOverrideCtrlLMB = qt_mac_resolveOption(
false, m_platformWindow->window(),
243 "_q_platform_MacDontOverrideCtrlLMB",
"QT_MAC_DONT_OVERRIDE_CTRL_LMB");
245 m_mouseMoveHelper = [[QNSViewMouseMoveHelper alloc] initWithView:self];
247 NSUInteger trackingOptions = NSTrackingCursorUpdate | NSTrackingMouseEnteredAndExited;
253 trackingOptions |= NSTrackingActiveAlways;
260 trackingOptions |= NSTrackingMouseMoved;
264 trackingOptions |= NSTrackingInVisibleRect;
265 static const NSRect trackingRect = NSZeroRect;
267 QMacAutoReleasePool pool;
268 [self addTrackingArea:[[[NSTrackingArea alloc] initWithRect:trackingRect
269 options:trackingOptions owner:m_mouseMoveHelper userInfo:nil] autorelease]];
272- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
275 if (!m_platformWindow)
277 if ([self isTransparentForUserInput])
281 [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint: &windowPoint andScreenPoint: &screenPoint];
285- (NSPoint)screenMousePoint:(NSEvent *)theEvent
289 NSPoint windowPoint = [theEvent locationInWindow];
290 if (qIsNaN(windowPoint.x) || qIsNaN(windowPoint.y)) {
291 screenPoint = [NSEvent mouseLocation];
293 screenPoint = [theEvent.window convertPointToScreen:windowPoint];
296 screenPoint = [NSEvent mouseLocation];
301- (
bool)handleMouseDownEvent:(NSEvent *)theEvent
303 if ([self isTransparentForUserInput])
306 const auto button = cocoaButton2QtButton(theEvent);
308 QPointF qtWindowPoint;
309 QPointF qtScreenPoint;
310 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
311 Q_UNUSED(qtScreenPoint);
314 QRegion mask = QHighDpi::toNativeLocalPosition(m_platformWindow->window()->mask(), m_platformWindow->window());
315 const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint());
317 m_acceptedMouseDowns &= ~button;
319 m_acceptedMouseDowns |= button;
327 [self handleMouseEvent:theEvent];
331- (
bool)handleMouseDraggedEvent:(NSEvent *)theEvent
333 if ([self isTransparentForUserInput])
336 const auto button = cocoaButton2QtButton(theEvent);
340 if (!(m_acceptedMouseDowns & button) == button)
343 [self handleMouseEvent:theEvent];
347- (
bool)handleMouseUpEvent:(NSEvent *)theEvent
349 if ([self isTransparentForUserInput])
352 auto button = cocoaButton2QtButton(theEvent);
356 if (!(m_acceptedMouseDowns & button) == button)
359 if (m_sendUpAsRightButton && button == Qt::LeftButton)
360 button = Qt::RightButton;
362 m_buttons &= ~button;
364 [self handleMouseEvent:theEvent];
366 if (button == Qt::RightButton)
367 m_sendUpAsRightButton =
false;
372- (
void)mouseDown:(NSEvent *)theEvent
374 if ([self isTransparentForUserInput])
375 return [super mouseDown:theEvent];
376 m_sendUpAsRightButton =
false;
378 QPointF qtWindowPoint;
379 QPointF qtScreenPoint;
380 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
381 Q_UNUSED(qtScreenPoint);
383 QRegion mask = QHighDpi::toNativeLocalPosition(m_platformWindow->window()->mask(), m_platformWindow->window());
384 const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint());
387 m_acceptedMouseDowns &= ~Qt::LeftButton;
389 m_acceptedMouseDowns |= Qt::LeftButton;
393 [super mouseDown:theEvent];
400 auto *focusObject = m_platformWindow->window()->focusObject();
401 if (queryInputMethod(focusObject)) {
404 if (QPlatformInputContext::inputItemClipRectangle().contains(qtWindowPoint)) {
405 qCDebug(lcQpaInputMethods) <<
"Asking input context to handle mouse press"
406 <<
"for focus object" << focusObject;
407 if ([NSTextInputContext.currentInputContext handleEvent:theEvent]) {
412 qCDebug(lcQpaInputMethods) <<
"Input context handled event; bailing out.";
418 if (!m_dontOverrideCtrlLMB && (theEvent.modifierFlags & NSEventModifierFlagControl)) {
419 m_buttons |= Qt::RightButton;
420 m_sendUpAsRightButton =
true;
422 m_buttons |= Qt::LeftButton;
425 [self handleMouseEvent:theEvent];
428- (
void)mouseDragged:(NSEvent *)theEvent
430 const bool accepted = [self handleMouseDraggedEvent:theEvent];
432 [super mouseDragged:theEvent];
435- (
void)mouseUp:(NSEvent *)theEvent
437 const bool accepted = [self handleMouseUpEvent:theEvent];
439 [super mouseUp:theEvent];
442- (
void)rightMouseDown:(NSEvent *)theEvent
444 const bool accepted = [self handleMouseDownEvent:theEvent];
446 [super rightMouseDown:theEvent];
449- (
void)rightMouseDragged:(NSEvent *)theEvent
451 const bool accepted = [self handleMouseDraggedEvent:theEvent];
453 [super rightMouseDragged:theEvent];
456- (
void)rightMouseUp:(NSEvent *)theEvent
458 const bool accepted = [self handleMouseUpEvent:theEvent];
460 [super rightMouseUp:theEvent];
463- (
void)otherMouseDown:(NSEvent *)theEvent
465 const bool accepted = [self handleMouseDownEvent:theEvent];
467 [super otherMouseDown:theEvent];
470- (
void)otherMouseDragged:(NSEvent *)theEvent
472 const bool accepted = [self handleMouseDraggedEvent:theEvent];
474 [super otherMouseDragged:theEvent];
477- (
void)otherMouseUp:(NSEvent *)theEvent
479 const bool accepted = [self handleMouseUpEvent:theEvent];
481 [super otherMouseUp:theEvent];
484- (
void)cursorUpdate:(NSEvent *)theEvent
489 auto previousCursor = NSCursor.currentCursor;
494 [super cursorUpdate:theEvent];
496 if (NSCursor.currentCursor != previousCursor)
497 qCInfo(lcQpaMouse) <<
"Cursor update for" << self <<
"resulted in new cursor" << NSCursor.currentCursor;
500- (
void)mouseMovedImpl:(NSEvent *)theEvent
502 if (!m_platformWindow)
512 QCocoaWindow *windowToLeave =
nullptr;
514 if (m_platformWindow->isContentView()) {
515 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
516 QWindow *childUnderMouse = m_platformWindow->childWindowAt(windowPoint.toPoint());
517 QCocoaWindow *childWindow =
static_cast<QCocoaWindow *>(childUnderMouse->handle());
518 if (childWindow != QCocoaWindow::s_windowUnderMouse) {
519 if (QCocoaWindow::s_windowUnderMouse)
520 windowToLeave = QCocoaWindow::s_windowUnderMouse;
521 QCocoaWindow::s_windowUnderMouse = childWindow;
528 if ([self isTransparentForUserInput])
532 qCInfo(lcQpaMouse) <<
"Detected new window under mouse at" << windowPoint <<
"; sending"
533 << QEvent::Enter << QCocoaWindow::s_windowUnderMouse->window()
534 << QEvent::Leave << windowToLeave->window();
535 QWindowSystemInterface::handleEnterLeaveEvent(QCocoaWindow::s_windowUnderMouse->window(), windowToLeave->window(), windowPoint, screenPoint);
540 if (m_platformWindow != QCocoaWindow::s_windowUnderMouse)
543 [self handleMouseEvent: theEvent];
546- (BOOL)shouldPropagateMouseEnterExit
548 Q_ASSERT(m_platformWindow);
555 if (m_platformWindow->isContentView())
559 if (m_platformWindow->isEmbedded())
563 QPlatformWindow *parentWindow = m_platformWindow->QPlatformWindow::parent();
564 if (parentWindow && parentWindow->isForeignWindow())
570- (
void)mouseEnteredImpl:(NSEvent *)theEvent
573 if (!m_platformWindow)
593 if (![self shouldPropagateMouseEnterExit])
598 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
599 QWindow *childUnderMouse = m_platformWindow->childWindowAt(windowPoint.toPoint());
600 QCocoaWindow::s_windowUnderMouse =
static_cast<QCocoaWindow *>(childUnderMouse->handle());
602 if ([self isTransparentForUserInput])
608 qCInfo(lcQpaMouse) <<
"Mouse entered" << self <<
"at" << windowPoint <<
"with" << currentlyPressedMouseButtons()
609 <<
"; sending" << QEvent::Enter <<
"to" << QCocoaWindow::s_windowUnderMouse->window();
610 QWindowSystemInterface::handleEnterEvent(QCocoaWindow::s_windowUnderMouse->window(), windowPoint, screenPoint);
613- (
void)mouseExitedImpl:(NSEvent *)theEvent
616 if (!m_platformWindow)
619 if (![self shouldPropagateMouseEnterExit])
622 QCocoaWindow *windowToLeave = QCocoaWindow::s_windowUnderMouse;
623 QCocoaWindow::s_windowUnderMouse =
nullptr;
625 if ([self isTransparentForUserInput])
634 qCInfo(lcQpaMouse) <<
"Mouse left" << self <<
"; sending" << QEvent::Leave <<
"to" << windowToLeave->window();
635 QWindowSystemInterface::handleLeaveEvent(windowToLeave->window());
638#if QT_CONFIG(wheelevent)
639- (
void)scrollWheel:(NSEvent *)theEvent
641 if (!m_platformWindow)
644 if ([self isTransparentForUserInput])
645 return [super scrollWheel:theEvent];
648 Qt::MouseEventSource source = Qt::MouseEventNotSynthesized;
649 if ([theEvent hasPreciseScrollingDeltas]) {
655 const int pixelsToDegrees = 2;
656 angleDelta.setX([theEvent scrollingDeltaX] * pixelsToDegrees);
657 angleDelta.setY([theEvent scrollingDeltaY] * pixelsToDegrees);
658 source = Qt::MouseEventSynthesizedBySystem;
661 angleDelta.setX(qBound(-120,
int([theEvent deltaX] * 10000), 120));
662 angleDelta.setY(qBound(-120,
int([theEvent deltaY] * 10000), 120));
666 if ([theEvent hasPreciseScrollingDeltas]) {
667 pixelDelta.setX([theEvent scrollingDeltaX]);
668 pixelDelta.setY([theEvent scrollingDeltaY]);
672 const CGFloat lineWithEstimate = 20.0;
673 pixelDelta.setX([theEvent scrollingDeltaX] * lineWithEstimate);
674 pixelDelta.setY([theEvent scrollingDeltaY] * lineWithEstimate);
677 QPointF qt_windowPoint;
678 QPointF qt_screenPoint;
679 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qt_windowPoint andScreenPoint:&qt_screenPoint];
680 NSTimeInterval timestamp = [theEvent timestamp];
681 ulong qt_timestamp = timestamp * 1000;
683 Qt::ScrollPhase phase = Qt::NoScrollPhase;
684 if (theEvent.phase == NSEventPhaseMayBegin || theEvent.phase == NSEventPhaseBegan) {
687 phase = m_scrolling ? Qt::ScrollUpdate : Qt::ScrollBegin;
689 }
else if (theEvent.phase == NSEventPhaseStationary || theEvent.phase == NSEventPhaseChanged) {
690 phase = Qt::ScrollUpdate;
691 }
else if (theEvent.phase == NSEventPhaseEnded) {
698 if ([NSApp nextEventMatchingMask:NSEventMaskScrollWheel untilDate:[NSDate distantPast]
699 inMode:@
"QtMomementumEventSearchMode" dequeue:NO].momentumPhase == NSEventPhaseBegan) {
702 phase = Qt::ScrollEnd;
705 }
else if (theEvent.momentumPhase == NSEventPhaseBegan) {
709 phase = m_scrolling ? Qt::ScrollUpdate : Qt::ScrollBegin;
711 }
else if (theEvent.momentumPhase == NSEventPhaseChanged) {
712 phase = Qt::ScrollMomentum;
713 }
else if (theEvent.phase == NSEventPhaseCancelled
714 || theEvent.momentumPhase == NSEventPhaseEnded
715 || theEvent.momentumPhase == NSEventPhaseCancelled) {
716 phase = Qt::ScrollEnd;
719 Q_ASSERT(theEvent.momentumPhase != NSEventPhaseStationary);
724 if (theEvent.phase == NSEventPhaseCancelled) {
725 if (!pixelDelta.isNull() || !angleDelta.isNull()) {
726 qCInfo(lcQpaMouse) <<
"Ignoring unexpected delta for" << theEvent;
727 pixelDelta = QPoint();
728 angleDelta = QPoint();
739 if (theEvent.momentumPhase == NSEventPhaseNone)
740 m_currentWheelModifiers = QAppleKeyMapper::fromCocoaModifiers(theEvent.modifierFlags);
743 bool isInverted = [theEvent isDirectionInvertedFromDevice];
745 qCInfo(lcQpaMouse).nospace() << phase <<
" at " << qt_windowPoint
746 <<
" pixelDelta=" << pixelDelta <<
" angleDelta=" << angleDelta
747 << (isInverted ?
" inverted=true" :
"");
749 const QPointingDevice *device = pointingDeviceFor(theEvent.deviceID);
752 if (theEvent.hasPreciseScrollingDeltas) {
753 auto *devicePriv = QPointingDevicePrivate::get(
const_cast<QPointingDevice *>(device));
754 if (!devicePriv->capabilities.testFlag(QInputDevice::Capability::PixelScroll)) {
755 devicePriv->name =
"trackpad or magic mouse"_L1;
756 devicePriv->deviceType = QInputDevice::DeviceType::TouchPad;
757 devicePriv->setCapabilities(devicePriv->capabilities | QInputDevice::Capability::PixelScroll);
758 qCDebug(lcQpaInputDevices) <<
"mouse scrolling: updated capabilities" << device;
762 QWindowSystemInterface::handleWheelEvent(m_platformWindow->window(), qt_timestamp,
763 device, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta,
764 m_currentWheelModifiers, phase, source, isInverted);
static const QPointingDevice * pointingDeviceFor(qint64 deviceID)