Skip to content

Commit

Permalink
Restore event replay when a popup is closed by mouse press outside
Browse files Browse the repository at this point in the history
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.

e4ef0f0 removed this feature which was
added to Qt 5 in 1f456b4, 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
Change-Id: I95b5d24ee9bc8608655ed5c585d1d91a891fbad3
Reviewed-by: Volker Hilsheimer <[email protected]>
(cherry picked from commit 601924c)
Reviewed-by: Qt Cherry-pick Bot <[email protected]>
  • Loading branch information
ec1oud authored and Qt Cherry-pick Bot committed Nov 5, 2024
1 parent d480177 commit 3544fa1
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/widgets/kernel/qapplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions src/widgets/kernel/qapplication_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class Q_WIDGETS_EXPORT QApplicationPrivate : public QGuiApplicationPrivate
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);
Expand Down
34 changes: 34 additions & 0 deletions src/widgets/kernel/qwidgetwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 3544fa1

Please sign in to comment.