summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <[email protected]>2024-11-01 12:31:56 +0100
committerShawn Rutledge <[email protected]>2024-11-05 21:54:40 +0100
commit601924cce57ee564ec149a170f1987b13924e0ee (patch)
tree6d925f3b28c423afabac662e118a8d7e9f4bff8b
parent7d62b54713720c62dfae3e26041cd688b368c12d (diff)
Restore event replay when a popup is closed by mouse press outside
If a popup (such as a context menu or combobox) is open, and the user presses any mouse button outside the popup, Windows users expect the mouse event to be sent to whatever is under the mouse, while the popup closes. (So the popup must close on press, not on release.) QPlatformIntegration::ReplayMousePressOutsidePopup requests this platform-specific behavior in general, and the WA_NoMouseReplay attribute can disable it on specific widgets. e4ef0f03e6f1fddc397980fd7fbf6f6b829f16d9 removed this feature which was added to Qt 5 in 1f456b4cbb93e3fea699878d117900b703146213, based on doubt that we really needed it: and if we did, maybe we would need it in QtGui. But so far it seems the main excuse for doing it this way is that popups are sometimes opened with exec(). If the nested event loop handles the mouse press completely, and the QPA event is discarded, the outer loop has no chance of seeing it after exec() finishes. In Qt Quick, we don't use exec(); so let's assume that this continues to be needed only for widgets. At least we don't use extern sharing of a global bool in this version. Fixes: QTBUG-130138 Pick-to: 6.8 Change-Id: I95b5d24ee9bc8608655ed5c585d1d91a891fbad3 Reviewed-by: Volker Hilsheimer <[email protected]>
-rw-r--r--src/widgets/kernel/qapplication.cpp8
-rw-r--r--src/widgets/kernel/qapplication_p.h2
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp34
3 files changed, 44 insertions, 0 deletions
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index 89d451589a8..e8c7598a826 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -124,6 +124,8 @@ QApplicationPrivate *QApplicationPrivate::self = nullptr;
bool QApplicationPrivate::autoSipEnabled = true;
+bool QApplicationPrivate::replayMousePress = false;
+
QApplicationPrivate::QApplicationPrivate(int &argc, char **argv)
: QGuiApplicationPrivate(argc, argv)
{
@@ -3350,6 +3352,12 @@ void QApplicationPrivate::closePopup(QWidget *popup)
if (popupGrabOk) {
popupGrabOk = false;
+ if (active_window && active_window->windowHandle()
+ && !popup->geometry().contains(QGuiApplicationPrivate::lastCursorPosition.toPoint())
+ && !popup->testAttribute(Qt::WA_NoMouseReplay)) {
+ QApplicationPrivate::replayMousePress = true;
+ }
+
// transfer grab back to mouse grabber if any, otherwise release the grab
ungrabMouseForPopup(popup);
diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h
index 5f1d4ef13c3..622d36135cd 100644
--- a/src/widgets/kernel/qapplication_p.h
+++ b/src/widgets/kernel/qapplication_p.h
@@ -109,6 +109,8 @@ public:
static bool inPopupMode();
void closePopup(QWidget *popup);
void openPopup(QWidget *popup);
+ static bool replayMousePress;
+
static void setFocusWidget(QWidget *focus, Qt::FocusReason reason);
static QWidget *focusNextPrevChild_helper(QWidget *toplevel, bool next,
bool *wrappingOccurred = nullptr);
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index e3f92564cce..1d2b7dfadbf 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -588,6 +588,40 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
}
}
+ if (QApplication::activePopupWidget() != activePopupWidget
+ && QApplicationPrivate::replayMousePress
+ && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ReplayMousePressOutsidePopup).toBool()) {
+ if (m_widget->windowType() != Qt::Popup)
+ qt_button_down = nullptr;
+ if (event->type() == QEvent::MouseButtonPress) {
+ // the popup disappeared: replay the mouse press event to whatever is behind it
+ QWidget *w = QApplication::widgetAt(event->globalPosition().toPoint());
+ if (w && !QApplicationPrivate::isBlockedByModal(w)) {
+ // activate window of the widget under mouse pointer
+ if (!w->isActiveWindow()) {
+ w->activateWindow();
+ w->window()->raise();
+ }
+
+ if (auto win = qt_widget_private(w)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)) {
+ const QRect globalGeometry = win->isTopLevel()
+ ? win->geometry()
+ : QRect(win->mapToGlobal(QPoint(0, 0)), win->size());
+ if (globalGeometry.contains(event->globalPosition().toPoint())) {
+ // Use postEvent() to ensure the local QEventLoop terminates when called from QMenu::exec()
+ const QPoint localPos = win->mapFromGlobal(event->globalPosition().toPoint());
+ QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonPress, localPos, localPos, event->globalPosition().toPoint(),
+ event->button(), event->buttons(), event->modifiers(), event->source());
+ QCoreApplicationPrivate::setEventSpontaneous(e, true);
+ e->setTimestamp(event->timestamp());
+ QCoreApplication::postEvent(win, e);
+ }
+ }
+ }
+ }
+ QApplicationPrivate::replayMousePress = false;
+ }
+
if (releaseAfter) {
qt_button_down = nullptr;
qt_popup_down_closed = false;