diff --git a/tests/auto/qml/qmlsettingsgenerator/generatortest.xml b/tests/auto/qml/qmlsettingsgenerator/generatortest.xml
new file mode 100644
index 0000000..1bc9c4d
--- /dev/null
+++ b/tests/auto/qml/qmlsettingsgenerator/generatortest.xml
@@ -0,0 +1,70 @@
+
+
+ QtCore/QDateTime
+ QtCore/QUrl
+
+
+
+
+
+
+
+
+
+ QUrl::fromLocalFile(QStringLiteral("/path/to/something"))
+
+
+
+
+
+
+
+
+
+
+
+
+ qRound(42.8)
+
+
+
+
+
+
+
+
+
+
+
+ "Hello World"
+ {
+ "test1",
+ "test2",
+ "test3"
+ }
+
+
diff --git a/tests/auto/qml/qmlsettingsgenerator/qmlsettingsgenerator.pro b/tests/auto/qml/qmlsettingsgenerator/qmlsettingsgenerator.pro
index 9c5d2d0..4e1c584 100644
--- a/tests/auto/qml/qmlsettingsgenerator/qmlsettingsgenerator.pro
+++ b/tests/auto/qml/qmlsettingsgenerator/qmlsettingsgenerator.pro
@@ -10,7 +10,9 @@ SOURCES += \
tst_qmlsettingsgenerator.cpp
QML_SETTINGS_DEFINITIONS += \
- ../../mvvmcore/settingsgenerator/generatortest.xml
+ generatortest.xml
+
+SETTINGS_DEFINITIONS += $$QML_SETTINGS_DEFINITIONS
DISTFILES += \
tst_qmlsettingsgenerator.qml
diff --git a/tools/settingsgenerator/cppsettingsgenerator.cpp b/tools/settingsgenerator/cppsettingsgenerator.cpp
index a3d0adf..780abfa 100644
--- a/tools/settingsgenerator/cppsettingsgenerator.cpp
+++ b/tools/settingsgenerator/cppsettingsgenerator.cpp
@@ -53,7 +53,7 @@ void CppSettingsGenerator::writeHeader(const SettingsType &settings)
_hdr << "\n";
// create the class
- _hdr << "class " << QString{settings.prefix ? settings.prefix.value() + QLatin1Char(' ') + settings.name.value() : settings.name.value()} << " : public QObject\n"
+ _hdr << "class " << (settings.prefix ? settings.prefix.value() + QLatin1Char(' ') + settings.name.value() : settings.name.value()) << " : public QObject\n"
<< "{\n"
<< "\tQ_OBJECT\n\n"
<< "\tQ_PROPERTY(QtMvvm::ISettingsAccessor *accessor READ accessor CONSTANT FINAL)\n\n"
@@ -82,6 +82,8 @@ void CppSettingsGenerator::writeNodeElementDeclarations(const NodeContentGroup &
writeListEntryDeclaration(nonstd::get(cNode), typeMappings, intendent);
else if(nonstd::holds_alternative(cNode))
writeNodeElementDeclarations(nonstd::get(cNode), typeMappings, intendent);
+ else
+ Q_UNREACHABLE();
}
}
@@ -196,6 +198,8 @@ void CppSettingsGenerator::writeNodeElementDefinitions(const SettingsGeneratorBa
writeListEntryDefinition(nonstd::get(cNode), typeMappings, baseKey, keyChain);
else if(nonstd::holds_alternative(cNode))
writeNodeElementDefinitions(nonstd::get(cNode), typeMappings, baseKey, keyChain);
+ else
+ Q_UNREACHABLE();
}
}
diff --git a/tools/settingsgenerator/qmlsettingsgenerator.cpp b/tools/settingsgenerator/qmlsettingsgenerator.cpp
index bd973eb..4aaa025 100644
--- a/tools/settingsgenerator/qmlsettingsgenerator.cpp
+++ b/tools/settingsgenerator/qmlsettingsgenerator.cpp
@@ -17,9 +17,13 @@ void QmlSettingsGenerator::process(const QString &inPath)
settings.name.value().prepend(QStringLiteral("QMLTYPE_"));
fixTrContext(settings, QFileInfo{inPath}.fileName());
+ _name = settings.name.value();
+ _prefixName = settings.prefix ? settings.prefix.value() + QLatin1Char(' ') + _name : _name;
+ _typeMappings = settings.typeMappings;
+
if(!_hdrFile.open(QIODevice::WriteOnly | QIODevice::Text))
throw FileException{_hdrFile};
- writeHeader(settings);
+ writeHeader(settings, QFileInfo{inPath}.completeBaseName() + QStringLiteral(".h"));
_hdrFile.close();
if(!_srcFile.open(QIODevice::WriteOnly | QIODevice::Text))
@@ -28,7 +32,7 @@ void QmlSettingsGenerator::process(const QString &inPath)
_srcFile.close();
}
-void QmlSettingsGenerator::writeHeader(const SettingsGeneratorBase::SettingsType &settings)
+void QmlSettingsGenerator::writeHeader(const SettingsGeneratorBase::SettingsType &settings, const QString &inHdrPath)
{
QString incGuard = QFileInfo{_hdrFile.fileName()}
.completeBaseName()
@@ -38,10 +42,12 @@ void QmlSettingsGenerator::writeHeader(const SettingsGeneratorBase::SettingsType
<< "#define " << incGuard << "\n\n";
// write the includes
+ const auto &hdrPath = settings.qml.has_value() ? settings.qml.value().header.value_or(inHdrPath) : inHdrPath;
auto includes = QList {
{false, QStringLiteral("QtCore/QObject")},
+ {false, QStringLiteral("QtCore/QScopedPointer")},
{false, QStringLiteral("QtQml/QQmlListProperty")},
- {false, QStringLiteral("QtMvvmCore/ISettingsAccessor")}
+ {false, hdrPath}
} + settings.includes;
for(const auto &inc : includes) {
if(inc.local)
@@ -51,16 +57,24 @@ void QmlSettingsGenerator::writeHeader(const SettingsGeneratorBase::SettingsType
}
_hdr << "\n";
+ writeListTypeClasses(settings);
+
+ // create all the qmltype classes
+ int offset;
+ QList childOffsets;
+ std::tie(offset, childOffsets) = writeNodeContentClassesDeclarations(settings);
+
// create the class
- _hdr << "class " << QString{settings.prefix ? settings.prefix.value() + QLatin1Char(' ') + settings.name.value() : settings.name.value()} << " : public QObject\n"
+ _hdr << "class " << _prefixName << " : public QObject\n"
<< "{\n"
<< "\tQ_OBJECT\n\n"
- << "\tQ_PROPERTY(QtMvvm::ISettingsAccessor *accessor READ accessor CONSTANT FINAL)\n\n"
- << "public:\n"
- << "\texplicit " << settings.name.value() << "(QObject *parent = nullptr);\n"
- << "\tQtMvvm::ISettingsAccessor *accessor() const;\n\n";
+ << "\tQ_PROPERTY(QtMvvm::ISettingsAccessor *accessor READ accessor CONSTANT FINAL)\n\n";
+ writeNodeClassPropertiesDeclarations(settings, childOffsets);
+ _hdr << "public:\n"
+ << "\texplicit " << settings.name.value() << "(QObject *parent = nullptr);\n"
+ << "\tQtMvvm::ISettingsAccessor *accessor() const;\n\n";
_hdr << "\nprivate:\n"
<< "\tQtMvvm::ISettingsAccessor *_accessor;\n"
@@ -68,7 +82,187 @@ void QmlSettingsGenerator::writeHeader(const SettingsGeneratorBase::SettingsType
<< "#endif //" << incGuard << '\n';
}
+void QmlSettingsGenerator::writeListTypeClasses(const SettingsGeneratorBase::SettingsType &settings)
+{
+ // write the list wrapper base class
+ _hdr << "class " << _prefixName << "_ListType : public QObject\n"
+ << "{\n"
+ << "\tQ_OBJECT\n\n"
+ << "\tQ_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged)\n\n"
+ << "public:\n"
+ << "\tclass Accessor {\n"
+ << "\tpublic:\n"
+ << "\t\tinline virtual ~Accessor() = default;\n"
+ << "\t\tvirtual QVariant value() const = 0;\n"
+ << "\t\tvirtual void setValue(const QVariant &value) = 0;\n"
+ << "\t};\n\n"
+ << "\texplicit " << _prefixName << "_ListType(QObject *parent);\n\n"
+ << "\tQVariant value() const { return _data ? _data->value() : _buffer; }\n"
+ << "\tvoid setValue(const QVariant &value) { if(_data) _data->setValue(value); else _buffer = value; }\n"
+ << "\tvoid setAccessor(Accessor *accessor) { _data.reset(accessor); if(_buffer.isValid()) _data->setValue(_buffer); _buffer.clear(); }\n\n"
+ << "Q_SIGNALS:\n"
+ << "\tvoid valueChanged(const QVariant &value);\n\n"
+ << "private:\n"
+ << "\tQScopedPointer _data;\n"
+ << "\tQVariant _buffer;\n"
+ << "};\n\n";
+
+ // write the generic variant
+ _hdr << "template \n"
+ << "class " << _prefixName << "_ListType_Accessor : public " << _prefixName << "_ListType::Accessor\n"
+ << "{\n"
+ << "public:\n"
+ << "\tstruct ListData {\n"
+ << "\t\tQtMvvm::SettingsEntry> &entry;\n"
+ << "\t\tQList<" << _prefixName << "_ListType> elements;\n"
+ << "\t};\n\n"
+ << "\t" << _prefixName << "_ListType_Accessor(QtMvvm::SettingsEntry> &entry, int index) : \n"
+ << "\t\t_entry{entry},\n"
+ << "\t\t_index{index}\n"
+ << "\t{}\n"
+ << "\tQVariant value() const final { return QVariant::fromValue(_entry.getAt(_index)); }\n"
+ << "\tvoid setValue(const QVariant &value) final { _entry.setAt(_index, value.template value()); }\n\n"
+ << "private:\n"
+ << "\tfriend class " << _prefixName << ";\n"
+ << "\tQtMvvm::SettingsEntry> &_entry;\n"
+ << "\tconst int _index;\n\n"
+ << "\tstatic void append(QQmlListProperty<" << _prefixName << "_ListType>* list, " << _prefixName << "_ListType* element) {\n"
+ << "\t\tconst auto data = reinterpret_cast(list->data);\n"
+ << "\t\tconst auto maxIndex = data->entry.size();\n"
+ << "\t\tfor(auto index = data->elements.size(); index <= maxIndex; index++) {\n"
+ << "\t\t\t" << _prefixName << "_ListType *elem;\n"
+ << "\t\t\tif(index == maxIndex) {\n"
+ << "\t\t\t\telem = element;\n"
+ << "\t\t\t\telem->setParent(list->object);\n"
+ << "\t\t\t} else\n"
+ << "\t\t\t\telem = new " << _prefixName << "_ListType{list->object};\n"
+ << "\t\t\tdata->elements.append(elem);\n"
+ << "\t\t\telem->setAccessor(new " << _prefixName << "_ListType_Accessor{data->entry, index});\n"
+ << "\t\t\tdata->entry.addChangeCallback(elem, [index, elem](int i, const T &d) {\n"
+ << "\t\t\t\tif(i == index)\n"
+ << "\t\t\t\t\temit elem->valueChanged(QVariant::fromValue(d));\n"
+ << "\t\t\t});\n"
+ << "\t\t}\n"
+ << "\t}\n\n"
+ << "\tstatic " << _prefixName << "_ListType *at(QQmlListProperty<" << _prefixName << "_ListType>* list, int index) {\n"
+ << "\t\tconst auto data = reinterpret_cast(list->data);\n"
+ << "\t\treturn data->elements.size() > index ? data->elements.value(index) : nullptr;\n"
+ << "\t}\n\n"
+ << "\tstatic int count(QQmlListProperty<" << _prefixName << "_ListType>* list) {\n"
+ << "\t\treturn reinterpret_cast(list->data)->elements.size();\n"
+ << "\t}\n\n"
+ << "\tstatic void clear(QQmlListProperty<" << _prefixName << "_ListType>* list) {\n"
+ << "\t\tconst auto data = reinterpret_cast(list->data);\n"
+ << "\t\tfor(auto elem : qAsConst(data->elements))\n"
+ << "\t\t\telem->deleteLater();\n"
+ << "\t\tdata->elements.clear();\n"
+ << "\t\tdata->entry.reset(false);\n"
+ << "\t}\n"
+ << "};\n\n";
+}
+
+std::tuple> QmlSettingsGenerator::writeNodeContentClassesDeclarations(const SettingsGeneratorBase::NodeContentGroup &node, int offset)
+{
+ QList offsetList;
+ for(const auto &cNode : node.contentNodes) {
+ if(nonstd::holds_alternative(cNode)) {
+ offset = writeNodeClassDeclaration(nonstd::get(cNode), offset);
+ offsetList.append(offset - 1);
+ } else if(nonstd::holds_alternative(cNode)) {
+ if(!nonstd::get(cNode).contentNodes.isEmpty()) {
+ offset = writeNodeClassDeclaration(nonstd::get(cNode), offset);
+ offsetList.append(offset - 1);
+ } else
+ offsetList.append(-1);
+ } else if(nonstd::holds_alternative(cNode)) {
+ if(!nonstd::get(cNode).contentNodes.isEmpty()) {
+ offset = writeNodeClassDeclaration(nonstd::get(cNode), offset);
+ offsetList.append(offset - 1);
+ } else
+ offsetList.append(-1);
+ } else if(nonstd::holds_alternative(cNode)) {
+ QList subList;
+ std::tie(offset, subList) = writeNodeContentClassesDeclarations(nonstd::get(cNode), offset);
+ offsetList.append(subList);
+ } else
+ Q_UNREACHABLE();
+ }
+ return std::make_tuple(offset, offsetList);
+}
+
+void QmlSettingsGenerator::writeNodeClassPropertiesDeclarations(const SettingsGeneratorBase::NodeContentGroup &node, QList &childOffsets)
+{
+ for(const auto &cNode : node.contentNodes) {
+ if(nonstd::holds_alternative(cNode))
+ writeNodePropertyDeclaration(nonstd::get(cNode), childOffsets.takeFirst());
+ else if(nonstd::holds_alternative(cNode))
+ writeEntryPropertyDeclaration(nonstd::get(cNode), childOffsets.takeFirst());
+ else if(nonstd::holds_alternative(cNode))
+ writeEntryPropertyDeclaration(nonstd::get(cNode), childOffsets.takeFirst());
+ else if(nonstd::holds_alternative(cNode))
+ writeNodeClassPropertiesDeclarations(nonstd::get(cNode), childOffsets);
+ else
+ Q_UNREACHABLE();
+ }
+}
+
+void QmlSettingsGenerator::writeNodePropertyDeclaration(const SettingsGeneratorBase::NodeType &entry, int classIndex, const QString &overwriteName)
+{
+ const auto &mName = overwriteName.isNull() ? entry.key : overwriteName;
+ _hdr << "\tQ_PROPERTY(" << _name << "_" << classIndex << "* " << mName
+ << " MEMBER _" << mName << " CONSTANT)\n";
+
+ _hdr << "\t" << _name << "_" << classIndex << "* _" << mName << ";\n\n";
+}
+
+void QmlSettingsGenerator::writeEntryPropertyDeclaration(const SettingsGeneratorBase::EntryType &entry, int classIndex)
+{
+ const auto &mType = _typeMappings.value(entry.type, entry.type);
+ if(mType != QStringLiteral("void")) {
+ _hdr << "\tQ_PROPERTY(" << mType << " " << entry.key
+ << " READ get_" << entry.key
+ << " WRITE set_" << entry.key
+ << " NOTIFY notify_" << entry.key << ")\n";
+
+ _hdr << "\t" << mType << " get_" << entry.key << "() const;\n"
+ << "\tvoid set_" << entry.key << "(const " << mType << "& value);\n"
+ << "Q_SIGNALS:\n"
+ << "\tvoid notify_" << entry.key << "();\n"
+ << "private:\n\n";
+ }
+
+ if(!entry.contentNodes.isEmpty())
+ writeNodePropertyDeclaration(entry, classIndex, entry.qmlGroupKey.value_or(entry.key + QStringLiteral("Group")));
+}
+
+int QmlSettingsGenerator::writeNodeClassDeclaration(const SettingsGeneratorBase::NodeType &node, int offset)
+{
+ QList childOffsets;
+ std::tie(offset, childOffsets) = writeNodeContentClassesDeclarations(node, offset);
+
+ _hdr << "class " << _prefixName << "_" << offset << " : public QObject // " << node.key << "\n"
+ << "{\n"
+ << "\tQ_OBJECT\n\n";
+
+ _hdr << "public:\n"
+ << "\t" << _name << "_" << offset << "(QtMvvm::ISettingsAccessor *accessor, const QString &parentKey, QObject *parent);\n\n"
+ << "private:\n"
+ << "\tQtMvvm::ISettingsAccessor *_accessor;\n"
+ << "\tQString _nodeKey;\n\n";
+
+ writeNodeClassPropertiesDeclarations(node, childOffsets);
+
+ _hdr << "};\n\n";
+
+ return ++offset;
+}
+
void QmlSettingsGenerator::writeSource(const SettingsGeneratorBase::SettingsType &settings)
{
+ _src << "#include \"" << _hdrFile.fileName() << "\"\n\n";
+ _src << "QtMvvm::ISettingsAccessor *" << settings.name.value() << "::accessor() const\n"
+ << "{\n"
+ << "\treturn _accessor;\n"
+ << "}\n\n";
}
diff --git a/tools/settingsgenerator/qmlsettingsgenerator.h b/tools/settingsgenerator/qmlsettingsgenerator.h
index 15dae40..0e878ec 100644
--- a/tools/settingsgenerator/qmlsettingsgenerator.h
+++ b/tools/settingsgenerator/qmlsettingsgenerator.h
@@ -2,6 +2,7 @@
#define QMLSETTINGSGENERATOR_H
#include "settingsgeneratorimpl.h"
+#include
class QmlSettingsGenerator : public SettingsGeneratorImpl
{
@@ -11,15 +12,27 @@ public:
void process(const QString &inPath);
- void writeHeader(const SettingsType &settings);
- void writeSource(const SettingsType &settings);
-
private:
QFile _hdrFile;
QFile _srcFile;
QTextStream _hdr;
QTextStream _src;
+
+ QString _name;
+ QString _prefixName;
+ QHash _typeMappings;
+
+ void writeHeader(const SettingsType &settings, const QString &inHdrPath);
+
+ void writeListTypeClasses(const SettingsType &settings);
+ std::tuple> writeNodeContentClassesDeclarations(const NodeContentGroup &node, int offset = 0);
+ void writeNodeClassPropertiesDeclarations(const NodeContentGroup &node, QList &childOffsets);
+ void writeNodePropertyDeclaration(const NodeType &entry, int classIndex, const QString &overwriteName = {});
+ void writeEntryPropertyDeclaration(const EntryType &entry, int classIndex);
+ int writeNodeClassDeclaration(const NodeType &node, int offset);
+
+ void writeSource(const SettingsType &settings);
};
#endif // QMLSETTINGSGENERATOR_H
diff --git a/tools/settingsgenerator/qsettingsgenerator.xsd b/tools/settingsgenerator/qsettingsgenerator.xsd
index e638652..d0ce70c 100644
--- a/tools/settingsgenerator/qsettingsgenerator.xsd
+++ b/tools/settingsgenerator/qsettingsgenerator.xsd
@@ -19,6 +19,15 @@
+
+
+
+
+
+
+
+
+
@@ -93,7 +102,6 @@
-
@@ -102,6 +110,7 @@
+