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
8cb02e23ab
). 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>
This commit is contained in:
parent
1cd3b2acfe
commit
ee2ac69595
|
@ -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)
|
||||
|
|
|
@ -281,6 +281,7 @@ public:
|
|||
|
||||
bool hasPointerHandlers() const;
|
||||
bool hasHoverHandlers() const;
|
||||
void addPointerHandler(QQuickPointerHandler *h);
|
||||
|
||||
// data property
|
||||
static void data_append(QQmlListProperty<QObject> *, QObject *);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue