aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2022-12-14 09:52:51 +0100
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2023-01-04 09:35:25 +0100
commitb9565b3aff7750968e8e885832c2ebd2997969ce (patch)
tree7010b88ad795b044632a9a36f35acf59e7a3ae4d
parenta21b91c99d3a94a03e6f2b51cf636d939f4d5445 (diff)
QQuickWidget: always accept touch events and grabbed event points
A QQuickWidget contains a Quick UI, which can be expected to handle touch events, and it handles touch events by forwarding them to the QQuickWindow. So set the AcceptTouchEvents attribute and let the Qt Quick delivery machinery deal with the touch-mouse synthesis within the scene. Also, Qt Quick's event delivery might return event points as ignored after setting the exclusive grabber. Qt Widgets touch event delivery logic doesn't care about exclusive grabbers, and relies on the event points being accepted to make the widget that received the TouchBegin an implicit grabber. QQuickWidget needs to translate those states back, so accept all points that come back with a grabber. Add a test that verifies that a button in a popup gets all events, and that those events are translated correctly - without the fix, the "clicked" test fails, as the release is delivered, but with coordinates outside of the button. Also test that we can have two QQuickWidgets where each gets one touch point. Fixes: QTBUG-101736 Change-Id: I3a2bf05fd297ae4d72b6e236ecd8e5ddac37ce06 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Paul Wicking <paul.wicking@qt.io> (cherry picked from commit dc8f44b14501ecd4acc196f5138aeff3f7502d0a) Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
-rw-r--r--src/quickwidgets/qquickwidget.cpp19
-rw-r--r--tests/auto/quickwidgets/qquickwidget/CMakeLists.txt1
-rw-r--r--tests/auto/quickwidgets/qquickwidget/data/button.qml27
-rw-r--r--tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp68
4 files changed, 113 insertions, 2 deletions
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index b4b06a8c66..3bb9d9fbe1 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -664,6 +664,7 @@ QQuickWidget::QQuickWidget(QWidget *parent)
{
setMouseTracking(true);
setFocusPolicy(Qt::StrongFocus);
+ setAttribute(Qt::WA_AcceptTouchEvents);
d_func()->init();
}
@@ -1638,9 +1639,23 @@ bool QQuickWidget::event(QEvent *e)
case QEvent::TouchBegin:
case QEvent::TouchEnd:
case QEvent::TouchUpdate:
- case QEvent::TouchCancel:
+ case QEvent::TouchCancel: {
// Touch events only have local and global positions, no need to map.
- return QCoreApplication::sendEvent(d->offscreenWindow, e);
+ bool res = QCoreApplication::sendEvent(d->offscreenWindow, e);
+ if (e->isAccepted() && e->type() == QEvent::TouchBegin) {
+ // If the TouchBegin got accepted, then make sure all points that have
+ // an exclusive grabber are also accepted so that the widget code for
+ // delivering touch events make this widget an implicit grabber of those
+ // points.
+ QPointerEvent *pointerEvent = static_cast<QPointerEvent *>(e);
+ auto deliveredPoints = pointerEvent->points();
+ for (auto &point : deliveredPoints) {
+ if (pointerEvent->exclusiveGrabber(point))
+ point.setAccepted(true);
+ }
+ }
+ return res;
+ }
case QEvent::FocusAboutToChange:
return QCoreApplication::sendEvent(d->offscreenWindow, e);
diff --git a/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt b/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt
index 24da087db5..5ce5cf3125 100644
--- a/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt
+++ b/tests/auto/quickwidgets/qquickwidget/CMakeLists.txt
@@ -19,6 +19,7 @@ qt_internal_add_test(tst_qquickwidget
Qt::GuiPrivate
Qt::QmlPrivate
Qt::QuickPrivate
+ Qt::QuickTemplates2Private
Qt::QuickWidgets
Qt::QuickWidgetsPrivate
Qt::WidgetsPrivate
diff --git a/tests/auto/quickwidgets/qquickwidget/data/button.qml b/tests/auto/quickwidgets/qquickwidget/data/button.qml
new file mode 100644
index 0000000000..30698908d7
--- /dev/null
+++ b/tests/auto/quickwidgets/qquickwidget/data/button.qml
@@ -0,0 +1,27 @@
+import QtQuick
+import QtQuick.Controls.Basic
+
+Item {
+ width: 100
+ height: 100
+ visible: true
+
+ property bool wasPressed: false
+ property bool wasReleased: false
+ property bool wasClicked: false
+
+ Popup {
+ closePolicy: Popup.NoAutoClose
+ visible: true
+
+ Button {
+ objectName: "button"
+ text: "TAP ME"
+ anchors.fill: parent
+
+ onPressed: wasPressed = true
+ onReleased: wasReleased = true
+ onClicked: wasClicked = true
+ }
+ }
+}
diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
index d3c5bc8f24..54673b70ce 100644
--- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
+++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
@@ -36,6 +36,7 @@
#include <QtQuick/qquickitem.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickmousearea_p.h>
+#include <QtQuickTemplates2/private/qquickbutton_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include <QtGui/QWindow>
#include <QtGui/QScreen>
@@ -150,6 +151,8 @@ private slots:
void synthMouseFromTouch_data();
void synthMouseFromTouch();
void touchTapMouseArea();
+ void touchTapButton();
+ void touchMultipleWidgets();
void tabKey();
void resizeOverlay();
void controls();
@@ -678,6 +681,71 @@ void tst_qquickwidget::touchTapMouseArea()
QVERIFY(rootItem->property("wasClicked").toBool());
}
+void tst_qquickwidget::touchTapButton()
+{
+ QWidget window;
+ QQuickWidget *quick = new QQuickWidget;
+ quick->setSource(testFileUrl("button.qml"));
+
+ QHBoxLayout hbox;
+ hbox.addWidget(quick);
+ window.setLayout(&hbox);
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickItem *rootItem = quick->rootObject();
+ QVERIFY(rootItem);
+ QQuickButton *button = rootItem->findChild<QQuickButton *>("button");
+ QVERIFY(button);
+
+ const QPoint point = quick->mapTo(&window, button->mapToScene(button->boundingRect().center()).toPoint());
+ QTest::touchEvent(&window, device).press(0, point, &window).commit();
+ QTRY_VERIFY(rootItem->property("wasPressed").toBool());
+ QTest::touchEvent(&window, device).release(0, point, &window).commit();
+ QTRY_VERIFY(rootItem->property("wasReleased").toBool());
+ QTRY_VERIFY(rootItem->property("wasClicked").toBool());
+}
+
+void tst_qquickwidget::touchMultipleWidgets()
+{
+ QWidget window;
+ QQuickWidget *leftQuick = new QQuickWidget;
+ leftQuick->setSource(testFileUrl("button.qml"));
+ QQuickWidget *rightQuick = new QQuickWidget;
+ rightQuick->setSource(testFileUrl("button.qml"));
+
+ QHBoxLayout hbox;
+ hbox.addWidget(leftQuick);
+ hbox.addWidget(rightQuick);
+ window.setLayout(&hbox);
+ window.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&window));
+
+ QQuickItem *leftRootItem = leftQuick->rootObject();
+ QQuickItem *rightRootItem = rightQuick->rootObject();
+ QVERIFY(leftRootItem);
+ QVERIFY(rightRootItem);
+ QQuickButton *leftButton = leftRootItem->findChild<QQuickButton *>("button");
+ QQuickButton *rightButton = rightRootItem->findChild<QQuickButton *>("button");
+ QVERIFY(leftButton);
+ QVERIFY(rightButton);
+
+ const QPoint leftPoint = leftQuick->mapTo(&window, leftButton->mapToScene(
+ leftButton->boundingRect().center()).toPoint());
+ const QPoint rightPoint = rightQuick->mapTo(&window, rightButton->mapToScene(
+ rightButton->boundingRect().center()).toPoint());
+ QTest::touchEvent(&window, device).press(0, leftPoint, &window).commit();
+ QTRY_VERIFY(leftRootItem->property("wasPressed").toBool());
+ QTest::touchEvent(&window, device).press(1, rightPoint, &window).commit();
+ QTRY_VERIFY(rightRootItem->property("wasPressed").toBool());
+ QTest::touchEvent(&window, device).release(1, rightPoint, &window).commit();
+ QTRY_VERIFY(rightRootItem->property("wasReleased").toBool());
+ QVERIFY(rightRootItem->property("wasClicked").toBool());
+ QTest::touchEvent(&window, device).release(0, leftPoint, &window).commit();
+ QTRY_VERIFY(leftRootItem->property("wasReleased").toBool());
+ QVERIFY(leftRootItem->property("wasClicked").toBool());
+}
+
void tst_qquickwidget::tabKey()
{
if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls)