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 NSRect screenRect = [[theEvent window] convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)];
294 screenPoint = screenRect.origin;
297 screenPoint = [NSEvent mouseLocation];
302- (
bool)handleMouseDownEvent:(NSEvent *)theEvent
304 if ([self isTransparentForUserInput])
307 const auto button = cocoaButton2QtButton(theEvent);
309 QPointF qtWindowPoint;
310 QPointF qtScreenPoint;
311 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
312 Q_UNUSED(qtScreenPoint);
315 QRegion mask = QHighDpi::toNativeLocalPosition(m_platformWindow->window()->mask(), m_platformWindow->window());
316 const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint());
318 m_acceptedMouseDowns &= ~button;
320 m_acceptedMouseDowns |= button;
328 [self handleMouseEvent:theEvent];
332- (
bool)handleMouseDraggedEvent:(NSEvent *)theEvent
334 if ([self isTransparentForUserInput])
337 const auto button = cocoaButton2QtButton(theEvent);
341 if (!(m_acceptedMouseDowns & button) == button)
344 [self handleMouseEvent:theEvent];
348- (
bool)handleMouseUpEvent:(NSEvent *)theEvent
350 if ([self isTransparentForUserInput])
353 auto button = cocoaButton2QtButton(theEvent);
357 if (!(m_acceptedMouseDowns & button) == button)
360 if (m_sendUpAsRightButton && button == Qt::LeftButton)
361 button = Qt::RightButton;
363 m_buttons &= ~button;
365 [self handleMouseEvent:theEvent];
367 if (button == Qt::RightButton)
368 m_sendUpAsRightButton =
false;
373- (
void)mouseDown:(NSEvent *)theEvent
375 if ([self isTransparentForUserInput])
376 return [super mouseDown:theEvent];
377 m_sendUpAsRightButton =
false;
379 QPointF qtWindowPoint;
380 QPointF qtScreenPoint;
381 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];
382 Q_UNUSED(qtScreenPoint);
384 QRegion mask = QHighDpi::toNativeLocalPosition(m_platformWindow->window()->mask(), m_platformWindow->window());
385 const bool masked = !mask.isEmpty() && !mask.contains(qtWindowPoint.toPoint());
388 m_acceptedMouseDowns &= ~Qt::LeftButton;
390 m_acceptedMouseDowns |= Qt::LeftButton;
394 [super mouseDown:theEvent];
401 auto *focusObject = m_platformWindow->window()->focusObject();
402 if (queryInputMethod(focusObject)) {
405 if (QPlatformInputContext::inputItemClipRectangle().contains(qtWindowPoint)) {
406 qCDebug(lcQpaInputMethods) <<
"Asking input context to handle mouse press"
407 <<
"for focus object" << focusObject;
408 if ([NSTextInputContext.currentInputContext handleEvent:theEvent]) {
413 qCDebug(lcQpaInputMethods) <<
"Input context handled event; bailing out.";
419 if (!m_dontOverrideCtrlLMB && (theEvent.modifierFlags & NSEventModifierFlagControl)) {
420 m_buttons |= Qt::RightButton;
421 m_sendUpAsRightButton =
true;
423 m_buttons |= Qt::LeftButton;
426 [self handleMouseEvent:theEvent];
429- (
void)mouseDragged:(NSEvent *)theEvent
431 const bool accepted = [self handleMouseDraggedEvent:theEvent];
433 [super mouseDragged:theEvent];
436- (
void)mouseUp:(NSEvent *)theEvent
438 const bool accepted = [self handleMouseUpEvent:theEvent];
440 [super mouseUp:theEvent];
443- (
void)rightMouseDown:(NSEvent *)theEvent
445 const bool accepted = [self handleMouseDownEvent:theEvent];
447 [super rightMouseDown:theEvent];
450- (
void)rightMouseDragged:(NSEvent *)theEvent
452 const bool accepted = [self handleMouseDraggedEvent:theEvent];
454 [super rightMouseDragged:theEvent];
457- (
void)rightMouseUp:(NSEvent *)theEvent
459 const bool accepted = [self handleMouseUpEvent:theEvent];
461 [super rightMouseUp:theEvent];
464- (
void)otherMouseDown:(NSEvent *)theEvent
466 const bool accepted = [self handleMouseDownEvent:theEvent];
468 [super otherMouseDown:theEvent];
471- (
void)otherMouseDragged:(NSEvent *)theEvent
473 const bool accepted = [self handleMouseDraggedEvent:theEvent];
475 [super otherMouseDragged:theEvent];
478- (
void)otherMouseUp:(NSEvent *)theEvent
480 const bool accepted = [self handleMouseUpEvent:theEvent];
482 [super otherMouseUp:theEvent];
485- (
void)cursorUpdate:(NSEvent *)theEvent
490 auto previousCursor = NSCursor.currentCursor;
495 [super cursorUpdate:theEvent];
497 if (NSCursor.currentCursor != previousCursor)
498 qCInfo(lcQpaMouse) <<
"Cursor update for" << self <<
"resulted in new cursor" << NSCursor.currentCursor;
501- (
void)mouseMovedImpl:(NSEvent *)theEvent
503 if (!m_platformWindow)
513 QCocoaWindow *windowToLeave =
nullptr;
515 if (m_platformWindow->isContentView()) {
516 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
517 QWindow *childUnderMouse = m_platformWindow->childWindowAt(windowPoint.toPoint());
518 QCocoaWindow *childWindow =
static_cast<QCocoaWindow *>(childUnderMouse->handle());
519 if (childWindow != QCocoaWindow::s_windowUnderMouse) {
520 if (QCocoaWindow::s_windowUnderMouse)
521 windowToLeave = QCocoaWindow::s_windowUnderMouse;
522 QCocoaWindow::s_windowUnderMouse = childWindow;
529 if ([self isTransparentForUserInput])
533 qCInfo(lcQpaMouse) <<
"Detected new window under mouse at" << windowPoint <<
"; sending"
534 << QEvent::Enter << QCocoaWindow::s_windowUnderMouse->window()
535 << QEvent::Leave << windowToLeave->window();
536 QWindowSystemInterface::handleEnterLeaveEvent(QCocoaWindow::s_windowUnderMouse->window(), windowToLeave->window(), windowPoint, screenPoint);
541 if (m_platformWindow != QCocoaWindow::s_windowUnderMouse)
544 [self handleMouseEvent: theEvent];
547- (BOOL)shouldPropagateMouseEnterExit
549 Q_ASSERT(m_platformWindow);
556 if (m_platformWindow->isContentView())
560 if (m_platformWindow->isEmbedded())
564 QPlatformWindow *parentWindow = m_platformWindow->QPlatformWindow::parent();
565 if (parentWindow && parentWindow->isForeignWindow())
571- (
void)mouseEnteredImpl:(NSEvent *)theEvent
574 if (!m_platformWindow)
594 if (![self shouldPropagateMouseEnterExit])
599 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
600 QWindow *childUnderMouse = m_platformWindow->childWindowAt(windowPoint.toPoint());
601 QCocoaWindow::s_windowUnderMouse =
static_cast<QCocoaWindow *>(childUnderMouse->handle());
603 if ([self isTransparentForUserInput])
609 qCInfo(lcQpaMouse) <<
"Mouse entered" << self <<
"at" << windowPoint <<
"with" << currentlyPressedMouseButtons()
610 <<
"; sending" << QEvent::Enter <<
"to" << QCocoaWindow::s_windowUnderMouse->window();
611 QWindowSystemInterface::handleEnterEvent(QCocoaWindow::s_windowUnderMouse->window(), windowPoint, screenPoint);
614- (
void)mouseExitedImpl:(NSEvent *)theEvent
617 if (!m_platformWindow)
620 if (![self shouldPropagateMouseEnterExit])
623 QCocoaWindow *windowToLeave = QCocoaWindow::s_windowUnderMouse;
624 QCocoaWindow::s_windowUnderMouse =
nullptr;
626 if ([self isTransparentForUserInput])
635 qCInfo(lcQpaMouse) <<
"Mouse left" << self <<
"; sending" << QEvent::Leave <<
"to" << windowToLeave->window();
636 QWindowSystemInterface::handleLeaveEvent(windowToLeave->window());
639#if QT_CONFIG(wheelevent)
640- (
void)scrollWheel:(NSEvent *)theEvent
642 if (!m_platformWindow)
645 if ([self isTransparentForUserInput])
646 return [super scrollWheel:theEvent];
649 Qt::MouseEventSource source = Qt::MouseEventNotSynthesized;
650 if ([theEvent hasPreciseScrollingDeltas]) {
656 const int pixelsToDegrees = 2;
657 angleDelta.setX([theEvent scrollingDeltaX] * pixelsToDegrees);
658 angleDelta.setY([theEvent scrollingDeltaY] * pixelsToDegrees);
659 source = Qt::MouseEventSynthesizedBySystem;
662 angleDelta.setX(qBound(-120,
int([theEvent deltaX] * 10000), 120));
663 angleDelta.setY(qBound(-120,
int([theEvent deltaY] * 10000), 120));
667 if ([theEvent hasPreciseScrollingDeltas]) {
668 pixelDelta.setX([theEvent scrollingDeltaX]);
669 pixelDelta.setY([theEvent scrollingDeltaY]);
673 const CGFloat lineWithEstimate = 20.0;
674 pixelDelta.setX([theEvent scrollingDeltaX] * lineWithEstimate);
675 pixelDelta.setY([theEvent scrollingDeltaY] * lineWithEstimate);
678 QPointF qt_windowPoint;
679 QPointF qt_screenPoint;
680 [self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qt_windowPoint andScreenPoint:&qt_screenPoint];
681 NSTimeInterval timestamp = [theEvent timestamp];
682 ulong qt_timestamp = timestamp * 1000;
684 Qt::ScrollPhase phase = Qt::NoScrollPhase;
685 if (theEvent.phase == NSEventPhaseMayBegin || theEvent.phase == NSEventPhaseBegan) {
688 phase = m_scrolling ? Qt::ScrollUpdate : Qt::ScrollBegin;
690 }
else if (theEvent.phase == NSEventPhaseStationary || theEvent.phase == NSEventPhaseChanged) {
691 phase = Qt::ScrollUpdate;
692 }
else if (theEvent.phase == NSEventPhaseEnded) {
699 if ([NSApp nextEventMatchingMask:NSEventMaskScrollWheel untilDate:[NSDate distantPast]
700 inMode:@
"QtMomementumEventSearchMode" dequeue:NO].momentumPhase == NSEventPhaseBegan) {
703 phase = Qt::ScrollEnd;
706 }
else if (theEvent.momentumPhase == NSEventPhaseBegan) {
710 phase = m_scrolling ? Qt::ScrollUpdate : Qt::ScrollBegin;
712 }
else if (theEvent.momentumPhase == NSEventPhaseChanged) {
713 phase = Qt::ScrollMomentum;
714 }
else if (theEvent.phase == NSEventPhaseCancelled
715 || theEvent.momentumPhase == NSEventPhaseEnded
716 || theEvent.momentumPhase == NSEventPhaseCancelled) {
717 phase = Qt::ScrollEnd;
720 Q_ASSERT(theEvent.momentumPhase != NSEventPhaseStationary);
725 if (theEvent.phase == NSEventPhaseCancelled) {
726 if (!pixelDelta.isNull() || !angleDelta.isNull()) {
727 qCInfo(lcQpaMouse) <<
"Ignoring unexpected delta for" << theEvent;
728 pixelDelta = QPoint();
729 angleDelta = QPoint();
740 if (theEvent.momentumPhase == NSEventPhaseNone)
741 m_currentWheelModifiers = QAppleKeyMapper::fromCocoaModifiers(theEvent.modifierFlags);
744 bool isInverted = [theEvent isDirectionInvertedFromDevice];
746 qCInfo(lcQpaMouse).nospace() << phase <<
" at " << qt_windowPoint
747 <<
" pixelDelta=" << pixelDelta <<
" angleDelta=" << angleDelta
748 << (isInverted ?
" inverted=true" :
"");
750 const QPointingDevice *device = pointingDeviceFor(theEvent.deviceID);
753 if (theEvent.hasPreciseScrollingDeltas) {
754 auto *devicePriv = QPointingDevicePrivate::get(
const_cast<QPointingDevice *>(device));
755 if (!devicePriv->capabilities.testFlag(QInputDevice::Capability::PixelScroll)) {
756 devicePriv->name =
"trackpad or magic mouse"_L1;
757 devicePriv->deviceType = QInputDevice::DeviceType::TouchPad;
758 devicePriv->setCapabilities(devicePriv->capabilities | QInputDevice::Capability::PixelScroll);
759 qCDebug(lcQpaInputDevices) <<
"mouse scrolling: updated capabilities" << device;
763 QWindowSystemInterface::handleWheelEvent(m_platformWindow->window(), qt_timestamp,
764 device, qt_windowPoint, qt_screenPoint, pixelDelta, angleDelta,
765 m_currentWheelModifiers, phase, source, isInverted);
static const QPointingDevice * pointingDeviceFor(qint64 deviceID)