From 8120ec1d3dd356194203a723e53a8b72e793d1e0 Mon Sep 17 00:00:00 2001 From: Sami Shalayel Date: Mon, 22 Aug 2022 11:13:03 +0200 Subject: [PATCH] qmltc: test alias on properties with attributes Add tests to qmltc to see if it behaves well for aliases to different kinds of properties, and compares if the engine shares the same behavior. Same for the attributes in the QMetaProperties. Changes: * add some more MOC information to aliases ** always add NOTIFY to aliases (like the engine does) ** always set DESIGNABLE to false for aliases (like the engine does) ** always set CONSTANT to false for aliases (like the engine does) ** always set STORED to false for aliases (like the engine does) Test if: * default aliases works when compiled via qmltc * attributes of aliases are set correctly in QMetaProperty and compare it to the attributes of the QMetaProperty obtained from the engine. * aliases can read/written/reset/notified Fixes: QTBUG-105708 Change-Id: I66b9c43c8c8de3dbd2b33d5ce15cd42ffb377ce7 Reviewed-by: Ulf Hermann --- .../auto/qml/qmltc/QmltcTests/CMakeLists.txt | 6 +- .../QmltcTests/DefaultPropertyAliasChild.qml | 8 + .../cpptypes/typewithmanyproperties.h | 90 ++++++++ .../qml/qmltc/QmltcTests/defaultAlias.qml | 7 +- .../QmltcTests/propertyAliasAttributes.qml | 37 +++ tests/auto/qml/qmltc/tst_qmltc.cpp | 215 +++++++++++++++++- tests/auto/qml/qmltc/tst_qmltc.h | 1 + tools/qmltc/qmltccompiler.cpp | 21 +- tools/qmltc/qmltcvisitor.cpp | 6 +- 9 files changed, 380 insertions(+), 11 deletions(-) create mode 100644 tests/auto/qml/qmltc/QmltcTests/DefaultPropertyAliasChild.qml create mode 100644 tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h create mode 100644 tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml diff --git a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt index 7a536c92be..9785ffa0c6 100644 --- a/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt +++ b/tests/auto/qml/qmltc/QmltcTests/CMakeLists.txt @@ -17,6 +17,8 @@ set(cpp_sources cpptypes/extensiontypes.h cpptypes/extensiontypes.cpp cpptypes/typewithspecialproperties.h + + cpptypes/typewithmanyproperties.h ) set(qml_sources @@ -50,6 +52,7 @@ set(qml_sources javaScriptFunctions.qml changingBindings.qml propertyAlias.qml + propertyAliasAttributes.qml propertyAlias_external.qml propertyChangeHandler.qml NestedHelloWorld.qml @@ -58,7 +61,7 @@ set(qml_sources listPropertySameName.qml defaultProperty.qml defaultPropertyCorrectSelection.qml - # defaultAlias.qml + defaultAlias.qml propertyReturningFunction.qml AttachedProperty.qml attachedPropertyDerived.qml @@ -99,6 +102,7 @@ set(qml_sources # support types: DefaultPropertySingleChild.qml + DefaultPropertyAliasChild.qml DefaultPropertyManyChildren.qml LocallyImported.qml LocalWithOnCompleted.qml diff --git a/tests/auto/qml/qmltc/QmltcTests/DefaultPropertyAliasChild.qml b/tests/auto/qml/qmltc/QmltcTests/DefaultPropertyAliasChild.qml new file mode 100644 index 0000000000..3672a1385b --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/DefaultPropertyAliasChild.qml @@ -0,0 +1,8 @@ +import QtQml 2.0 + +QtObject { + id: self + + property QtObject someObject + default property alias child: self.someObject +} diff --git a/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h new file mode 100644 index 0000000000..873c38771e --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/cpptypes/typewithmanyproperties.h @@ -0,0 +1,90 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef PROPERTYALIASATTRIBUTES_H +#define PROPERTYALIASATTRIBUTES_H + +#include +#include +#include + +using namespace Qt::Literals; + +class TypeWithManyProperties : public QObject +{ + Q_OBJECT + QML_ELEMENT + + const QString m_readOnly = u"Hello World!"_s; + QString m_readAndWrite; + QString m_resettable; + QString m_unresettable; + QString m_hasAllAttributes; + QString m_hasAllAttributes2; + +public: + TypeWithManyProperties() + { + hasAllAttributesBindable().setBinding( + [&] { return u"From the bindable: "_s + readOnly(); }); + } + + Q_PROPERTY(QString readOnly READ readOnly); + Q_PROPERTY(QString readAndWrite READ readAndWrite WRITE setReadAndWrite); + Q_PROPERTY(QString readAndWriteMember MEMBER m_readAndWrite); + Q_PROPERTY(QString resettable READ resettable WRITE setReadAndWrite RESET resetResettable); + Q_PROPERTY(QString unresettable READ unresettable WRITE setUnresettable); + Q_PROPERTY(QString notifiable READ readAndWrite WRITE setReadAndWriteAndNotify NOTIFY + notifiableChanged); + Q_PROPERTY(QString notifiableMember MEMBER m_readAndWrite NOTIFY notifiableChanged); + Q_PROPERTY(QString latestReadAndWrite MEMBER m_readAndWrite REVISION(1, 0)); + Q_PROPERTY(QString notExisting MEMBER m_readAndWrite REVISION(6, 0)); + + Q_PROPERTY(QString hasAllAttributes READ hasAllAttributes WRITE setHasAllAttributes RESET + resetHasAllAttributes NOTIFY hasAllAttributesChanged REVISION(1, 0) + BINDABLE hasAllAttributesBindable + DESIGNABLE false SCRIPTABLE true STORED false USER true FINAL + REQUIRED); + + Q_OBJECT_BINDABLE_PROPERTY(TypeWithManyProperties, QString, hasAllAttributesProperty); + + QBindable hasAllAttributesBindable() + { + return QBindable(&hasAllAttributesProperty); + } + + Q_PROPERTY(QString hasAllAttributes2 READ hasAllAttributes2 + DESIGNABLE true SCRIPTABLE true STORED true USER false CONSTANT); + + QString readOnly() { return m_readOnly; } + + QString readAndWrite() { return m_readAndWrite; } + + void setReadAndWrite(const QString &s) { m_readAndWrite = s; } + + void setReadAndWriteAndNotify(const QString &s) + { + if (s != readAndWrite()) { + setReadAndWrite(s); + emit notifiableChanged(s); + } + } + + void resetResettable() { m_resettable = u"Reset!"_s; } + + QString resettable() { return m_resettable; } + QString unresettable() { return m_unresettable; } + void setResettable(const QString &s) { m_resettable = s; } + void setUnresettable(const QString &s) { m_unresettable = s; } + + QString hasAllAttributes2() { return u"Some Constant string"_s; } + QString hasAllAttributes() { return m_hasAllAttributes; } + void setHasAllAttributes(const QString &s) { m_hasAllAttributes = s; } + void resetHasAllAttributes() { m_hasAllAttributes = "This value has been reset."; } + +signals: + void notifiableChanged(const QString &newValue); + void hasAllAttributesChanged(const QString &newValue); +}; + +#endif // PROPERTYALIASATTRIBUTES_H diff --git a/tests/auto/qml/qmltc/QmltcTests/defaultAlias.qml b/tests/auto/qml/qmltc/QmltcTests/defaultAlias.qml index 48203c3063..1909ed72a1 100644 --- a/tests/auto/qml/qmltc/QmltcTests/defaultAlias.qml +++ b/tests/auto/qml/qmltc/QmltcTests/defaultAlias.qml @@ -1,9 +1,8 @@ -import QtQml 2.0 +import QtQml -QtObject { +DefaultPropertyAliasChild { + id: self property string hello: "Hello from parent" - property QtObject origin - default property alias child: origin QtObject { property string hello: "Hello from parent.child (alias)" diff --git a/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml b/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml new file mode 100644 index 0000000000..66cd948ac4 --- /dev/null +++ b/tests/auto/qml/qmltc/QmltcTests/propertyAliasAttributes.qml @@ -0,0 +1,37 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQml +import QmltcTests + +TypeWithManyProperties { + id: self + + property alias hasAllAttributesAlias: self.hasAllAttributes + //hasAllAttributes: "The string." + hasAllAttributesAlias: "The string." // when this is missing then qmltc does not emit any error... + property alias hasAllAttributes2Alias: self.hasAllAttributes2 + + property alias readOnlyAlias: self.readOnly + property alias readAndWriteMemberAlias: self.readAndWriteMember + property alias resettableAlias: self.resettable + property alias unresettableAlias: self.unresettable + property alias notifiableAlias: self.notifiable + property alias notifiableMemberAlias: self.notifiableMember + property alias latestReadAndWriteAlias: self.latestReadAndWrite + + // aliases cannot be readonly, they inherit it from the property pointed to + // readonly default property alias readOnlyAlias2: self.readAndWriteMember // not valid + + default property alias defaultAlias: self.readAndWriteMember + + // cannot be compiled by qmlcachegen it seems: required property cannot have initializer + // required property alias requiredAlias: self.hasAllAttributes + + function assignUndefinedToResettableAlias() { + resettableAlias = undefined + } + function assignUndefinedToUnresettableAlias() { + unresettableAlias = undefined + } +} diff --git a/tests/auto/qml/qmltc/tst_qmltc.cpp b/tests/auto/qml/qmltc/tst_qmltc.cpp index 85cc82a1b1..c0bfb0436d 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.cpp +++ b/tests/auto/qml/qmltc/tst_qmltc.cpp @@ -33,6 +33,7 @@ #include "changingbindings.h" #include "propertyalias.h" #include "propertyalias_external.h" +#include "propertyaliasattributes.h" #include "complexaliases.h" #include "propertychangehandler.h" #include "nestedhelloworld.h" @@ -72,6 +73,7 @@ #include "valuetypelistproperty.h" #include "translations.h" #include "translationsbyid.h" +#include "defaultalias.h" #include "testprivateproperty.h" @@ -894,8 +896,19 @@ void tst_qmltc::specialProperties() // alias attributes: const QMetaObject *mo = created.metaObject(); QMetaProperty xxAlias = mo->property(mo->indexOfProperty("xxAlias")); + + QQmlComponent c(&e); + c.loadUrl(QUrl("qrc:/qt/qml/QmltcTests/specialProperties.qml")); + QScopedPointer _fromEngine(c.create()); + QVERIFY2(_fromEngine, qPrintable(c.errorString())); + QObject &fromEngine = *_fromEngine; + const QMetaObject *fromEngineMetaObject = fromEngine.metaObject(); + + QMetaProperty xxAliasFromEngine = + fromEngineMetaObject->property(mo->indexOfProperty("xxAlias")); QVERIFY(xxAlias.isValid()); - QVERIFY(xxAlias.isConstant()); + QVERIFY(xxAliasFromEngine.isValid()); + QCOMPARE(xxAlias.isConstant(), xxAliasFromEngine.isConstant()); QCOMPARE(created.xyAlias(), u"reset"); } @@ -1091,6 +1104,192 @@ void tst_qmltc::propertyAlias_external() QCOMPARE(heightAliasChangedSpy.count(), 1); } +void tst_qmltc::propertyAliasAttribute() +{ + QQmlEngine e; + PREPEND_NAMESPACE(propertyAliasAttributes) fromQmltc(&e); + + QQmlComponent c(&e); + c.loadUrl(QUrl("qrc:/qt/qml/QmltcTests/propertyAliasAttributes.qml")); + QScopedPointer _fromEngine(c.create()); + QVERIFY2(_fromEngine, qPrintable(c.errorString())); + QObject &fromEngine = *_fromEngine; + const QMetaObject *fromEngineMetaObject = fromEngine.metaObject(); + + const QString stringA = u"The quick brown fox"_s; + const QString stringB = u"jumps over the lazy dog."_s; + + QCOMPARE(fromQmltc.readOnlyAlias(), u"Hello World!"_s); + QCOMPARE(fromEngine.property("readOnlyAlias"), u"Hello World!"_s); + + QVERIFY(!fromQmltc.setProperty("readOnlyAlias", u"Some string"_s)); + QVERIFY(!fromEngine.setProperty("readOnlyAlias", u"Some string"_s)); + + // reading and writing from alias is already covered in the alias test + // check if it works on properties with the MEMBERS attribute + fromQmltc.setReadAndWriteMemberAlias(stringA); + fromEngine.setProperty("readAndWriteMemberAlias", stringA); + + QCOMPARE(fromQmltc.property("readAndWriteMember"), stringA); + QCOMPARE(fromEngine.property("readAndWriteMember"), stringA); + + fromQmltc.setReadAndWriteMemberAlias(stringB); + fromEngine.setProperty("readAndWriteMemberAlias", stringB); + + QCOMPARE(fromQmltc.readAndWriteMemberAlias(), stringB); + QCOMPARE(fromQmltc.property("readAndWriteMember"), stringB); + + QCOMPARE(fromEngine.property("readAndWriteMemberAlias"), stringB); + QCOMPARE(fromEngine.property("readAndWriteMember"), stringB); + + // check if alias can be reset through property + fromQmltc.setResettableAlias(stringA); + fromEngine.setProperty("resettableAlias", stringB); + fromQmltc.resetResettable(); + const int resettableIdx = fromEngineMetaObject->indexOfProperty("resettable"); + QVERIFY(fromEngineMetaObject->property(resettableIdx).reset(&fromEngine)); + QCOMPARE(fromQmltc.resettable(), u"Reset!"_s); + QCOMPARE(fromQmltc.resettableAlias(), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettable"), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettableAlias"), u"Reset!"_s); + + // check if property can be reset through alias + fromQmltc.setResettableAlias(stringA); + fromEngine.setProperty("resettableAlias", stringA); + fromQmltc.resetResettableAlias(); + QMetaMethod resetResettableAlias = fromEngineMetaObject->method( + fromEngineMetaObject->indexOfMethod("resetResettableAlias")); + resetResettableAlias.invoke(&fromEngine); + QCOMPARE(fromQmltc.resettable(), u"Reset!"_s); + QCOMPARE(fromQmltc.resettableAlias(), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettable"), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettableAlias"), u"Reset!"_s); + + // check if property can be reset by assigning undefined to alias + fromQmltc.setResettableAlias(stringA); + fromEngine.setProperty("resettableAlias", stringA); + fromQmltc.assignUndefinedToResettableAlias(); + QMetaMethod assignUndefinedToResettableAlias = fromEngineMetaObject->method( + fromEngineMetaObject->indexOfMethod("assignUndefinedToResettableAlias")); + assignUndefinedToResettableAlias.invoke(&fromEngine); + QCOMPARE(fromQmltc.resettableAlias(), u"Reset!"_s); + QCOMPARE(fromQmltc.resettable(), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettableAlias"), u"Reset!"_s); + QCOMPARE(fromEngine.property("resettable"), u"Reset!"_s); + + // check if property can be reset by assigning undefined to alias of + // non-resettable prop which should not happen: instead, nothing should happen + fromQmltc.setUnresettableAlias(stringA); + fromEngine.setProperty("unresettableAlias", stringA); + fromQmltc.assignUndefinedToUnresettableAlias(); + QMetaMethod assignUndefinedToUnresettableAlias = fromEngineMetaObject->method( + fromEngineMetaObject->indexOfMethod("assignUndefinedToUnresettableAlias")); + assignUndefinedToUnresettableAlias.invoke(&fromEngine); + QCOMPARE(fromQmltc.unresettableAlias(), stringA); + QCOMPARE(fromQmltc.property("unresettable"), stringA); + QCOMPARE(fromEngine.property("unresettableAlias"), stringA); + QCOMPARE(fromEngine.property("unresettable"), stringA); + + // check if notify arrives! + fromQmltc.setReadAndWrite(stringB); + fromEngine.setProperty("readAndWrite", stringB); + qsizetype calls = 0; + QSignalSpy spyQmltc(&fromQmltc, SIGNAL(notifiableChanged(QString))); + QSignalSpy spyEngine(&fromEngine, SIGNAL(notifiableChanged(QString))); + // write through alias + fromQmltc.setNotifiableAlias(stringA); + QVERIFY(fromEngine.setProperty("notifiableAlias", stringA)); + QCOMPARE(spyQmltc.count(), ++calls); + QCOMPARE(spyEngine.count(), calls); + // write through property + fromQmltc.setReadAndWriteAndNotify(stringB); + QVERIFY(fromEngine.setProperty("notifiable", stringB)); + QCOMPARE(spyQmltc.count(), ++calls); + QCOMPARE(spyEngine.count(), calls); + + fromQmltc.setNotifiableMemberAlias(stringA); + QVERIFY(fromEngine.setProperty("notifiableMemberAlias", stringA)); + QCOMPARE(spyQmltc.count(), ++calls); + QCOMPARE(spyEngine.count(), calls); + fromQmltc.setProperty("notifiableMember", stringB); + QVERIFY(fromEngine.setProperty("notifiableMember", stringB)); + QCOMPARE(spyQmltc.count(), ++calls); + QCOMPARE(spyEngine.count(), calls); + + // check that the alias to a revisioned property works + fromQmltc.setLatestReadAndWriteAlias(stringA); + QVERIFY(fromEngine.setProperty("latestReadAndWriteAlias", stringA)); + QCOMPARE(fromQmltc.latestReadAndWriteAlias(), stringA); + QCOMPARE(fromQmltc.property("latestReadAndWrite"), stringA); + QCOMPARE(fromEngine.property("latestReadAndWriteAlias"), stringA); + QCOMPARE(fromEngine.property("latestReadAndWrite"), stringA); + + QVERIFY(fromQmltc.setProperty("latestReadAndWrite", stringB)); + QVERIFY(fromEngine.setProperty("latestReadAndWrite", stringB)); + QCOMPARE(fromQmltc.latestReadAndWriteAlias(), stringB); + QCOMPARE(fromQmltc.property("latestReadAndWrite"), stringB); + QCOMPARE(fromEngine.property("latestReadAndWriteAlias"), stringB); + QCOMPARE(fromEngine.property("latestReadAndWrite"), stringB); + + // check if metaobject of alias is correct + const QVector metaObjects = { + fromQmltc.metaObject(), + fromEngine.metaObject(), + }; + + QVERIFY(metaObjects[0]); + QVERIFY(metaObjects[1]); + + QVector> metaProperties(2); + for (int j = 0; j < metaObjects.size(); j++) { + const QMetaObject *metaObject = metaObjects[j]; + for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) { + metaProperties[j][QString::fromLatin1(metaObject->property(i).name())] = + metaObject->property(i); + } + } + + { + QVERIFY(metaProperties[0].contains("hasAllAttributesAlias")); + QVERIFY(metaProperties[1].contains("hasAllAttributesAlias")); + QMetaProperty mpQmltc = metaProperties[0].value("hasAllAttributesAlias"); + QMetaProperty mpEngine = metaProperties[1].value("hasAllAttributesAlias"); + QCOMPARE(mpQmltc.isReadable(), mpEngine.isReadable()); + QCOMPARE(mpQmltc.isWritable(), mpEngine.isWritable()); + QCOMPARE(mpQmltc.isResettable(), mpEngine.isResettable()); + QCOMPARE(mpQmltc.hasNotifySignal(), mpEngine.hasNotifySignal()); + QCOMPARE(mpQmltc.revision(), mpEngine.revision()); + QCOMPARE(mpQmltc.isDesignable(), mpEngine.isDesignable()); + QCOMPARE(mpQmltc.isScriptable(), mpEngine.isScriptable()); + QCOMPARE(mpQmltc.isStored(), mpEngine.isStored()); + QCOMPARE(mpQmltc.isUser(), mpEngine.isUser()); + QCOMPARE(mpQmltc.isBindable(), mpEngine.isBindable()); + QCOMPARE(mpQmltc.isConstant(), mpEngine.isConstant()); + QCOMPARE(mpQmltc.isFinal(), mpEngine.isFinal()); + QCOMPARE(mpQmltc.isRequired(), mpEngine.isRequired()); + } + + { + QVERIFY(metaProperties[0].contains("hasAllAttributes2Alias")); + QVERIFY(metaProperties[1].contains("hasAllAttributes2Alias")); + QMetaProperty mpQmltc = metaProperties[0].value("hasAllAttributes2Alias"); + QMetaProperty mpEngine = metaProperties[1].value("hasAllAttributes2Alias"); + QCOMPARE(mpQmltc.isReadable(), mpEngine.isReadable()); + QCOMPARE(mpQmltc.isWritable(), mpEngine.isWritable()); + QCOMPARE(mpQmltc.isResettable(), mpEngine.isResettable()); + QCOMPARE(mpQmltc.hasNotifySignal(), mpEngine.hasNotifySignal()); + QCOMPARE(mpQmltc.revision(), mpEngine.revision()); + QCOMPARE(mpQmltc.isDesignable(), mpEngine.isDesignable()); + QCOMPARE(mpQmltc.isScriptable(), mpEngine.isScriptable()); + QCOMPARE(mpQmltc.isStored(), mpEngine.isStored()); + QCOMPARE(mpQmltc.isUser(), mpEngine.isUser()); + QCOMPARE(mpQmltc.isBindable(), mpEngine.isBindable()); + QCOMPARE(mpQmltc.isConstant(), mpEngine.isConstant()); + QCOMPARE(mpQmltc.isFinal(), mpEngine.isFinal()); + QCOMPARE(mpQmltc.isRequired(), mpEngine.isRequired()); + } +} + // TODO: we need to support RESET in aliases as well? (does it make sense?) void tst_qmltc::complexAliases() { @@ -1455,7 +1654,19 @@ void tst_qmltc::defaultPropertyCorrectSelection() void tst_qmltc::defaultAlias() { - QSKIP("Not implemented - not supported"); + QQmlEngine e; + PREPEND_NAMESPACE(defaultAlias) created(&e); + + QQmlComponent c(&e); + c.loadUrl(QUrl("qrc:/qt/qml/QmltcTests/defaultAlias.qml")); + QScopedPointer fromEngine(c.create()); + QVERIFY2(fromEngine, qPrintable(c.errorString())); + + auto *child = static_cast(created.child()); + QVERIFY(fromEngine->property("child").canConvert()); + QObject *childFromEngine = fromEngine->property("child").value(); + QVERIFY(childFromEngine); + QCOMPARE(child->hello(), childFromEngine->property("hello")); } void tst_qmltc::attachedProperty() diff --git a/tests/auto/qml/qmltc/tst_qmltc.h b/tests/auto/qml/qmltc/tst_qmltc.h index f2c68d2e8d..0f862170c4 100644 --- a/tests/auto/qml/qmltc/tst_qmltc.h +++ b/tests/auto/qml/qmltc/tst_qmltc.h @@ -45,6 +45,7 @@ private slots: void changingBindings(); void propertyAlias(); void propertyAlias_external(); + void propertyAliasAttribute(); void complexAliases(); void propertyChangeHandler(); void nestedHelloWorld(); diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp index ee932a84cd..57e8363bae 100644 --- a/tools/qmltc/qmltccompiler.cpp +++ b/tools/qmltc/qmltccompiler.cpp @@ -733,10 +733,20 @@ void QmltcCompiler::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &a current.functions.emplaceBack(bindable); mocLines << u"BINDABLE"_s << bindable.name; } + // 3. add notify - which is pretty special - if (QString notifyName = result.property.notify(); !notifyName.isEmpty()) { + // step 1: generate the moc instructions + // mimic the engines behavior: do it even if the notify will never be emitted + if (const QString aliasNotifyName = alias.notify(); !aliasNotifyName.isEmpty()) { + Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise + mocLines << u"NOTIFY"_s << aliasNotifyName; + } + + // step 2: connect the notifier to the aliased property notifier, if this latter exists + // otherwise, mimic the engines behavior and generate a useless notify + if (const QString notifyName = result.property.notify(); !notifyName.isEmpty()) { auto notifyFrames = frames; notifyFrames.pop(); // we don't need the last frame at all in this case @@ -762,6 +772,7 @@ void QmltcCompiler::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &a current.endInit.body += notifyEpilogue; current.endInit.body << u"}"_s; } + if (QString resetName = result.property.reset(); !resetName.isEmpty()) { Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise QmltcMethod reset {}; @@ -775,8 +786,12 @@ void QmltcCompiler::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &a mocLines << u"RESET"_s << reset.name; } - if (result.property.isConstant()) - mocLines << u"CONSTANT"_s; + // mimic the engines behavior: aliases are never constants + // mocLines << u"CONSTANT"_s; + // mimic the engines behavior: aliases are never stored + mocLines << u"STORED"_s << u"false"_s; + // mimic the engines behavior: aliases are never designable + mocLines << u"DESIGNABLE"_s << u"false"_s; // 4. add moc entry // Q_PROPERTY(QString text READ text WRITE setText BINDABLE bindableText NOTIFY textChanged) diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp index aeea6371ab..017b5d5440 100644 --- a/tools/qmltc/qmltcvisitor.cpp +++ b/tools/qmltc/qmltcvisitor.cpp @@ -562,8 +562,12 @@ static void setAliasData(QQmlJSMetaProperty *alias, const QQmlJSUtils::ResolvedA return; if (origin.property.isWritable() && alias->write().isEmpty()) alias->setWrite(compiledData.write); - if (!origin.property.notify().isEmpty() && alias->notify().isEmpty()) + + // the engine always compiles a notify for properties/aliases defined in qml code + // Yes, this generated notify will never be emitted. + if (alias->notify().isEmpty()) alias->setNotify(compiledData.notify); + if (!origin.property.bindable().isEmpty() && alias->bindable().isEmpty()) alias->setBindable(compiledData.bindable); }