diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 4612d88a6e5..25c2bdebfbd 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -2609,18 +2609,25 @@ void QWindow::closeEvent(QCloseEvent *ev) */ bool QWindow::event(QEvent *ev) { + Q_D(QWindow); switch (ev->type()) { case QEvent::MouseMove: mouseMoveEvent(static_cast(ev)); break; - case QEvent::MouseButtonPress: - mousePressEvent(static_cast(ev)); + case QEvent::MouseButtonPress: { + auto *me = static_cast(ev); + mousePressEvent(me); + d->maybeSynthesizeContextMenuEvent(me); break; + } - case QEvent::MouseButtonRelease: - mouseReleaseEvent(static_cast(ev)); + case QEvent::MouseButtonRelease: { + auto *me = static_cast(ev); + mouseReleaseEvent(me); + d->maybeSynthesizeContextMenuEvent(me); break; + } case QEvent::MouseButtonDblClick: mouseDoubleClickEvent(static_cast(ev)); @@ -2677,7 +2684,6 @@ bool QWindow::event(QEvent *ev) case QEvent::Close: { - Q_D(QWindow); const bool wasVisible = d->treatAsVisible(); const bool participatesInLastWindowClosed = d->participatesInLastWindowClosed(); @@ -2738,35 +2744,50 @@ bool QWindow::event(QEvent *ev) return QObject::event(ev); } + return true; +} + +/*! \internal + Synthesize and send a QContextMenuEvent if the given \a event is a suitable + mouse event (a right-button press or release, depending on + QStyleHints::contextMenuTrigger()) that was *not accepted* and *isn't* + exclusively grabbed. On most platforms, it's done on mouse release; on + Windows, it's done on press, because of the potential to support + right-button clicks and drags to select or lasso items, and then still + getting a context menu at the end of that gesture. (That is in conflict + with supporting the press-drag-release gesture to select menu items on the + context menus themselves. Context menus can be implemented that way by + handling the separate press, move and release events.) Any time the + \a event was already handled in some way, it must be accepted, to avoid + synthesis of the QContextMenuEvent here. + + The QContextMenuEvent occurs at the scenePosition(). The position() + was likely already "localized" during the previous delivery. + + The synthesis from a mouse button event could be done in the platform + plugin, but so far on Windows it's not done: WM_CONTEXTMENU is not + generated by the OS, because we never call the default window procedure + that would do that in response to unhandled WM_RBUTTONUP. If we + eventually want to do that, we would have to avoid doing it here, + on platforms where the platform plugin is responsible for it. + + QGuiApplicationPrivate::processContextMenuEvent also allows + keyboard-triggered context menu events that the QPA plugin might generate. + On Windows, the keyboard may have a menu key. On macOS, control-return + is the usual shortcut; on Gnome, it's shift-F10; and so on. +*/ +void QWindowPrivate::maybeSynthesizeContextMenuEvent(QMouseEvent *event) +{ #ifndef QT_NO_CONTEXTMENU - /* - QGuiApplicationPrivate::processContextMenuEvent blocks mouse-triggered - context menu events that the QPA plugin might generate. In practice that - never happens, as even on Windows WM_CONTEXTMENU is never generated by - the OS (we never call the default window procedure that would do that in - response to unhandled WM_RBUTTONUP). - - So, we always have to syntheize QContextMenuEvent for mouse events anyway. - QWidgetWindow synthesizes QContextMenuEvent similar to this code, and - never calls QWindow::event, so we have to do it here as well. - - This logic could be simplified by always synthesizing events in - QGuiApplicationPrivate, or perhaps even in each QPA plugin. See QTBUG-93486. - */ - auto asMouseEvent = [](QEvent *ev) { - const auto t = ev->type(); - return t == QEvent::MouseButtonPress || t == QEvent::MouseButtonRelease - ? static_cast(ev) : nullptr ; - }; - if (QMouseEvent *me = asMouseEvent(ev); - me && ev->type() == QGuiApplicationPrivate::contextMenuEventType() - && me->button() == Qt::RightButton) { - QContextMenuEvent e(QContextMenuEvent::Mouse, me->position().toPoint(), - me->globalPosition().toPoint(), me->modifiers()); - QGuiApplication::sendEvent(this, &e); + if (!event->isAccepted() && !event->exclusivePointGrabber() + && event->button() == Qt::RightButton + && event->type() == QGuiApplicationPrivate::contextMenuEventType()) { + QContextMenuEvent e(QContextMenuEvent::Mouse, event->scenePosition().toPoint(), + event->globalPosition().toPoint(), event->modifiers()); + qCDebug(lcPopup) << "synthesized QContextMenuEvent after un-accepted" << event->type() << ":" << &e; + QGuiApplication::sendEvent(q_func(), &e); } #endif - return true; } /*! diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h index a9d89210c92..0d513285173 100644 --- a/src/gui/kernel/qwindow_p.h +++ b/src/gui/kernel/qwindow_p.h @@ -95,6 +95,8 @@ public: virtual bool participatesInLastWindowClosed() const; virtual bool treatAsVisible() const; + virtual void maybeSynthesizeContextMenuEvent(QMouseEvent *event); + const QWindow *forwardToPopup(QEvent *event, const QWindow *activePopupOnPress); bool isPopup() const { return (windowFlags & Qt::WindowType_Mask) == Qt::Popup; }