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());
|
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)) {
|
else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
|
||||||
Q_ASSERT(pointerHandler->parentItem() == that);
|
Q_ASSERT(pointerHandler->parentItem() == that);
|
||||||
// Accept all buttons, and leave filtering to pointerEvent() and/or user JS,
|
QQuickItemPrivate::get(that)->addPointerHandler(pointerHandler);
|
||||||
// because there can be multiple handlers...
|
|
||||||
that->setAcceptedMouseButtons(Qt::AllButtons);
|
|
||||||
QQuickItemPrivate *p = QQuickItemPrivate::get(that);
|
|
||||||
p->extra.value().pointerHandlers.prepend(pointerHandler);
|
|
||||||
} else {
|
} else {
|
||||||
QQuickWindow *thisWindow = qmlobject_cast<QQuickWindow *>(o);
|
QQuickWindow *thisWindow = qmlobject_cast<QQuickWindow *>(o);
|
||||||
QQuickItem *item = that;
|
QQuickItem *item = that;
|
||||||
|
@ -8207,6 +8203,17 @@ bool QQuickItemPrivate::hasHoverHandlers() const
|
||||||
return false;
|
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)
|
#if QT_CONFIG(quick_shadereffect)
|
||||||
QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
|
QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
|
||||||
: m_item(item)
|
: m_item(item)
|
||||||
|
|
|
@ -281,6 +281,7 @@ public:
|
||||||
|
|
||||||
bool hasPointerHandlers() const;
|
bool hasPointerHandlers() const;
|
||||||
bool hasHoverHandlers() const;
|
bool hasHoverHandlers() const;
|
||||||
|
void addPointerHandler(QQuickPointerHandler *h);
|
||||||
|
|
||||||
// data property
|
// data property
|
||||||
static void data_append(QQmlListProperty<QObject> *, QObject *);
|
static void data_append(QQmlListProperty<QObject> *, QObject *);
|
||||||
|
|
|
@ -145,6 +145,7 @@ static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject
|
||||||
return QQmlPrivate::Parented;
|
return QQmlPrivate::Parented;
|
||||||
}
|
}
|
||||||
} else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj)) {
|
} else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj)) {
|
||||||
|
QQuickItemPrivate::get(parentItem)->addPointerHandler(handler);
|
||||||
handler->setParent(parent);
|
handler->setParent(parent);
|
||||||
return QQmlPrivate::Parented;
|
return QQmlPrivate::Parented;
|
||||||
}
|
}
|
||||||
|
@ -156,13 +157,14 @@ static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject
|
||||||
qCDebug(lcTransient) << win << "is transient for" << parentWindow;
|
qCDebug(lcTransient) << win << "is transient for" << parentWindow;
|
||||||
win->setTransientParent(parentWindow);
|
win->setTransientParent(parentWindow);
|
||||||
return QQmlPrivate::Parented;
|
return QQmlPrivate::Parented;
|
||||||
} else {
|
} else if (QQuickItem *item = qmlobject_cast<QQuickItem *>(obj)) {
|
||||||
QQuickItem *item = qmlobject_cast<QQuickItem *>(obj);
|
// The parent of an Item inside a Window is actually the implicit content Item
|
||||||
if (item) {
|
item->setParentItem(parentWindow->contentItem());
|
||||||
// The parent of an Item inside a Window is actually the implicit content Item
|
return QQmlPrivate::Parented;
|
||||||
item->setParentItem(parentWindow->contentItem());
|
} else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj)) {
|
||||||
return QQmlPrivate::Parented;
|
QQuickItemPrivate::get(parentWindow->contentItem())->addPointerHandler(handler);
|
||||||
}
|
handler->setParent(parentWindow->contentItem());
|
||||||
|
return QQmlPrivate::Parented;
|
||||||
}
|
}
|
||||||
return QQmlPrivate::IncompatibleObject;
|
return QQmlPrivate::IncompatibleObject;
|
||||||
} else if (qmlobject_cast<QQuickItem *>(obj)) {
|
} 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
|
class EventHandler : public QQuickPointerHandler
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
void handlePointerEventImpl(QQuickPointerEvent *event) override
|
void handlePointerEventImpl(QQuickPointerEvent *event) override
|
||||||
{
|
{
|
||||||
QQuickPointerHandler::handlePointerEventImpl(event);
|
QQuickPointerHandler::handlePointerEventImpl(event);
|
||||||
if (!enabled())
|
if (!enabled())
|
||||||
return;
|
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;
|
qCDebug(lcPointerTests) << item->objectName() << event;
|
||||||
int c = event->pointCount();
|
int c = event->pointCount();
|
||||||
for (int i = 0; i < c; ++i) {
|
for (int i = 0; i < c; ++i) {
|
||||||
|
@ -206,10 +212,13 @@ class EventHandler : public QQuickPointerHandler
|
||||||
|
|
||||||
void onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabTransition stateChange, QQuickEventPoint *point) override
|
void onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabTransition stateChange, QQuickEventPoint *point) override
|
||||||
{
|
{
|
||||||
EventItem *item = static_cast<EventItem *>(target());
|
EventItem *item = qmlobject_cast<EventItem *>(target());
|
||||||
item->eventList.append(Event(Event::HandlerDestination, QEvent::None,
|
if (item)
|
||||||
static_cast<Qt::TouchPointState>(point->state()), stateChange, eventPos(point), point->scenePosition()));
|
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
|
class tst_PointerHandlers : public QQmlDataTest
|
||||||
|
@ -228,6 +237,8 @@ private slots:
|
||||||
void mouseEventDelivery();
|
void mouseEventDelivery();
|
||||||
void touchReleaseOutside_data();
|
void touchReleaseOutside_data();
|
||||||
void touchReleaseOutside();
|
void touchReleaseOutside();
|
||||||
|
void dynamicCreation();
|
||||||
|
void dynamicCreationInWindow();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject *, QEvent *event)
|
bool eventFilter(QObject *, QEvent *event)
|
||||||
|
@ -593,6 +604,52 @@ void tst_PointerHandlers::touchReleaseOutside()
|
||||||
QCOMPARE_EVENT(endIndexToTest, endDestination, endType, endState, endGrabState);
|
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)
|
QTEST_MAIN(tst_PointerHandlers)
|
||||||
|
|
||||||
#include "tst_qquickpointerhandler.moc"
|
#include "tst_qquickpointerhandler.moc"
|
||||||
|
|
Loading…
Reference in New Issue