aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@qt.io>2018-11-22 14:00:39 +0100
committerShawn Rutledge <shawn.rutledge@qt.io>2018-12-07 20:45:10 +0000
commitee2ac69595d3f854f7bf7158e600091f2675fa18 (patch)
tree50ef6ecedadd96ac1c563e65f16f28dd1ded2943
parent1cd3b2acfeb041ef54fe546b07a5e9efb5a2099b (diff)
Add dynamically-created Event Handlers to the relevant handlers vector
If any kind of Pointer Handler is created dynamically in JS by calling Component.createObject(), QObject::setParent() is called rather than passing the parent to the constructor, so QQuickItemPrivate::data_append() did not take care of adding the handler to QQuickItemPrivate's extra->pointerHandlers vector. We need to use the auto-parent mechanism (just as we did with handling dynamic creation of nested Windows in 8cb02e23abbefc9d020707fc1a2d8b6eb4e103b6). Added QQuickItemPrivate::addPointerHandler() to put the prepend() and implied setAcceptedMouseButtons() in one place. Fixes: QTBUG-71427 Change-Id: I3be3dd033c1c89e6e5b5c3463e1a720bbe963281 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
-rw-r--r--src/quick/items/qquickitem.cpp17
-rw-r--r--src/quick/items/qquickitem_p.h1
-rw-r--r--src/quick/items/qquickitemsmodule.cpp16
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/data/dynamicallyCreated.qml34
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/data/dynamicallyCreatedInWindow.qml20
-rw-r--r--tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp65
6 files changed, 137 insertions, 16 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 064406ee3c..015fe66202 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -3281,11 +3281,7 @@ void QQuickItemPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className());
else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
Q_ASSERT(pointerHandler->parentItem() == that);
- // Accept all buttons, and leave filtering to pointerEvent() and/or user JS,
- // because there can be multiple handlers...
- that->setAcceptedMouseButtons(Qt::AllButtons);
- QQuickItemPrivate *p = QQuickItemPrivate::get(that);
- p->extra.value().pointerHandlers.prepend(pointerHandler);
+ QQuickItemPrivate::get(that)->addPointerHandler(pointerHandler);
} else {
QQuickWindow *thisWindow = qmlobject_cast<QQuickWindow *>(o);
QQuickItem *item = that;
@@ -8207,6 +8203,17 @@ bool QQuickItemPrivate::hasHoverHandlers() const
return false;
}
+void QQuickItemPrivate::addPointerHandler(QQuickPointerHandler *h)
+{
+ Q_Q(QQuickItem);
+ // Accept all buttons, and leave filtering to pointerEvent() and/or user JS,
+ // because there can be multiple handlers...
+ q->setAcceptedMouseButtons(Qt::AllButtons);
+ auto &handlers = extra.value().pointerHandlers;
+ if (!handlers.contains(h))
+ handlers.prepend(h);
+}
+
#if QT_CONFIG(quick_shadereffect)
QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
: m_item(item)
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 11b47114cf..4ca3a01d02 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -281,6 +281,7 @@ public:
bool hasPointerHandlers() const;
bool hasHoverHandlers() const;
+ void addPointerHandler(QQuickPointerHandler *h);
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index a20150e3b9..b2fcfb4307 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -145,6 +145,7 @@ static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject
return QQmlPrivate::Parented;
}
} else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj)) {
+ QQuickItemPrivate::get(parentItem)->addPointerHandler(handler);
handler->setParent(parent);
return QQmlPrivate::Parented;
}
@@ -156,13 +157,14 @@ static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject
qCDebug(lcTransient) << win << "is transient for" << parentWindow;
win->setTransientParent(parentWindow);
return QQmlPrivate::Parented;
- } else {
- QQuickItem *item = qmlobject_cast<QQuickItem *>(obj);
- if (item) {
- // The parent of an Item inside a Window is actually the implicit content Item
- item->setParentItem(parentWindow->contentItem());
- return QQmlPrivate::Parented;
- }
+ } else if (QQuickItem *item = qmlobject_cast<QQuickItem *>(obj)) {
+ // The parent of an Item inside a Window is actually the implicit content Item
+ item->setParentItem(parentWindow->contentItem());
+ return QQmlPrivate::Parented;
+ } else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj)) {
+ QQuickItemPrivate::get(parentWindow->contentItem())->addPointerHandler(handler);
+ handler->setParent(parentWindow->contentItem());
+ return QQmlPrivate::Parented;
}
return QQmlPrivate::IncompatibleObject;
} else if (qmlobject_cast<QQuickItem *>(obj)) {
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/dynamicallyCreated.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/dynamicallyCreated.qml
new file mode 100644
index 0000000000..8f774a7ec3
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/dynamicallyCreated.qml
@@ -0,0 +1,34 @@
+import QtQuick 2.12
+import Qt.test 1.0
+
+Item {
+ id: root
+ objectName: "root Item"
+ width: 320
+ height: 480
+
+ Rectangle {
+ objectName: "eventItem's bounds"
+ anchors.fill: eventItem
+ color: "lightsteelblue"
+ }
+
+ EventItem {
+ id: eventItem
+ objectName: "eventItem1"
+ x: 5
+ y: 5
+ height: 30
+ width: 30
+
+ Component.onCompleted: handlerComponent.createObject(eventItem)
+
+ Component {
+ id: handlerComponent
+
+ EventHandler {
+ objectName: "eventHandler"
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/dynamicallyCreatedInWindow.qml b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/dynamicallyCreatedInWindow.qml
new file mode 100644
index 0000000000..058726b267
--- /dev/null
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/data/dynamicallyCreatedInWindow.qml
@@ -0,0 +1,20 @@
+import QtQuick 2.12
+import QtQuick.Window 2.12
+import Qt.test 1.0
+
+Window {
+ id: root
+ objectName: "root Window"
+ width: 320
+ height: 480
+
+ Component.onCompleted: handlerComponent.createObject(root)
+
+ Component {
+ id: handlerComponent
+
+ EventHandler {
+ objectName: "eventHandler"
+ }
+ }
+}
diff --git a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
index e8b076052a..906cbf63e4 100644
--- a/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
+++ b/tests/auto/quick/pointerhandlers/qquickpointerhandler/tst_qquickpointerhandler.cpp
@@ -182,12 +182,18 @@ public:
class EventHandler : public QQuickPointerHandler
{
+public:
void handlePointerEventImpl(QQuickPointerEvent *event) override
{
QQuickPointerHandler::handlePointerEventImpl(event);
if (!enabled())
return;
- EventItem *item = static_cast<EventItem *>(target());
+ ++eventCount;
+ EventItem *item = qmlobject_cast<EventItem *>(target());
+ if (!item) {
+ event->point(0)->setGrabberPointerHandler(this);
+ return;
+ }
qCDebug(lcPointerTests) << item->objectName() << event;
int c = event->pointCount();
for (int i = 0; i < c; ++i) {
@@ -206,10 +212,13 @@ class EventHandler : public QQuickPointerHandler
void onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabTransition stateChange, QQuickEventPoint *point) override
{
- EventItem *item = static_cast<EventItem *>(target());
- item->eventList.append(Event(Event::HandlerDestination, QEvent::None,
- static_cast<Qt::TouchPointState>(point->state()), stateChange, eventPos(point), point->scenePosition()));
+ EventItem *item = qmlobject_cast<EventItem *>(target());
+ if (item)
+ item->eventList.append(Event(Event::HandlerDestination, QEvent::None,
+ static_cast<Qt::TouchPointState>(point->state()), stateChange, eventPos(point), point->scenePosition()));
}
+
+ int eventCount = 0;
};
class tst_PointerHandlers : public QQmlDataTest
@@ -228,6 +237,8 @@ private slots:
void mouseEventDelivery();
void touchReleaseOutside_data();
void touchReleaseOutside();
+ void dynamicCreation();
+ void dynamicCreationInWindow();
protected:
bool eventFilter(QObject *, QEvent *event)
@@ -593,6 +604,52 @@ void tst_PointerHandlers::touchReleaseOutside()
QCOMPARE_EVENT(endIndexToTest, endDestination, endType, endState, endGrabState);
}
+void tst_PointerHandlers::dynamicCreation()
+{
+ QScopedPointer<QQuickView> windowPtr;
+ createView(windowPtr, "dynamicallyCreated.qml");
+ QQuickView * window = windowPtr.data();
+
+ EventItem *eventItem1 = window->rootObject()->findChild<EventItem*>("eventItem1");
+ QVERIFY(eventItem1);
+ EventHandler *handler = window->rootObject()->findChild<EventHandler*>("eventHandler");
+ QVERIFY(handler);
+
+ QCOMPARE(handler->parentItem(), eventItem1);
+ QCOMPARE(handler->target(), eventItem1);
+
+ QPoint p1(20, 20);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTRY_COMPARE(eventItem1->eventList.size(), 2);
+ QCOMPARE_EVENT(0, Event::HandlerDestination, QEvent::Pointer, Qt::TouchPointPressed, NoGrab);
+ QCOMPARE_EVENT(1, Event::MouseDestination, QEvent::MouseButtonPress, Qt::TouchPointPressed, NoGrab);
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
+}
+
+void tst_PointerHandlers::dynamicCreationInWindow()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.loadUrl(testFileUrl("dynamicallyCreatedInWindow.qml"));
+ QQuickWindow *window = qobject_cast<QQuickWindow*>(component.create());
+ QScopedPointer<QQuickWindow> cleanup(window);
+ QVERIFY(window);
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window));
+
+ EventHandler *handler = window->contentItem()->findChild<EventHandler*>("eventHandler");
+ QVERIFY(handler);
+
+ QCOMPARE(handler->parentItem(), window->contentItem());
+ QCOMPARE(handler->target(), window->contentItem());
+
+ QPoint p1(20, 20);
+ QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTRY_COMPARE(handler->eventCount, 1);
+ QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, p1);
+ QTRY_COMPARE(handler->eventCount, 2);
+}
+
QTEST_MAIN(tst_PointerHandlers)
#include "tst_qquickpointerhandler.moc"