macOS: Don't close popups in Cocoa plugin for most mouse events

QWidget and QApplication handle popup closing for most events at the
right time for the Qt-internal state logic. On other platforms popup
QWindows are never closed automatically when clicking outside.
So don't close any popups in the Cocoa plugin either, and let the Qt
logic take care of it. This ensures that window activation is done at
the right time, that Qt's modal popup stack is consistent, and that
mouse replay for events closing a popup works.

There are however two exceptions: mouse events in the window frame don't
produce a QMouseEvent for Qt; and mouse events in a modally blocked (by
Cocoa) window don't reach Qt at all. For those case, the logic in QWidget
and QApplication is not enough.

For the former, leave the change introduced in 70b94eea10
to explicitly close popups for LMB down in the frame. This still needs
to happen before the event is delivered.

For the latter case, deliver the event explicitly to Qt when we discover
that the target window is modally blocked while a popup is active. The
handleMouseEvent implementation then takes care of the redirect to the
active popup, and Qt will further respect the modal stack in the
QApplication::isWindowBlocked implementation.

Change-Id: I578eb5e6aebc897a0ff1f69bc5c53bcaa05d138d
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Volker Hilsheimer 2021-09-08 14:57:37 +02:00
parent 1093562d08
commit 73753ee3af
3 changed files with 13 additions and 10 deletions

View File

@ -56,6 +56,7 @@ QT_DECLARE_NAMESPACED_OBJC_INTERFACE(QNSView, NSView
#if defined(__OBJC__)
@interface QNSView (MouseAPI)
- (void)handleMouseEvent:(NSEvent *)theEvent;
- (void)handleFrameStrutMouseEvent:(NSEvent *)theEvent;
- (bool)closePopups:(NSEvent *)theEvent;
- (void)resetMouseButtons;

View File

@ -446,16 +446,6 @@ static const QPointingDevice *pointingDeviceFor(qint64 deviceID)
return [super mouseDown:theEvent];
m_sendUpAsRightButton = false;
// Handle any active poup windows; clicking outisde them should close them
// all. Don't do anything or clicks inside one of the menus, let Cocoa
// handle that case. Note that in practice many windows of the Qt::Popup type
// will actually close themselves in this case using logic implemented in
// 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.
if ([self closePopups:theEvent])
return;
QPointF qtWindowPoint;
QPointF qtScreenPoint;
[self convertFromScreen:[self screenMousePoint:theEvent] toWindowPoint:&qtWindowPoint andScreenPoint:&qtScreenPoint];

View File

@ -45,6 +45,7 @@
#include "qcocoawindow.h"
#include "qcocoahelpers.h"
#include "qcocoaeventdispatcher.h"
#include "qcocoaintegration.h"
#include <qpa/qwindowsysteminterface.h>
#include <qoperatingsystemversion.h>
@ -370,6 +371,17 @@ OSStatus CGSClearWindowTags(const CGSConnectionID, const CGSWindowID, int *, int
if (!m_platformWindow)
return; // Platform window went away while processing event
// Cocoa will not deliver mouse events to a window that is modally blocked (by Cocoa,
// not Qt). However, an active popup is expected to grab any mouse event within the
// application, so we need to handle those explicitly and trust Qt's isWindowBlocked
// implementation to eat events that shouldn't be delivered anyway.
if (isMouseEvent(theEvent) && QCocoaIntegration::instance()->activePopupWindow()
&& QGuiApplicationPrivate::instance()->isWindowBlocked(m_platformWindow->window(), nullptr)) {
qCDebug(lcQpaWindow) << "Mouse event over modally blocked window" << m_platformWindow->window()
<< "while popup" << QCocoaIntegration::instance()->activePopupWindow()
<< "is open - redirecting";
[qnsview_cast(m_platformWindow->view()) handleMouseEvent:theEvent];
}
if (m_platformWindow->frameStrutEventsEnabled() && mouseEventInFrameStrut)
[qnsview_cast(m_platformWindow->view()) handleFrameStrutMouseEvent:theEvent];
}