diff options
author | Shawn Rutledge <[email protected]> | 2024-05-08 17:53:34 -0700 |
---|---|---|
committer | Shawn Rutledge <[email protected]> | 2024-05-30 06:29:07 -0700 |
commit | 7b5d6f668330a80212be8b3ed5c19fe86ec04f55 (patch) | |
tree | fe3694507a68be112d1d3ccfb2a23ce1275a12d4 | |
parent | 562353a720f6d4aa8bec0819503f5ce3f2fe74fe (diff) |
Add and use QQPopupPrivate::handleReleaseWithoutGrab for menus
QQuickDeliveryAgent doesn't allow an item to see a pointer release event
unless the item has already grabbed; that's usually done on press.
But during the drag-press-release gesture, we don't know which menu item
the user will choose until the release. QQuickMenu isn't doing all the
event handling on its own, and thus isn't the grabber: QQuickMenuItem
inherits QQuickAbstractButton, and if that "button" didn't get grabbed,
it's not going to react to the release. So we need a workaround.
Given that the popup window did not see the mouse/touch/tablet press
but just some moves and a release, there will not be a grabber within
the popup window. In that case, on release, call virtual
QQuickPopupPrivate::handleReleaseWithoutGrab(), which is overridden in
QQuickMenuPrivate to programmatically "click" the currently-hovered menu
item. Then propagation continues, to allow the component that opened
the menu to also see the release, and exit from its "pressed" state.
Task-number: QTBUG-68080
Change-Id: I3280ffe722560726003522bb33e5152302cb8cbc
Reviewed-by: Mitch Curtis <[email protected]>
Reviewed-by: Richard Moe Gustavsen <[email protected]>
-rw-r--r-- | src/quicktemplates/qquickmenu.cpp | 28 | ||||
-rw-r--r-- | src/quicktemplates/qquickmenu_p_p.h | 1 | ||||
-rw-r--r-- | src/quicktemplates/qquickpopup_p_p.h | 1 | ||||
-rw-r--r-- | src/quicktemplates/qquickpopupwindow.cpp | 16 | ||||
-rw-r--r-- | src/quicktemplates/qquickpopupwindow_p_p.h | 1 |
5 files changed, 47 insertions, 0 deletions
diff --git a/src/quicktemplates/qquickmenu.cpp b/src/quicktemplates/qquickmenu.cpp index 7f06c9682f..6cf039a710 100644 --- a/src/quicktemplates/qquickmenu.cpp +++ b/src/quicktemplates/qquickmenu.cpp @@ -35,6 +35,7 @@ #include <QtQuick/private/qquickitem_p.h> #include <QtQuick/private/qquickitemchangelistener_p.h> #include <QtQuick/private/qquickevents_p_p.h> +#include <QtQuick/private/qquicklistview_p.h> #include <QtQuick/private/qquickrendercontrol_p.h> #include <QtQuick/private/qquickwindow_p.h> @@ -942,6 +943,33 @@ bool QQuickMenuPrivate::blockInput(QQuickItem *item, const QPointF &point) const return (cascade && parentMenu && contains(point)) || QQuickPopupPrivate::blockInput(item, point); } +/*! \internal + QQuickPopupWindow::event() calls this to handle the release event of a + menu drag-press-release gesture, because the \a eventPoint does not have + a grabber within the popup window. This override finds and activates the + appropriate menu item, as if it had been pressed and released. + Returns true on success, to indicate that handling \a eventPoint is done. + */ +bool QQuickMenuPrivate::handleReleaseWithoutGrab(const QEventPoint &eventPoint) +{ + QQuickMenuItem *menuItem = nullptr; + // Usually, hover events have occurred, and currentIndex is set. + // If not, use eventPoint.position() for picking. + if (currentIndex < 0) { + auto *list = qobject_cast<QQuickListView *>(contentItem); + if (!list) + return false; + menuItem = qobject_cast<QQuickMenuItem *>(list->itemAt(eventPoint.position().x(), eventPoint.position().y())); + } else { + menuItem = qobject_cast<QQuickMenuItem *>(itemAt(currentIndex)); + } + if (Q_LIKELY(menuItem)) { + menuItem->animateClick(); + return true; + } + return false; +} + void QQuickMenuPrivate::onItemHovered() { Q_Q(QQuickMenu); diff --git a/src/quicktemplates/qquickmenu_p_p.h b/src/quicktemplates/qquickmenu_p_p.h index 368623e873..e3003b7d55 100644 --- a/src/quicktemplates/qquickmenu_p_p.h +++ b/src/quicktemplates/qquickmenu_p_p.h @@ -93,6 +93,7 @@ public: bool prepareEnterTransition() override; bool prepareExitTransition() override; bool blockInput(QQuickItem *item, const QPointF &point) const override; + bool handleReleaseWithoutGrab(const QEventPoint &eventPoint) override; void onItemHovered(); void onItemTriggered(); diff --git a/src/quicktemplates/qquickpopup_p_p.h b/src/quicktemplates/qquickpopup_p_p.h index 556f3b6154..1c6d5283f5 100644 --- a/src/quicktemplates/qquickpopup_p_p.h +++ b/src/quicktemplates/qquickpopup_p_p.h @@ -85,6 +85,7 @@ public: virtual bool handlePress(QQuickItem* item, const QPointF &point, ulong timestamp); virtual bool handleMove(QQuickItem* item, const QPointF &point, ulong timestamp); virtual bool handleRelease(QQuickItem* item, const QPointF &point, ulong timestamp); + virtual bool handleReleaseWithoutGrab(const QEventPoint &) { return false; } virtual void handleUngrab(); bool handleMouseEvent(QQuickItem *item, QMouseEvent *event); diff --git a/src/quicktemplates/qquickpopupwindow.cpp b/src/quicktemplates/qquickpopupwindow.cpp index c61be40f94..03d7472bf1 100644 --- a/src/quicktemplates/qquickpopupwindow.cpp +++ b/src/quicktemplates/qquickpopupwindow.cpp @@ -99,6 +99,22 @@ void QQuickPopupWindow::resizeEvent(QResizeEvent *e) d->m_popupItem->setHeight(e->size().height()); } +bool QQuickPopupWindow::event(QEvent *e) +{ + Q_D(QQuickPopupWindow); + if (e->isPointerEvent()) { + auto *pe = static_cast<QPointerEvent *>(e); + const auto &eventPoint = pe->points().first(); + const auto *grabber = qmlobject_cast<QQuickItem *>(pe->exclusiveGrabber(eventPoint)); + if (pe->isEndEvent() && (!grabber || grabber->window() != this)) + QQuickPopupPrivate::get(d->m_popup)->handleReleaseWithoutGrab(eventPoint); + // handleReleaseWithoutGrab() returns true if the popup handled eventPoint. + // Nevertheless, the component that opened the menu needs to see the release too, + // to avoid getting stuck in "pressed" state; so we don't return early. + } + return QQuickWindowQmlImpl::event(e); +} + void QQuickPopupWindow::windowChanged(QWindow *window) { if (window) { diff --git a/src/quicktemplates/qquickpopupwindow_p_p.h b/src/quicktemplates/qquickpopupwindow_p_p.h index d4d4adda61..0b9842c059 100644 --- a/src/quicktemplates/qquickpopupwindow_p_p.h +++ b/src/quicktemplates/qquickpopupwindow_p_p.h @@ -37,6 +37,7 @@ protected: void hideEvent(QHideEvent *e) override; void moveEvent(QMoveEvent *e) override; void resizeEvent(QResizeEvent *e) override; + bool event(QEvent *e) override; private: void windowChanged(QWindow *window); |