QWindow: move context menu synthesis code into private virtual
QQuickWindow needs to be able to call this to ensure that it gets the context menu event after the mouse event, and not the other way around, otherwise the menu is immediately closed after opening. Move the code into QWindowPrivate::maybeSynthesizeContextMenuEvent(), which is called only for mouse press and release events, and which does the synthesis only if the event was not already accepted and has no exclusive grabber. Use scenePosition() to avoid getting a localized position left over from delivery to specific widgets or Qt Quick items. Add explanations to internal docs. This also opens up the opportunity for QQuickWindowPrivate to do this in a Qt Quick-specific way. Task-number: QTBUG-67331 Task-number: QTBUG-93486 Change-Id: I909671d9d62c9007b22646cbea6eede7465ab686 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
This commit is contained in:
parent
40c3b28172
commit
9f75fe29d3
@ -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<QMouseEvent*>(ev));
|
||||
break;
|
||||
|
||||
case QEvent::MouseButtonPress:
|
||||
mousePressEvent(static_cast<QMouseEvent*>(ev));
|
||||
case QEvent::MouseButtonPress: {
|
||||
auto *me = static_cast<QMouseEvent*>(ev);
|
||||
mousePressEvent(me);
|
||||
d->maybeSynthesizeContextMenuEvent(me);
|
||||
break;
|
||||
}
|
||||
|
||||
case QEvent::MouseButtonRelease:
|
||||
mouseReleaseEvent(static_cast<QMouseEvent*>(ev));
|
||||
case QEvent::MouseButtonRelease: {
|
||||
auto *me = static_cast<QMouseEvent*>(ev);
|
||||
mouseReleaseEvent(me);
|
||||
d->maybeSynthesizeContextMenuEvent(me);
|
||||
break;
|
||||
}
|
||||
|
||||
case QEvent::MouseButtonDblClick:
|
||||
mouseDoubleClickEvent(static_cast<QMouseEvent*>(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<QMouseEvent *>(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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -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; }
|
||||
|
Loading…
x
Reference in New Issue
Block a user