aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <[email protected]>2024-05-08 17:53:34 -0700
committerShawn Rutledge <[email protected]>2024-05-30 06:29:07 -0700
commit7b5d6f668330a80212be8b3ed5c19fe86ec04f55 (patch)
treefe3694507a68be112d1d3ccfb2a23ce1275a12d4
parent562353a720f6d4aa8bec0819503f5ce3f2fe74fe (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.cpp28
-rw-r--r--src/quicktemplates/qquickmenu_p_p.h1
-rw-r--r--src/quicktemplates/qquickpopup_p_p.h1
-rw-r--r--src/quicktemplates/qquickpopupwindow.cpp16
-rw-r--r--src/quicktemplates/qquickpopupwindow_p_p.h1
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);