Browse Source

WIP qml listentry definition

pull/2/head
Skycoder42 7 years ago
parent
commit
f4f11977e2
No known key found for this signature in database GPG Key ID: 8E01AD9EF0578D2B
  1. 70
      tests/auto/qml/qmlsettingsgenerator/generatortest.xml
  2. 4
      tests/auto/qml/qmlsettingsgenerator/qmlsettingsgenerator.pro
  3. 6
      tools/settingsgenerator/cppsettingsgenerator.cpp
  4. 210
      tools/settingsgenerator/qmlsettingsgenerator.cpp
  5. 19
      tools/settingsgenerator/qmlsettingsgenerator.h
  6. 11
      tools/settingsgenerator/qsettingsgenerator.xsd

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

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Settings name="TestSettings">
<Include>QtCore/QDateTime</Include>
<Include local="false">QtCore/QUrl</Include>
<Qml uri="de.skycoder42.QtMvvm.Core.test"
major="4"
minor="2"
type="Singleton"
register="true"
header="generatortest.h"/>
<TypeMapping key="range" type="int"/>
<Node key="emptyNode"/>
<Entry key="emptyEntry"
type="bool"/>
<Entry key="advancedEntry"
type="QString"
qmlGroupKey="qmlAdvancedEntry"
default="Hello World"
tr="true"
trContext="some_context"/>
<Entry key="codeEntry"
type="QUrl">
<Code>QUrl::fromLocalFile(QStringLiteral("/path/to/something"))</Code>
</Entry>
<Node key="parentNode">
<Node key="emptyChildNode"/>
<Node key="fullChildNode">
<Entry key="replaceEntry"
type="range"
default="42"/>
</Node>
<Entry key="parentEntry"
type="bool"
default="true">
<Node key="subNode"/>
<Entry key="nodeWithCodeEntry"
type="int">
<Node key="someNode"/>
<Code>
qRound(42.8)
</Code>
</Entry>
<Entry key="leafEntry"
type="QString"
default="translate me"
tr="true"/>
</Entry>
</Node>
<Entry key="voidEntry"
type="void"/>
<Entry key="variantEntry"
type="QVariant"/>
<ListEntry key="listEntry"
type="QByteArray">
<Entry key="dummyChild"
type="bool"/>
<Code>&quot;Hello World&quot;</Code>
<Init> {
&quot;test1&quot;,
&quot;test2&quot;,
&quot;test3&quot;
} </Init>
</ListEntry>
</Settings>

4
tests/auto/qml/qmlsettingsgenerator/qmlsettingsgenerator.pro

@ -10,7 +10,9 @@ SOURCES += \
tst_qmlsettingsgenerator.cpp tst_qmlsettingsgenerator.cpp
QML_SETTINGS_DEFINITIONS += \ QML_SETTINGS_DEFINITIONS += \
../../mvvmcore/settingsgenerator/generatortest.xml generatortest.xml
SETTINGS_DEFINITIONS += $$QML_SETTINGS_DEFINITIONS
DISTFILES += \ DISTFILES += \
tst_qmlsettingsgenerator.qml tst_qmlsettingsgenerator.qml

6
tools/settingsgenerator/cppsettingsgenerator.cpp

@ -53,7 +53,7 @@ void CppSettingsGenerator::writeHeader(const SettingsType &settings)
_hdr << "\n"; _hdr << "\n";
// create the class // 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" << "{\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"
@ -82,6 +82,8 @@ void CppSettingsGenerator::writeNodeElementDeclarations(const NodeContentGroup &
writeListEntryDeclaration(nonstd::get<ListEntryType>(cNode), typeMappings, intendent); writeListEntryDeclaration(nonstd::get<ListEntryType>(cNode), typeMappings, intendent);
else if(nonstd::holds_alternative<NodeContentGroup>(cNode)) else if(nonstd::holds_alternative<NodeContentGroup>(cNode))
writeNodeElementDeclarations(nonstd::get<NodeContentGroup>(cNode), typeMappings, intendent); writeNodeElementDeclarations(nonstd::get<NodeContentGroup>(cNode), typeMappings, intendent);
else
Q_UNREACHABLE();
} }
} }
@ -196,6 +198,8 @@ void CppSettingsGenerator::writeNodeElementDefinitions(const SettingsGeneratorBa
writeListEntryDefinition(nonstd::get<ListEntryType>(cNode), typeMappings, baseKey, keyChain); writeListEntryDefinition(nonstd::get<ListEntryType>(cNode), typeMappings, baseKey, keyChain);
else if(nonstd::holds_alternative<NodeContentGroup>(cNode)) else if(nonstd::holds_alternative<NodeContentGroup>(cNode))
writeNodeElementDefinitions(nonstd::get<NodeContentGroup>(cNode), typeMappings, baseKey, keyChain); writeNodeElementDefinitions(nonstd::get<NodeContentGroup>(cNode), typeMappings, baseKey, keyChain);
else
Q_UNREACHABLE();
} }
} }

210
tools/settingsgenerator/qmlsettingsgenerator.cpp

@ -17,9 +17,13 @@ void QmlSettingsGenerator::process(const QString &inPath)
settings.name.value().prepend(QStringLiteral("QMLTYPE_")); settings.name.value().prepend(QStringLiteral("QMLTYPE_"));
fixTrContext(settings, QFileInfo{inPath}.fileName()); 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)) if(!_hdrFile.open(QIODevice::WriteOnly | QIODevice::Text))
throw FileException{_hdrFile}; throw FileException{_hdrFile};
writeHeader(settings); 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))
@ -28,7 +32,7 @@ void QmlSettingsGenerator::process(const QString &inPath)
_srcFile.close(); _srcFile.close();
} }
void QmlSettingsGenerator::writeHeader(const SettingsGeneratorBase::SettingsType &settings) void QmlSettingsGenerator::writeHeader(const SettingsGeneratorBase::SettingsType &settings, const QString &inHdrPath)
{ {
QString incGuard = QFileInfo{_hdrFile.fileName()} QString incGuard = QFileInfo{_hdrFile.fileName()}
.completeBaseName() .completeBaseName()
@ -38,10 +42,12 @@ void QmlSettingsGenerator::writeHeader(const SettingsGeneratorBase::SettingsType
<< "#define " << incGuard << "\n\n"; << "#define " << incGuard << "\n\n";
// write the includes // write the includes
const auto &hdrPath = settings.qml.has_value() ? settings.qml.value().header.value_or(inHdrPath) : inHdrPath;
auto includes = QList<IncludeType> { auto includes = QList<IncludeType> {
{false, QStringLiteral("QtCore/QObject")}, {false, QStringLiteral("QtCore/QObject")},
{false, QStringLiteral("QtCore/QScopedPointer")},
{false, QStringLiteral("QtQml/QQmlListProperty")}, {false, QStringLiteral("QtQml/QQmlListProperty")},
{false, QStringLiteral("QtMvvmCore/ISettingsAccessor")} {false, hdrPath}
} + settings.includes; } + settings.includes;
for(const auto &inc : includes) { for(const auto &inc : includes) {
if(inc.local) if(inc.local)
@ -51,16 +57,24 @@ void QmlSettingsGenerator::writeHeader(const SettingsGeneratorBase::SettingsType
} }
_hdr << "\n"; _hdr << "\n";
writeListTypeClasses(settings);
// create all the qmltype classes
int offset;
QList<int> childOffsets;
std::tie(offset, childOffsets) = writeNodeContentClassesDeclarations(settings);
// create the class // 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" << "{\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";
<< "public:\n"
<< "\texplicit " << settings.name.value() << "(QObject *parent = nullptr);\n"
<< "\tQtMvvm::ISettingsAccessor *accessor() const;\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" _hdr << "\nprivate:\n"
<< "\tQtMvvm::ISettingsAccessor *_accessor;\n" << "\tQtMvvm::ISettingsAccessor *_accessor;\n"
@ -68,7 +82,187 @@ void QmlSettingsGenerator::writeHeader(const SettingsGeneratorBase::SettingsType
<< "#endif //" << incGuard << '\n'; << "#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<Accessor> _data;\n"
<< "\tQVariant _buffer;\n"
<< "};\n\n";
// write the generic variant
_hdr << "template <typename T>\n"
<< "class " << _prefixName << "_ListType_Accessor : public " << _prefixName << "_ListType::Accessor\n"
<< "{\n"
<< "public:\n"
<< "\tstruct ListData {\n"
<< "\t\tQtMvvm::SettingsEntry<QList<T>> &entry;\n"
<< "\t\tQList<" << _prefixName << "_ListType> elements;\n"
<< "\t};\n\n"
<< "\t" << _prefixName << "_ListType_Accessor(QtMvvm::SettingsEntry<QList<T>> &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<T>()); }\n\n"
<< "private:\n"
<< "\tfriend class " << _prefixName << ";\n"
<< "\tQtMvvm::SettingsEntry<QList<T>> &_entry;\n"
<< "\tconst int _index;\n\n"
<< "\tstatic void append(QQmlListProperty<" << _prefixName << "_ListType>* list, " << _prefixName << "_ListType* element) {\n"
<< "\t\tconst auto data = reinterpret_cast<ListData*>(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<T>{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<ListData*>(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<ListData*>(list->data)->elements.size();\n"
<< "\t}\n\n"
<< "\tstatic void clear(QQmlListProperty<" << _prefixName << "_ListType>* list) {\n"
<< "\t\tconst auto data = reinterpret_cast<ListData*>(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<int, QList<int>> QmlSettingsGenerator::writeNodeContentClassesDeclarations(const SettingsGeneratorBase::NodeContentGroup &node, int offset)
{
QList<int> offsetList;
for(const auto &cNode : node.contentNodes) {
if(nonstd::holds_alternative<NodeType>(cNode)) {
offset = writeNodeClassDeclaration(nonstd::get<NodeType>(cNode), offset);
offsetList.append(offset - 1);
} else if(nonstd::holds_alternative<EntryType>(cNode)) {
if(!nonstd::get<EntryType>(cNode).contentNodes.isEmpty()) {
offset = writeNodeClassDeclaration(nonstd::get<EntryType>(cNode), offset);
offsetList.append(offset - 1);
} else
offsetList.append(-1);
} else if(nonstd::holds_alternative<ListEntryType>(cNode)) {
if(!nonstd::get<ListEntryType>(cNode).contentNodes.isEmpty()) {
offset = writeNodeClassDeclaration(nonstd::get<ListEntryType>(cNode), offset);
offsetList.append(offset - 1);
} else
offsetList.append(-1);
} else if(nonstd::holds_alternative<NodeContentGroup>(cNode)) {
QList<int> subList;
std::tie(offset, subList) = writeNodeContentClassesDeclarations(nonstd::get<NodeContentGroup>(cNode), offset);
offsetList.append(subList);
} else
Q_UNREACHABLE();
}
return std::make_tuple(offset, offsetList);
}
void QmlSettingsGenerator::writeNodeClassPropertiesDeclarations(const SettingsGeneratorBase::NodeContentGroup &node, QList<int> &childOffsets)
{
for(const auto &cNode : node.contentNodes) {
if(nonstd::holds_alternative<NodeType>(cNode))
writeNodePropertyDeclaration(nonstd::get<NodeType>(cNode), childOffsets.takeFirst());
else if(nonstd::holds_alternative<EntryType>(cNode))
writeEntryPropertyDeclaration(nonstd::get<EntryType>(cNode), childOffsets.takeFirst());
else if(nonstd::holds_alternative<ListEntryType>(cNode))
writeEntryPropertyDeclaration(nonstd::get<ListEntryType>(cNode), childOffsets.takeFirst());
else if(nonstd::holds_alternative<NodeContentGroup>(cNode))
writeNodeClassPropertiesDeclarations(nonstd::get<NodeContentGroup>(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<int> 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) 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";
} }

19
tools/settingsgenerator/qmlsettingsgenerator.h

@ -2,6 +2,7 @@
#define QMLSETTINGSGENERATOR_H #define QMLSETTINGSGENERATOR_H
#include "settingsgeneratorimpl.h" #include "settingsgeneratorimpl.h"
#include <tuple>
class QmlSettingsGenerator : public SettingsGeneratorImpl class QmlSettingsGenerator : public SettingsGeneratorImpl
{ {
@ -11,15 +12,27 @@ public:
void process(const QString &inPath); void process(const QString &inPath);
void writeHeader(const SettingsType &settings);
void writeSource(const SettingsType &settings);
private: private:
QFile _hdrFile; QFile _hdrFile;
QFile _srcFile; QFile _srcFile;
QTextStream _hdr; QTextStream _hdr;
QTextStream _src; QTextStream _src;
QString _name;
QString _prefixName;
QHash<QString, QString> _typeMappings;
void writeHeader(const SettingsType &settings, const QString &inHdrPath);
void writeListTypeClasses(const SettingsType &settings);
std::tuple<int, QList<int>> writeNodeContentClassesDeclarations(const NodeContentGroup &node, int offset = 0);
void writeNodeClassPropertiesDeclarations(const NodeContentGroup &node, QList<int> &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 #endif // QMLSETTINGSGENERATOR_H

11
tools/settingsgenerator/qsettingsgenerator.xsd

@ -19,6 +19,15 @@
</xs:simpleContent> </xs:simpleContent>
</xs:complexType> </xs:complexType>
<xs:complexType name="QmlType">
<xs:attribute name="uri" type="xs:string" use="required"/>
<xs:attribute name="major" type="xs:integer" use="optional" default="1"/>
<xs:attribute name="minor" type="xs:integer" use="optional" default="0"/>
<xs:attribute name="type" type="xs:string" use="optional" default="Singleton"/> <!-- TODO enum -->
<xs:attribute name="register" type="xs:boolean" use="optional" default="true" qxg:member="autoRegister"/>
<xs:attribute name="header" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="ImportType"> <xs:complexType name="ImportType">
<xs:simpleContent> <xs:simpleContent>
<xs:extension base="xs:string" qxg:member="importPath"> <xs:extension base="xs:string" qxg:member="importPath">
@ -93,7 +102,6 @@
<xs:sequence> <xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Init" type="xs:string"/> <xs:element maxOccurs="1" minOccurs="0" name="Init" type="xs:string"/>
</xs:sequence> </xs:sequence>
<xs:attribute name="qmllist" type="xs:boolean" use="optional" default="false"/>
</xs:extension> </xs:extension>
</xs:complexContent> </xs:complexContent>
</xs:complexType> </xs:complexType>
@ -102,6 +110,7 @@
<xs:sequence> <xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Include" type="IncludeType" qxg:member="includes"/> <xs:element maxOccurs="unbounded" minOccurs="0" name="Include" type="IncludeType" qxg:member="includes"/>
<xs:element maxOccurs="1" minOccurs="0" name="Backend" type="BackendType"/> <xs:element maxOccurs="1" minOccurs="0" name="Backend" type="BackendType"/>
<xs:element maxOccurs="1" minOccurs="0" name="Qml" type="QmlType"/>
<xs:group ref="TypeMappingGroup" qxg:member="typeMappings" qxg:method="read_type_mapping"/> <xs:group ref="TypeMappingGroup" qxg:member="typeMappings" qxg:method="read_type_mapping"/>
<xs:group ref="NodeContentGroup" qxg:inherit="true"/> <xs:group ref="NodeContentGroup" qxg:inherit="true"/>
</xs:sequence> </xs:sequence>

Loading…
Cancel
Save