diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2019-10-01 14:26:44 +0200 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2019-10-21 14:38:32 +0200 |
commit | 1eed1b860534f481b89b411fc7e21a7daa22d5ee (patch) | |
tree | f5f0d2291c1dbbb4c5fd4a897fd08956c438075b | |
parent | 488a37c861a1724a935a8d7b925693a38918a71a (diff) |
Fix interface handling in bindings
If a type had a property with an interface type, we did not support the
case where the assigned binding value is convertible to the interface.
This is now fixed by adding a last new check to QQmlPrivate::write
Fixes: QTBUG-78721
Change-Id: I0b85fbfdf8561ba43610ac343001ae380287a674
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r-- | src/qml/qml/qqmlproperty.cpp | 12 | ||||
-rw-r--r-- | tests/auto/qml/qqmlproperty/data/interfaceBinding.qml | 27 | ||||
-rw-r--r-- | tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp | 79 |
3 files changed, 117 insertions, 1 deletions
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 5f57e0eca1..b6baa86bd7 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1373,8 +1373,9 @@ bool QQmlPropertyPrivate::write(QObject *object, } } if (!ok) { - // the only other option is that they are assigning a single value + // the only other options are that they are assigning a single value // to a sequence type property (eg, an int to a QList<int> property). + // or that we encountered an interface type // Note that we've already handled single-value assignment to QList<QUrl> properties. if (variantType == QVariant::Int && propertyType == qMetaTypeId<QList<int> >()) { QList<int> list; @@ -1405,6 +1406,15 @@ bool QQmlPropertyPrivate::write(QObject *object, } } + if (!ok && QQmlMetaType::isInterface(propertyType)) { + auto valueAsQObject = qvariant_cast<QObject *>(value); + if (valueAsQObject && valueAsQObject->qt_metacast(QQmlMetaType::interfaceIId(propertyType))) { + // this case can occur when object has an interface type + // and the variant contains a type implementing the interface + return property.writeProperty(object, const_cast<void *>(value.constData()), flags); + } + } + if (ok) { return property.writeProperty(object, const_cast<void *>(v.constData()), flags); } else { diff --git a/tests/auto/qml/qqmlproperty/data/interfaceBinding.qml b/tests/auto/qml/qqmlproperty/data/interfaceBinding.qml new file mode 100644 index 0000000000..4e72a75f42 --- /dev/null +++ b/tests/auto/qml/qqmlproperty/data/interfaceBinding.qml @@ -0,0 +1,27 @@ +import QtQuick 2.12 +import io.qt.bugreports 1.0 +Item { + InterfaceConsumer { + objectName: "a1" + i: A { + property int i: 42 + } + } + + InterfaceConsumer { + objectName: "a2" + property A a: A { + property int i: 43 + } + i: a + } + + InterfaceConsumer { + objectName: "a3" + property A a: A { + id : aa + property int i: 44 + } + i: aa + } +} diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp index bc407e97a2..7ebe0bac52 100644 --- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp +++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp @@ -40,6 +40,7 @@ #endif #include <QtCore/private/qobject_p.h> #include "../../shared/util.h" +#include "qobject.h" #include <QDebug> class MyQmlObject : public QObject @@ -148,6 +149,7 @@ private slots: void readOnlyDynamicProperties(); void aliasToIdWithMatchingQmlFileNameOnCaseInsensitiveFileSystem(); void nullPropertyBinding(); + void interfaceBinding(); void floatToStringPrecision_data(); void floatToStringPrecision(); @@ -2083,6 +2085,83 @@ void tst_qqmlproperty::nullPropertyBinding() QMetaObject::invokeMethod(root.get(), "tog"); } +struct Interface { +}; + +QT_BEGIN_NAMESPACE +#define MyInterface_iid "io.qt.bugreports.Interface" +Q_DECLARE_INTERFACE(Interface, MyInterface_iid); +QT_END_NAMESPACE + +class A : public QObject, Interface { + Q_OBJECT + Q_INTERFACES(Interface) +}; + +class B : public QObject, Interface { + Q_OBJECT + Q_INTERFACES(Interface) +}; + +class C : public QObject { + Q_OBJECT +}; + +class InterfaceConsumer : public QObject { + Q_OBJECT + Q_PROPERTY(Interface* i READ interface WRITE setInterface NOTIFY interfaceChanged) + Q_PROPERTY(int testValue READ testValue NOTIFY testValueChanged) + + +public: + + Interface* interface() const + { + return m_interface; + } + void setInterface(Interface* interface) + { + QObject* object = reinterpret_cast<QObject*>(interface); + m_testValue = object->property("i").toInt(); + emit testValueChanged(); + if (m_interface == interface) + return; + + m_interface = interface; + emit interfaceChanged(); + } + + int testValue() { + return m_testValue; + } + +signals: + void interfaceChanged(); + void testValueChanged(); + +private: + Interface* m_interface = nullptr; + int m_testValue = 0; +}; +void tst_qqmlproperty::interfaceBinding() +{ + + qmlRegisterInterface<Interface>("Interface"); + qmlRegisterType<A>("io.qt.bugreports", 1, 0, "A"); + qmlRegisterType<B>("io.qt.bugreports", 1, 0, "B"); + qmlRegisterType<C>("io.qt.bugreports", 1, 0, "C"); + qmlRegisterType<InterfaceConsumer>("io.qt.bugreports", 1, 0, "InterfaceConsumer"); + + const QUrl url = testFileUrl("interfaceBinding.qml"); + QQmlEngine engine; + QQmlComponent component(&engine, url); + QScopedPointer<QObject> root(component.create()); + QVERIFY(root); + QCOMPARE(root->findChild<QObject*>("a1")->property("testValue").toInt(), 42); + QCOMPARE(root->findChild<QObject*>("a2")->property("testValue").toInt(), 43); + QCOMPARE(root->findChild<QObject*>("a3")->property("testValue").toInt(), 44); +} + void tst_qqmlproperty::floatToStringPrecision_data() { QTest::addColumn<QString>("propertyName"); |