From 00308626cecd1dc1e780e389e2ac97a9a4a3f48a Mon Sep 17 00:00:00 2001 From: Skycoder42 Date: Wed, 25 Jul 2018 19:36:43 +0200 Subject: [PATCH] completed qml binding impl, with tests --- .../qmlsettingsgenerator/generatortest.xml | 6 +- .../qmlsettingsgenerator.pro | 6 +- .../qml/qmlsettingsgenerator/testbackend.cpp | 38 +++++++ .../qml/qmlsettingsgenerator/testbackend.h | 30 ++++++ .../tst_qmlsettingsgenerator.qml | 56 ++++++++++ .../tst_qmlsettingsgenerator.qmlc | Bin 892 -> 3816 bytes .../qmlsettingsgenerator.cpp | 98 ++++++++++++++---- .../settingsgenerator/qmlsettingsgenerator.h | 9 +- 8 files changed, 219 insertions(+), 24 deletions(-) create mode 100644 tests/auto/qml/qmlsettingsgenerator/testbackend.cpp create mode 100644 tests/auto/qml/qmlsettingsgenerator/testbackend.h diff --git a/tests/auto/qml/qmlsettingsgenerator/generatortest.xml b/tests/auto/qml/qmlsettingsgenerator/generatortest.xml index 49176af..42ee0fe 100644 --- a/tests/auto/qml/qmlsettingsgenerator/generatortest.xml +++ b/tests/auto/qml/qmlsettingsgenerator/generatortest.xml @@ -1,7 +1,11 @@ - + QtCore/QDateTime QtCore/QUrl + testbackend.h + + +#include + +class TestBackend : public QtMvvm::ISettingsAccessor +{ + Q_OBJECT + +public: + explicit TestBackend(QObject *parent = nullptr); + + // ISettingsAccessor interface +public: + bool contains(const QString &key) const override; + QVariant load(const QString &key, const QVariant &defaultValue) const override; + void save(const QString &key, const QVariant &value) override; + void remove(const QString &key) override; + +public slots: + void sync() override; + +public: + QVariantHash _data; +}; + +#define SOME_EXPORT + +#endif // TESTBACKEND_H diff --git a/tests/auto/qml/qmlsettingsgenerator/tst_qmlsettingsgenerator.qml b/tests/auto/qml/qmlsettingsgenerator/tst_qmlsettingsgenerator.qml index 9a14de0..ba1e524 100644 --- a/tests/auto/qml/qmlsettingsgenerator/tst_qmlsettingsgenerator.qml +++ b/tests/auto/qml/qmlsettingsgenerator/tst_qmlsettingsgenerator.qml @@ -1,11 +1,67 @@ import QtQuick 2.10 import QtTest 1.1 +import de.skycoder42.QtMvvm.Core.test 4.2 Item { id: root + Component { + id: listElement + TestSettings_ListElement_8 {} + } + TestCase { name: "QmlSettings" + + function test_0_instance() { + verify(TestSettings); + verify(TestSettings.accessor); + } + + function test_1_defaults() { + compare(TestSettings.emptyEntry, false); + compare(TestSettings.advancedEntry, "Hello World"); + compare(TestSettings.codeEntry, "file:///path/to/something"); + compare(TestSettings.parentNode.fullChildNode.replaceEntry, 42); + compare(TestSettings.parentNode.parentEntry, true); + compare(TestSettings.parentNode.parentEntryGroup.nodeWithCodeEntry, 43); + compare(TestSettings.parentNode.parentEntryGroup.leafEntry, qsTr("translate me")); + compare(TestSettings.variantEntry, undefined); + compare(TestSettings.simpleListEntry, [42]); + compare(TestSettings.listEntryGroup.dummyChild, false); + } + + function test_2_listEntry() { + // test reading + compare(TestSettings.listEntry.length, 3); + verify(TestSettings.listEntry[0]); + compare(TestSettings.listEntry[0].value, "test1"); + verify(TestSettings.listEntry[1]); + compare(TestSettings.listEntry[1].value, "test2"); + verify(TestSettings.listEntry[2]); + compare(TestSettings.listEntry[2].value, "test3"); + verify(!TestSettings.listEntry[3]); + + // test appending + var elem = listElement.createObject(root, {value: "baum42"}); + verify(elem); + compare(elem.value, "baum42"); + TestSettings.listEntry.push(elem); + compare(TestSettings.listEntry.length, 4); + verify(TestSettings.listEntry[3]); + compare(TestSettings.listEntry[3].value, "baum42"); + verify(!TestSettings.listEntry[4]); + + // test resetting + var elem2 = TestSettings.create_listEntry_element("newElem"); + verify(elem2); + compare(elem2.value, "newElem"); + TestSettings.listEntry = [elem2] + compare(TestSettings.listEntry.length, 1); + verify(TestSettings.listEntry[0]); + compare(TestSettings.listEntry[0].value, "newElem"); + verify(!TestSettings.listEntry[1]); + } } } diff --git a/tests/auto/qml/qmlsettingsgenerator/tst_qmlsettingsgenerator.qmlc b/tests/auto/qml/qmlsettingsgenerator/tst_qmlsettingsgenerator.qmlc index bfc79ddfbbce1211583cb8c918ba6fca327d04aa..6d97e5ffdbef38c33cd1d2129f4cb2192e2977e2 100644 GIT binary patch literal 3816 zcmb7`O>9(E6vyw(bUxm^mbTDBO9!Y(=}@Gd7OE|N&`1cHh(aL3LT#th89=7(&`v8P zE=*9vvI|2<+%z$MB*vtP3yCWhE?Ah@7#F%BE?lrO#1)B)`uo58-pp&M3Eb(w=bd}c zJ@O^y_bCANB_{8#F?#I1YF-+tBH_r?A5|8_;c`~3RK**`uRESN&) z+y5ky8Um|4#GrS9#?O{`R5Ggo6HJPKYm7mj1?^dm8`A)N0M+O~W*Iz+THgMTnLa&y z6!Y|G=Ru#B4$VtPZ5_T59sHTB<{1^x0UE_HPFvzp?G-QEQqUHY^H(1y-Z}ns3sih7 zklw9eJJ<$h;QX!{5=Fz3$K8k;1uWr?|}?h1iQe;U^lo0wD(KU4XhuTt!~mWslIJdIn(AQTie}$ z+YYy*#&u*mGdmHE1j`+nF4$FbU-HAT<3$+o>U?pdjHj)m6u zWqO#iHc@2VB>N_y%)JqgtNae)s`b537}veXDz5v0a@qqlw;#L!dcjGc8haNU0MkIV zdIP9NKLh>XdoTcg06Fjz&>i>%=nmZnhr!?A2x!2UVQ4#a1iBA83e7@~LWiL*LSKcx z1U&FJX)WLO99C*Q=*b_;*PLD~t>DEJTV`F2J3~h4V-shZX+KppW zyek{Wm_s22PH(>xiPv0-ht1sJODb?Cyg!|4s2RWoby{<7x_7{dq}>DuCc3f-J7**0 zI*p0+u}FH)elI>=^b(;XH=Gz8h|YS16+PeMq#+$1AVw`t_dG(!(Y$z#Kql(Xdp(YM z)uLo#%s--EE+t^7{nJ2eehc;b2kg4u@_)Sa>28QFfjGJorkf`XYp;E#e={%CM-p4= zqlq;k7!e+P6Y(kXRCZ~v?9!+Jo$J)@ZL$_Ct*4n6G}OQCP#0ZW({rHbX=tF&@wM5m zytXEP13mBfYnxHoAIyKq^TxO+-pajw3wjU6g>1o(EnX(<+-)3M26w?z(}i*($6bDY&VPrB;w*L`>$nWEQ`?fsZTYL+<-!sP9mrn+X$ z401Exirg<_nQS=0llBK|+NXC(VB=Xb!e9 z1Fuswbp9E*ve~HJTQ<#*z{hcFW!yVm*5 zdqrf9*lUpTn*Ezuru;2(!W{OL*du@Q*dvSP`6)jdlpZsRdKIY?UetAl>OwizO5V$* zr0sW79@`gd{!N8;*?7H%#xlQIdc=9}q%u3~P@aR@sh?)QiAvb23?Jpqte;MAT<3-LBdj-Ut2|Mj zHeWwUv}QTEA~Em#Z^pOjJXXkd-459p)~UG2_dHgI=apw=UDY)2*{W*muGOdR)m2u` z`T3N*a-+O!k8Vx4ZgkE9Gq(B^#UjtPKIhq^e9Mmd%?af{Etoy8bn&O0Me%C{h zxBdn9xUN%ms`GSX6&t-XWq%nRx`E<@`=#%*Ft6{lFt6`5`Cq_$MN<3TGLz>q6wz?g*M{7l%+1qjzJ!NJrReuZj#*6T> WS0_q-4oP*PDjnxZcl7&`^7B98fV^J- delta 359 zcmaDM`-e@cu*@VmC9xz?l7WGNk(-raT3Y$O6h;Py8s>>oM)f>BAA_yJG{2pj;D`04*ZzyVYT!c0JX07`!V(jWjb z3ToWs)vT(#KY;uKpa3(FW|{n#)z}dv8vs>R01{;oU|?lX0J1=U9i|(~Qvix6fY~sD zbuuHn`Q#I90$wl~Hn0@P1_%K8i-W-ch$nyrSbz@Y0b&&(HUVN6APxcI93ZX%;yFx{ fr*Rlf=HOJAJcHANF?TW}U*qImd`gp7@!0?Xt9>@u diff --git a/tools/settingsgenerator/qmlsettingsgenerator.cpp b/tools/settingsgenerator/qmlsettingsgenerator.cpp index d7548af..5d60e16 100644 --- a/tools/settingsgenerator/qmlsettingsgenerator.cpp +++ b/tools/settingsgenerator/qmlsettingsgenerator.cpp @@ -14,26 +14,27 @@ void QmlSettingsGenerator::process(const QString &inPath) auto settings = readDocument(inPath); if(!settings.name) settings.name = QFileInfo{inPath}.baseName(); - _cppName = settings.name.value(); - settings.name.value().prepend(QStringLiteral("QMLTYPE_")); fixTrContext(settings, QFileInfo{inPath}.fileName()); - _name = settings.name.value(); + _cppName = settings.name.value(); + _name = QStringLiteral("QMLTYPE_") + _cppName; _prefixName = settings.prefix ? settings.prefix.value() + QLatin1Char(' ') + _name : _name; _typeMappings = settings.typeMappings; if(!_hdrFile.open(QIODevice::WriteOnly | QIODevice::Text)) throw FileException{_hdrFile}; - writeHeader(settings, QFileInfo{inPath}.completeBaseName() + QStringLiteral(".h")); + _listTypes.clear(); + auto typeNum = writeHeader(settings, QFileInfo{inPath}.completeBaseName() + QStringLiteral(".h")); _hdrFile.close(); if(!_srcFile.open(QIODevice::WriteOnly | QIODevice::Text)) throw FileException{_srcFile}; - writeSource(settings); + writeSource(settings, typeNum); + _listTypes.clear(); _srcFile.close(); } -void QmlSettingsGenerator::writeHeader(const SettingsType &settings, const QString &inHdrPath) +int QmlSettingsGenerator::writeHeader(const SettingsType &settings, const QString &inHdrPath) { QString incGuard = QFileInfo{_hdrFile.fileName()} .completeBaseName() @@ -67,7 +68,7 @@ void QmlSettingsGenerator::writeHeader(const SettingsType &settings, const QStri std::tie(offset, childOffsets) = writeNodeContentClasses(settings, keyList); // create the class - _hdr << "class " << _name << " : public QObject\n" + _hdr << "class " << (settings.prefix ? settings.prefix.value() + QLatin1Char(' ') : QString{}) << _name << " : public QObject\n" << "{\n" << "\tQ_OBJECT\n\n" << "\tQ_PROPERTY(QtMvvm::ISettingsAccessor *accessor READ accessor CONSTANT FINAL)\n\n"; @@ -90,11 +91,13 @@ void QmlSettingsGenerator::writeHeader(const SettingsType &settings, const QStri << "\t{}\n\n" << "\tQtMvvm::ISettingsAccessor *accessor() const { return _settings->accessor(); }\n" << "\t" << _cppName << " *settings() const { return _settings; }\n\n" - << "\t" << (settings.prefix ? settings.prefix.value() + QLatin1Char(' ') : QString{}) << "static void registerQmlTypes(const char *uri, int major, int minor);\n"; + << "\tstatic void registerQmlTypes(const char *uri, int major, int minor);\n"; if(settings.qml) - _hdr << "\t" << (settings.prefix ? settings.prefix.value() + QLatin1Char(' ') : QString{}) << "static void registerQmlTypes();\n"; + _hdr << "\tstatic void registerQmlTypes();\n"; _hdr << "};\n\n" << "#endif //" << incGuard << '\n'; + + return offset; } void QmlSettingsGenerator::writeListTypeBaseClass() @@ -143,7 +146,9 @@ void QmlSettingsGenerator::writeListTypeBaseClass() << "\t\telement->_entry = &data->entry;\n" << "\t\tdata->elements.append(element);\n" << "\t\tif(copyDefault)\n" - << "\t\t\telement->setValue(T{std::move(element->_buffer)});\n" + << "\t\t\tdata->entry.push(T{std::move(element->_buffer)});\n" + << "\t\telse\n" + << "\t\t\tdata->entry.push(T{});\n" << "\t\tdata->entry.addChangeCallback(element, [maxIndex, element](int i, const T &d) {\n" << "\t\t\tif(i == maxIndex)\n" << "\t\t\t\temit element->valueChanged(d);\n" @@ -248,6 +253,11 @@ int QmlSettingsGenerator::writeListEntryElementClass(const ListEntryType &entry, << "\texplicit " << _name << "_" << offset << "(QObject *parent = nullptr) : \n" << "\t\t" << _name << "_ListType{parent}\n" << "\t{}\n\n" + << "\texplicit " << _name << "_" << offset << "(const " << mType << " &value, QObject *parent = nullptr) : \n" + << "\t\t" << _name << "_" << offset << "{parent}\n" + << "\t{\n" + << "\t\tsetValue(value);\n" + << "\t}\n\n" << "Q_SIGNALS:\n" << "\tvoid valueChanged(const " << mType << " &value);\n" << "};\n\n"; @@ -266,6 +276,7 @@ void QmlSettingsGenerator::writeProperties(const NodeContentGroup &node, const Q auto lIndex = childOffsets.takeFirst(); //done seperately because of undefine param call order writeListEntryProperty(nonstd::get(cNode), keyList, lIndex, childOffsets.takeFirst(), childConstructs); listEntries.append(lIndex); + _listTypes.insert(lIndex); } else if(nonstd::holds_alternative(cNode)) writeProperties(nonstd::get(cNode), keyList, childOffsets, listEntries, childConstructs); else @@ -317,7 +328,12 @@ void QmlSettingsGenerator::writeListEntryProperty(const ListEntryType &entry, QS << "\t\t\t&" << _name << "_" << listIndex << "::at<" << _name << "_" << listIndex << ">,\n" << "\t\t\t&" << _name << "_" << listIndex << "::clear<" << _name << "_" << listIndex << ">\n" << "\t\t};\n" - << "\t}\n\n"; + << "\t}\n" + << "public:\n" + << "\tQ_INVOKABLE " << _name << "_" << listIndex << " *create_" << entry.key << "_element(const " << _typeMappings.value(entry.type, entry.type) << " &value) {\n" + << "\t\treturn new " << _name << "_" << listIndex << "{value, this};\n" + << "\t}\n" + << "private:\n\n"; childConstructs.append({entry.key, -1}); @@ -370,27 +386,71 @@ void QmlSettingsGenerator::writeListEntryPropertySignalConnect(const SettingsGen _hdr << "\t\t_settings->" << keyList.join(QLatin1Char('.')) << ".addSizeChangeCallback(this, std::bind(&" << _name << "_" << classIndex << "::adjust<" << _name << "_" << classIndex - << ">, &_" << entry.key << ", this, std::placeholders::_1));\n"; + << ">, &_" << entry.key << ", this, std::placeholders::_1));\n" + << "\t\t"<< _name << "_" << classIndex << "::adjust<" << _name << "_" << classIndex + << ">(&_" << entry.key << ", this, _settings->" << keyList.join(QLatin1Char('.')) << ".size());\n"; } -void QmlSettingsGenerator::writeSource(const SettingsType &settings) +void QmlSettingsGenerator::writeSource(const SettingsType &settings, int typeNum) { - _src << "#include \"" << _hdrFile.fileName() << "\"\n\n"; + _src << "#include \"" << _hdrFile.fileName() << "\"\n" + << "#include \n" + << "#include \n\n"; - writeQmlRegistration(settings); + writeQmlRegistration(settings.qml ? settings.qml.value().type : Singleton, typeNum); if(settings.qml) { const auto &qml = settings.qml.value(); - _src << "\nvoid " << _name << "::registerQmlTypes()\n" + _src << "void " << _name << "::registerQmlTypes()\n" << "{\n" << "\tregisterQmlTypes(\"" << qml.uri << "\", " << qml.major << ", " << qml.minor << ");\n" << "}\n"; + + if(qml.autoRegister) { + _src << "\nnamespace {\n\n" + << "void __register_qml_types()\n" + << "{\n" + << "\t" << _name << "::registerQmlTypes();\n" + << "}\n\n" + << "}\n" + << "Q_COREAPP_STARTUP_FUNCTION(__register_qml_types)\n"; + } } } -void QmlSettingsGenerator::writeQmlRegistration(const SettingsType &settings) +void QmlSettingsGenerator::writeQmlRegistration(QmlRegistrationMode mode, int typeNum) { - _src << "\nvoid " << _name << "::registerQmlTypes(const char *uri, int major, int minor)\n" + if(mode == Singleton) { + _src << "namespace {\n\n" + << "QObject *__create_qml_singleton(QQmlEngine *qmlEngine, QJSEngine *)\n" + << "{\n" + << "\treturn new " << _name << "{qmlEngine};\n" + << "}\n\n" + << "}\n\n"; + } + + _src << "void " << _name << "::registerQmlTypes(const char *uri, int major, int minor)\n" << "{\n" - << "}\n"; + << "\tconst QString msg{QStringLiteral(\"Settings-Helpertypes cannot be created\")};\n\n"; + for(auto i = 0; i < typeNum; i++) { + if(_listTypes.contains(i)) + _src << "\tqmlRegisterType<" << _name << "_" << i << ">(uri, major, minor, \"" << _cppName << "_ListElement_" << i << "\");\n"; + else + _src << "\tqmlRegisterUncreatableType<" << _name << "_" << i << ">(uri, major, minor, \"" << _name << "_" << i << "\", msg);\n"; + } + switch(mode) { + case Singleton: + _src << "\n\tqmlRegisterSingletonType<" << _name << ">(uri, major, minor, \"" << _cppName << "\", __create_qml_singleton);\n"; + break; + case Uncreatable: + _src << "\n\tqmlRegisterUncreatableType<" << _name << ">(uri, major, minor, \"" << _cppName << "\", msg);\n"; + break; + case Creatable: + _src << "\n\tqmlRegisterType<" << _name << ">(uri, major, minor, \"" << _cppName << "\");\n"; + break; + default: + Q_UNREACHABLE(); + break; + } + _src << "}\n\n"; } diff --git a/tools/settingsgenerator/qmlsettingsgenerator.h b/tools/settingsgenerator/qmlsettingsgenerator.h index cf4af0e..c2388dd 100644 --- a/tools/settingsgenerator/qmlsettingsgenerator.h +++ b/tools/settingsgenerator/qmlsettingsgenerator.h @@ -3,6 +3,7 @@ #include "settingsgeneratorimpl.h" #include +#include class QmlSettingsGenerator : public SettingsGeneratorImpl { @@ -24,7 +25,9 @@ private: QString _prefixName; QHash _typeMappings; - void writeHeader(const SettingsType &settings, const QString &inHdrPath); + QSet _listTypes; + + int writeHeader(const SettingsType &settings, const QString &inHdrPath); void writeListTypeBaseClass(); @@ -46,9 +49,9 @@ private: void writeEntryPropertySignalConnect(const EntryType &entry, QStringList keyList, int classIndex); void writeListEntryPropertySignalConnect(const ListEntryType &entry, QStringList keyList, QList &listEntries); - void writeSource(const SettingsType &settings); + void writeSource(const SettingsType &settings, int typeNum); - void writeQmlRegistration(const SettingsType &settings); + void writeQmlRegistration(QmlRegistrationMode mode, int typeNum); }; #endif // QMLSETTINGSGENERATOR_H