summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa/qnsview_mouse.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/cocoa/qnsview_mouse.mm')
-rw-r--r--src/plugins/platforms/cocoa/qnsview_mouse.mm112
1 files changed, 71 insertions, 41 deletions
diff --git a/src/plugins/platforms/cocoa/qnsview_mouse.mm b/src/plugins/platforms/cocoa/qnsview_mouse.mm
index b42f2d0e7e..81f2e4fd58 100644
--- a/src/plugins/platforms/cocoa/qnsview_mouse.mm
+++ b/src/plugins/platforms/cocoa/qnsview_mouse.mm
@@ -93,7 +93,7 @@
- (void)resetMouseButtons
{
- qCDebug(lcQpaMouse) << "Reseting mouse buttons";
+ qCDebug(lcQpaMouse) << "Resetting mouse buttons";
m_buttons = Qt::NoButton;
m_frameStrutButtons = Qt::NoButton;
}
@@ -103,21 +103,14 @@
if (!m_platformWindow)
return;
- // get m_buttons in sync
- // Don't send frme strut events if we are in the middle of a mouse drag.
- if (m_buttons != Qt::NoButton)
- return;
-
switch (theEvent.type) {
case NSEventTypeLeftMouseDown:
- case NSEventTypeLeftMouseDragged:
m_frameStrutButtons |= Qt::LeftButton;
break;
case NSEventTypeLeftMouseUp:
m_frameStrutButtons &= ~Qt::LeftButton;
break;
case NSEventTypeRightMouseDown:
- case NSEventTypeRightMouseDragged:
m_frameStrutButtons |= Qt::RightButton;
break;
case NSEventTypeRightMouseUp:
@@ -132,6 +125,22 @@
break;
}
+ // m_buttons can sometimes get out of sync with the button state in AppKit
+ // E.g if the QNSView where a drag starts is reparented to another window
+ // while the drag is ongoing, it will not get the corresponding mouseUp
+ // call. This will result in m_buttons to be stuck on Qt::LeftButton.
+ // Since we know which buttons was pressed/released directly on the frame
+ // strut, we can rectify m_buttons here so that we at least don't return early
+ // from the drag test underneath because of the faulty m_buttons state.
+ // FIXME: get m_buttons in sync with AppKit/NSEvent all over in QNSView.
+ m_buttons &= ~m_frameStrutButtons;
+
+ if (m_buttons != Qt::NoButton) {
+ // Don't send frame strut events if we are in the middle of
+ // a mouse drag that didn't start on the frame strut.
+ return;
+ }
+
NSWindow *window = [self window];
NSPoint windowPoint = [theEvent locationInWindow];
@@ -176,6 +185,39 @@
QWindowSystemInterface::handleFrameStrutMouseEvent(m_platformWindow->window(),
timestamp, qtWindowPoint, qtScreenPoint, m_frameStrutButtons, button, eventType);
}
+
+- (bool)closePopups:(NSEvent *)theEvent
+{
+ QList<QCocoaWindow *> *popups = QCocoaIntegration::instance()->popupWindowStack();
+ if (!popups->isEmpty()) {
+ // Check if the click is outside all popups.
+ bool inside = false;
+ QPointF qtScreenPoint = QCocoaScreen::mapFromNative([self screenMousePoint:theEvent]);
+ for (QList<QCocoaWindow *>::const_iterator it = popups->begin(); it != popups->end(); ++it) {
+ if ((*it)->geometry().contains(qtScreenPoint.toPoint())) {
+ inside = true;
+ break;
+ }
+ }
+ // Close the popups if the click was outside.
+ if (!inside) {
+ bool selfClosed = false;
+ Qt::WindowType type = QCocoaIntegration::instance()->activePopupWindow()->window()->type();
+ while (QCocoaWindow *popup = QCocoaIntegration::instance()->popPopupWindow()) {
+ selfClosed = self == popup->view();
+ QWindowSystemInterface::handleCloseEvent(popup->window());
+ QWindowSystemInterface::flushWindowSystemEvents();
+ if (!m_platformWindow)
+ return true; // Bail out if window was destroyed
+ }
+ // Consume the mouse event when closing the popup, except for tool tips
+ // were it's expected that the event is processed normally.
+ if (type != Qt::ToolTip || selfClosed)
+ return true;
+ }
+ }
+ return false;
+}
@end
@implementation QNSView (Mouse)
@@ -381,34 +423,8 @@
// that particular poup type (for example context menus). However, Qt expects
// that plain popup QWindows will also be closed, so we implement the logic
// here as well.
- QList<QCocoaWindow *> *popups = QCocoaIntegration::instance()->popupWindowStack();
- if (!popups->isEmpty()) {
- // Check if the click is outside all popups.
- bool inside = false;
- QPointF qtScreenPoint = QCocoaScreen::mapFromNative([self screenMousePoint:theEvent]);
- for (QList<QCocoaWindow *>::const_iterator it = popups->begin(); it != popups->end(); ++it) {
- if ((*it)->geometry().contains(qtScreenPoint.toPoint())) {
- inside = true;
- break;
- }
- }
- // Close the popups if the click was outside.
- if (!inside) {
- bool selfClosed = false;
- Qt::WindowType type = QCocoaIntegration::instance()->activePopupWindow()->window()->type();
- while (QCocoaWindow *popup = QCocoaIntegration::instance()->popPopupWindow()) {
- selfClosed = self == popup->view();
- QWindowSystemInterface::handleCloseEvent(popup->window());
- QWindowSystemInterface::flushWindowSystemEvents();
- if (!m_platformWindow)
- return; // Bail out if window was destroyed
- }
- // Consume the mouse event when closing the popup, except for tool tips
- // were it's expected that the event is processed normally.
- if (type != Qt::ToolTip || selfClosed)
- return;
- }
- }
+ if ([self closePopups:theEvent])
+ return;
QPointF qtWindowPoint;
QPointF qtScreenPoint;
@@ -655,14 +671,18 @@
// had time to emit a momentum phase event.
if ([NSApp nextEventMatchingMask:NSEventMaskScrollWheel untilDate:[NSDate distantPast]
inMode:@"QtMomementumEventSearchMode" dequeue:NO].momentumPhase == NSEventPhaseBegan) {
- Q_ASSERT(pixelDelta.isNull() && angleDelta.isNull());
- return; // Ignore this event, as it has a delta of 0,0
+ return; // Ignore, even if it has delta
+ } else {
+ phase = Qt::ScrollEnd;
+ m_scrolling = false;
}
- phase = Qt::ScrollEnd;
- m_scrolling = false;
} else if (theEvent.momentumPhase == NSEventPhaseBegan) {
Q_ASSERT(!pixelDelta.isNull() && !angleDelta.isNull());
- phase = Qt::ScrollUpdate; // Send as update, it has a delta
+ // If we missed finding a momentum NSEventPhaseBegan when the non-momentum
+ // phase ended we need to treat this as a scroll begin, to not confuse client
+ // code. Otherwise we treat it as a continuation of the existing scroll.
+ phase = m_scrolling ? Qt::ScrollUpdate : Qt::ScrollBegin;
+ m_scrolling = true;
} else if (theEvent.momentumPhase == NSEventPhaseChanged) {
phase = Qt::ScrollMomentum;
} else if (theEvent.phase == NSEventPhaseCancelled
@@ -674,6 +694,16 @@
Q_ASSERT(theEvent.momentumPhase != NSEventPhaseStationary);
}
+ // Sanitize deltas for events that should not result in scrolling.
+ // On macOS 12.1 this phase has been observed to report deltas.
+ if (theEvent.phase == NSEventPhaseCancelled) {
+ if (!pixelDelta.isNull() || !angleDelta.isNull()) {
+ qCInfo(lcQpaMouse) << "Ignoring unexpected delta for" << theEvent;
+ pixelDelta = QPoint();
+ angleDelta = QPoint();
+ }
+ }
+
// Prevent keyboard modifier state from changing during scroll event streams.
// A two-finger trackpad flick generates a stream of scroll events. We want
// the keyboard modifier state to be the state at the beginning of the