Browse Source

completed qml binding impl, with tests

pull/2/head
Skycoder42 7 years ago
parent
commit
00308626ce
No known key found for this signature in database GPG Key ID: 8E01AD9EF0578D2B
  1. 6
      tests/auto/qml/qmlsettingsgenerator/generatortest.xml
  2. 6
      tests/auto/qml/qmlsettingsgenerator/qmlsettingsgenerator.pro
  3. 38
      tests/auto/qml/qmlsettingsgenerator/testbackend.cpp
  4. 30
      tests/auto/qml/qmlsettingsgenerator/testbackend.h
  5. 56
      tests/auto/qml/qmlsettingsgenerator/tst_qmlsettingsgenerator.qml
  6. BIN
      tests/auto/qml/qmlsettingsgenerator/tst_qmlsettingsgenerator.qmlc
  7. 98
      tools/settingsgenerator/qmlsettingsgenerator.cpp
  8. 9
      tools/settingsgenerator/qmlsettingsgenerator.h

6
tests/auto/qml/qmlsettingsgenerator/generatortest.xml

@ -1,7 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<Settings name="TestSettings"> <Settings name="TestSettings"
prefix="SOME_EXPORT">
<Include>QtCore/QDateTime</Include> <Include>QtCore/QDateTime</Include>
<Include local="false">QtCore/QUrl</Include> <Include local="false">QtCore/QUrl</Include>
<Include local="true">testbackend.h</Include>
<Backend class="TestBackend"/>
<Qml uri="de.skycoder42.QtMvvm.Core.test" <Qml uri="de.skycoder42.QtMvvm.Core.test"
major="4" major="4"

6
tests/auto/qml/qmlsettingsgenerator/qmlsettingsgenerator.pro

@ -6,8 +6,12 @@ CONFIG -= app_bundle
TARGET = tst_qmlsettingsgenerator TARGET = tst_qmlsettingsgenerator
HEADERS += \
testbackend.h
SOURCES += \ SOURCES += \
tst_qmlsettingsgenerator.cpp tst_qmlsettingsgenerator.cpp \
testbackend.cpp
QML_SETTINGS_DEFINITIONS += \ QML_SETTINGS_DEFINITIONS += \
generatortest.xml generatortest.xml

38
tests/auto/qml/qmlsettingsgenerator/testbackend.cpp

@ -0,0 +1,38 @@
#include "testbackend.h"
TestBackend::TestBackend(QObject *parent) :
ISettingsAccessor{parent}
{}
bool TestBackend::contains(const QString &key) const
{
return _data.contains(key);
}
QVariant TestBackend::load(const QString &key, const QVariant &defaultValue) const
{
return _data.value(key, defaultValue);
}
void TestBackend::save(const QString &key, const QVariant &value)
{
_data.insert(key, value);
emit entryChanged(key, value);
}
void TestBackend::remove(const QString &key)
{
for(auto it = _data.begin(); it != _data.end();) {
if(it.key().startsWith(key + QLatin1Char('/'))) {
it = _data.erase(it);
emit entryRemoved(key);
} else
++it;
}
_data.remove(key);
emit entryRemoved(key);
}
void TestBackend::sync()
{
}

30
tests/auto/qml/qmlsettingsgenerator/testbackend.h

@ -0,0 +1,30 @@
#ifndef TESTBACKEND_H
#define TESTBACKEND_H
#include <QtCore/QVariantHash>
#include <QtMvvmCore/ISettingsAccessor>
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

56
tests/auto/qml/qmlsettingsgenerator/tst_qmlsettingsgenerator.qml

@ -1,11 +1,67 @@
import QtQuick 2.10 import QtQuick 2.10
import QtTest 1.1 import QtTest 1.1
import de.skycoder42.QtMvvm.Core.test 4.2
Item { Item {
id: root id: root
Component {
id: listElement
TestSettings_ListElement_8 {}
}
TestCase { TestCase {
name: "QmlSettings" 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]);
}
} }
} }

BIN
tests/auto/qml/qmlsettingsgenerator/tst_qmlsettingsgenerator.qmlc

Binary file not shown.

98
tools/settingsgenerator/qmlsettingsgenerator.cpp

@ -14,26 +14,27 @@ void QmlSettingsGenerator::process(const QString &inPath)
auto settings = readDocument(inPath); auto settings = readDocument(inPath);
if(!settings.name) if(!settings.name)
settings.name = QFileInfo{inPath}.baseName(); settings.name = QFileInfo{inPath}.baseName();
_cppName = settings.name.value();
settings.name.value().prepend(QStringLiteral("QMLTYPE_"));
fixTrContext(settings, QFileInfo{inPath}.fileName()); 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; _prefixName = settings.prefix ? settings.prefix.value() + QLatin1Char(' ') + _name : _name;
_typeMappings = settings.typeMappings; _typeMappings = settings.typeMappings;
if(!_hdrFile.open(QIODevice::WriteOnly | QIODevice::Text)) if(!_hdrFile.open(QIODevice::WriteOnly | QIODevice::Text))
throw FileException{_hdrFile}; throw FileException{_hdrFile};
writeHeader(settings, QFileInfo{inPath}.completeBaseName() + QStringLiteral(".h")); _listTypes.clear();
auto typeNum = writeHeader(settings, QFileInfo{inPath}.completeBaseName() + QStringLiteral(".h"));
_hdrFile.close(); _hdrFile.close();
if(!_srcFile.open(QIODevice::WriteOnly | QIODevice::Text)) if(!_srcFile.open(QIODevice::WriteOnly | QIODevice::Text))
throw FileException{_srcFile}; throw FileException{_srcFile};
writeSource(settings); writeSource(settings, typeNum);
_listTypes.clear();
_srcFile.close(); _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()} QString incGuard = QFileInfo{_hdrFile.fileName()}
.completeBaseName() .completeBaseName()
@ -67,7 +68,7 @@ void QmlSettingsGenerator::writeHeader(const SettingsType &settings, const QStri
std::tie(offset, childOffsets) = writeNodeContentClasses(settings, keyList); std::tie(offset, childOffsets) = writeNodeContentClasses(settings, keyList);
// create the class // create the class
_hdr << "class " << _name << " : public QObject\n" _hdr << "class " << (settings.prefix ? settings.prefix.value() + QLatin1Char(' ') : QString{}) << _name << " : public QObject\n"
<< "{\n" << "{\n"
<< "\tQ_OBJECT\n\n" << "\tQ_OBJECT\n\n"
<< "\tQ_PROPERTY(QtMvvm::ISettingsAccessor *accessor READ accessor CONSTANT FINAL)\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" << "\t{}\n\n"
<< "\tQtMvvm::ISettingsAccessor *accessor() const { return _settings->accessor(); }\n" << "\tQtMvvm::ISettingsAccessor *accessor() const { return _settings->accessor(); }\n"
<< "\t" << _cppName << " *settings() const { return _settings; }\n\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) if(settings.qml)
_hdr << "\t" << (settings.prefix ? settings.prefix.value() + QLatin1Char(' ') : QString{}) << "static void registerQmlTypes();\n"; _hdr << "\tstatic void registerQmlTypes();\n";
_hdr << "};\n\n" _hdr << "};\n\n"
<< "#endif //" << incGuard << '\n'; << "#endif //" << incGuard << '\n';
return offset;
} }
void QmlSettingsGenerator::writeListTypeBaseClass() void QmlSettingsGenerator::writeListTypeBaseClass()
@ -143,7 +146,9 @@ void QmlSettingsGenerator::writeListTypeBaseClass()
<< "\t\telement->_entry = &data->entry;\n" << "\t\telement->_entry = &data->entry;\n"
<< "\t\tdata->elements.append(element);\n" << "\t\tdata->elements.append(element);\n"
<< "\t\tif(copyDefault)\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\tdata->entry.addChangeCallback(element, [maxIndex, element](int i, const T &d) {\n"
<< "\t\t\tif(i == maxIndex)\n" << "\t\t\tif(i == maxIndex)\n"
<< "\t\t\t\temit element->valueChanged(d);\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" << "\texplicit " << _name << "_" << offset << "(QObject *parent = nullptr) : \n"
<< "\t\t" << _name << "_ListType{parent}\n" << "\t\t" << _name << "_ListType{parent}\n"
<< "\t{}\n\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" << "Q_SIGNALS:\n"
<< "\tvoid valueChanged(const " << mType << " &value);\n" << "\tvoid valueChanged(const " << mType << " &value);\n"
<< "};\n\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 auto lIndex = childOffsets.takeFirst(); //done seperately because of undefine param call order
writeListEntryProperty(nonstd::get<ListEntryType>(cNode), keyList, lIndex, childOffsets.takeFirst(), childConstructs); writeListEntryProperty(nonstd::get<ListEntryType>(cNode), keyList, lIndex, childOffsets.takeFirst(), childConstructs);
listEntries.append(lIndex); listEntries.append(lIndex);
_listTypes.insert(lIndex);
} else if(nonstd::holds_alternative<NodeContentGroup>(cNode)) } else if(nonstd::holds_alternative<NodeContentGroup>(cNode))
writeProperties(nonstd::get<NodeContentGroup>(cNode), keyList, childOffsets, listEntries, childConstructs); writeProperties(nonstd::get<NodeContentGroup>(cNode), keyList, childOffsets, listEntries, childConstructs);
else 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 << "::at<" << _name << "_" << listIndex << ">,\n"
<< "\t\t\t&" << _name << "_" << listIndex << "::clear<" << _name << "_" << listIndex << ">\n" << "\t\t\t&" << _name << "_" << listIndex << "::clear<" << _name << "_" << listIndex << ">\n"
<< "\t\t};\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}); childConstructs.append({entry.key, -1});
@ -370,27 +386,71 @@ void QmlSettingsGenerator::writeListEntryPropertySignalConnect(const SettingsGen
_hdr << "\t\t_settings->" << keyList.join(QLatin1Char('.')) _hdr << "\t\t_settings->" << keyList.join(QLatin1Char('.'))
<< ".addSizeChangeCallback(this, std::bind(&" << ".addSizeChangeCallback(this, std::bind(&"
<< _name << "_" << classIndex << "::adjust<" << _name << "_" << classIndex << _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 <QtCore/QCoreApplication>\n"
<< "#include <QtQml/QQmlEngine>\n\n";
writeQmlRegistration(settings); writeQmlRegistration(settings.qml ? settings.qml.value().type : Singleton, typeNum);
if(settings.qml) { if(settings.qml) {
const auto &qml = settings.qml.value(); const auto &qml = settings.qml.value();
_src << "\nvoid " << _name << "::registerQmlTypes()\n" _src << "void " << _name << "::registerQmlTypes()\n"
<< "{\n" << "{\n"
<< "\tregisterQmlTypes(\"" << qml.uri << "\", " << qml.major << ", " << qml.minor << ");\n" << "\tregisterQmlTypes(\"" << qml.uri << "\", " << qml.major << ", " << qml.minor << ");\n"
<< "}\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"
<< "}\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";
} }

9
tools/settingsgenerator/qmlsettingsgenerator.h

@ -3,6 +3,7 @@
#include "settingsgeneratorimpl.h" #include "settingsgeneratorimpl.h"
#include <tuple> #include <tuple>
#include <QSet>
class QmlSettingsGenerator : public SettingsGeneratorImpl class QmlSettingsGenerator : public SettingsGeneratorImpl
{ {
@ -24,7 +25,9 @@ private:
QString _prefixName; QString _prefixName;
QHash<QString, QString> _typeMappings; QHash<QString, QString> _typeMappings;
void writeHeader(const SettingsType &settings, const QString &inHdrPath); QSet<int> _listTypes;
int writeHeader(const SettingsType &settings, const QString &inHdrPath);
void writeListTypeBaseClass(); void writeListTypeBaseClass();
@ -46,9 +49,9 @@ private:
void writeEntryPropertySignalConnect(const EntryType &entry, QStringList keyList, int classIndex); void writeEntryPropertySignalConnect(const EntryType &entry, QStringList keyList, int classIndex);
void writeListEntryPropertySignalConnect(const ListEntryType &entry, QStringList keyList, QList<int> &listEntries); void writeListEntryPropertySignalConnect(const ListEntryType &entry, QStringList keyList, QList<int> &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 #endif // QMLSETTINGSGENERATOR_H

Loading…
Cancel
Save