diff --git a/README.md b/README.md index cfbd1d4..d5cf3a8 100644 --- a/README.md +++ b/README.md @@ -57,17 +57,13 @@ Good links to get started: - Package: `qtmvvm` - **IMPORTANT:** Due to limitations of homebrew, you must run `source /usr/local/opt/qtmvvm/bashrc.sh` before you can use the module. Some goes for the `qtdatasync` dependency. 2. Simply add my repository to your Qt MaintenanceTool (Image-based How-To here: [Add custom repository](https://github.com/Skycoder42/QtModules/blob/master/README.md#add-my-repositories-to-qt-maintenancetool)): - 1. Open the MaintenanceTool, located in your Qt install directory (e.g. `~/Qt/MaintenanceTool`) - 2. Select `Add or remove components` and click on the `Settings` button - 3. Go to `Repositories`, scroll to the bottom, select `User defined repositories` and press `Add` - 4. In the right column (selected by default), type: + 1. Start the MaintenanceTool from the commandline using `/path/to/MaintenanceTool --addTempRepository ` with one of the following urls (GUI-Method is currently broken, see [QTIFW-1156](https://bugreports.qt.io/browse/QTIFW-1156)) - This must be done *every time* you start the tool: - On Linux: https://install.skycoder42.de/qtmodules/linux_x64 - On Windows: https://install.skycoder42.de/qtmodules/windows_x86 - On Mac: https://install.skycoder42.de/qtmodules/mac_x64 - 5. Press `Ok`, make shure `Add or remove components` is still selected, and continue the install (`Next >`) - 6. A new entry appears under all supported Qt Versions (e.g. `Qt > Qt 5.10 > Skycoder42 Qt modules`) - 7. You can install either all of my modules, or select the one you need: `Qt Mvvm` - 8. Continue the setup and thats it! you can now use the module for all of your installed Kits for that Qt Version + 2. A new entry appears under all supported Qt Versions (e.g. `Qt > Qt 5.11 > Skycoder42 Qt modules`) + 3. You can install either all of my modules, or select the one you need: `Qt Mvvm` + 4. Continue the setup and thats it! you can now use the module for all of your installed Kits for that Qt Version 3. Download the compiled modules from the release page. **Note:** You will have to add the correct ones yourself and may need to adjust some paths to fit your installation! In addition to that, you will have to download the modules this one depends on as well. See Section "Requirements" below. 4. Build it yourself! **Note:** This requires all build an runtime dependencies to be available (See Section "Requirements" below). If you don't have/need cmake, you can ignore the related warnings. To automatically build and install to your Qt installation, run: - `qmake` diff --git a/src/mvvmcore/settingsentry.h b/src/mvvmcore/settingsentry.h index 9fac433..863451a 100644 --- a/src/mvvmcore/settingsentry.h +++ b/src/mvvmcore/settingsentry.h @@ -14,6 +14,7 @@ public: SettingsEntry() = default; bool isSet() const; + QString key() const; T get() const; void set(const T &value); @@ -26,7 +27,7 @@ public: void addChangeCallback(QObject *scope, const std::function &callback); // internal - void setup(const QString &key, ISettingsAccessor *accessor, const QVariant &defaultValue = {}); + void setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue = {}); private: QString _key; @@ -40,10 +41,10 @@ class SettingsEntry Q_DISABLE_COPY(SettingsEntry) public: - SettingsEntry() = default; + inline SettingsEntry() = default; // internal - void setup(const QString &, ISettingsAccessor *, const QVariant & = {}) {} + inline void setup(const QString &, ISettingsAccessor *, const QVariant & = {}) {} }; @@ -55,6 +56,12 @@ bool SettingsEntry::isSet() const return _accessor->contains(_key); } +template +QString SettingsEntry::key() const +{ + return _key; +} + template T SettingsEntry::get() const { @@ -110,12 +117,12 @@ SettingsEntry::operator const T() const } template -void SettingsEntry::setup(const QString &key, ISettingsAccessor *accessor, const QVariant &defaultValue) +void SettingsEntry::setup(QString key, ISettingsAccessor *accessor, QVariant defaultValue) { Q_ASSERT_X(accessor, Q_FUNC_INFO, "You must set a valid accessor before initializing the settings!"); - _key = key; + _key = std::move(key); _accessor = accessor; - _default = defaultValue; + _default = std::move(defaultValue); } } diff --git a/tests/auto/mvvmcore/settingsgenerator/settings.xml b/tests/auto/mvvmcore/settingsgenerator/generatortest.xml similarity index 91% rename from tests/auto/mvvmcore/settingsgenerator/settings.xml rename to tests/auto/mvvmcore/settingsgenerator/generatortest.xml index 04414e7..d877cb7 100644 --- a/tests/auto/mvvmcore/settingsgenerator/settings.xml +++ b/tests/auto/mvvmcore/settingsgenerator/generatortest.xml @@ -6,7 +6,7 @@ QtCore/QUrl testbackend.h - + Test Backend 42 @@ -35,13 +35,14 @@ default="42"/> + type="bool" + default="true"> - qRound(42.5) + qRound(42.8) + + + + + + +
+ + +
+
+ + + + + + +
+
+
diff --git a/tests/auto/mvvmcore/settingsgenerator/import_normal.xml b/tests/auto/mvvmcore/settingsgenerator/import_normal.xml new file mode 100644 index 0000000..27d8303 --- /dev/null +++ b/tests/auto/mvvmcore/settingsgenerator/import_normal.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/auto/mvvmcore/settingsgenerator/importtest.xml b/tests/auto/mvvmcore/settingsgenerator/importtest.xml new file mode 100644 index 0000000..9b75be9 --- /dev/null +++ b/tests/auto/mvvmcore/settingsgenerator/importtest.xml @@ -0,0 +1,11 @@ + + + QtCore/QUrl + + + + + import_normal.xml + + import_config.xml + diff --git a/tests/auto/mvvmcore/settingsgenerator/settingsgenerator.pro b/tests/auto/mvvmcore/settingsgenerator/settingsgenerator.pro index 6862b82..a55a7b2 100644 --- a/tests/auto/mvvmcore/settingsgenerator/settingsgenerator.pro +++ b/tests/auto/mvvmcore/settingsgenerator/settingsgenerator.pro @@ -8,11 +8,16 @@ CONFIG -= app_bundle TARGET = tst_settingsgenerator SOURCES += \ - tst_settingsgenerator.cpp \ - testbackend.cpp + tst_settingsgenerator.cpp \ + testbackend.cpp SETTINGS_DEFINITIONS += \ - settings.xml + generatortest.xml \ + importtest.xml HEADERS += \ - testbackend.h + testbackend.h + +DISTFILES += \ + import_normal.xml \ + import_config.xml diff --git a/tests/auto/mvvmcore/settingsgenerator/testbackend.cpp b/tests/auto/mvvmcore/settingsgenerator/testbackend.cpp index 6404b82..87138dc 100644 --- a/tests/auto/mvvmcore/settingsgenerator/testbackend.cpp +++ b/tests/auto/mvvmcore/settingsgenerator/testbackend.cpp @@ -8,20 +8,24 @@ TestBackend::TestBackend(QString name, int code, QObject *parent) : bool TestBackend::contains(const QString &key) const { - return false; + return _data.contains(key); } QVariant TestBackend::load(const QString &key, const QVariant &defaultValue) const { - return {}; + 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) { + _data.remove(key); + emit entryRemoved(key); } void TestBackend::sync() diff --git a/tests/auto/mvvmcore/settingsgenerator/testbackend.h b/tests/auto/mvvmcore/settingsgenerator/testbackend.h index f42bfa3..a3ddbf5 100644 --- a/tests/auto/mvvmcore/settingsgenerator/testbackend.h +++ b/tests/auto/mvvmcore/settingsgenerator/testbackend.h @@ -1,6 +1,7 @@ #ifndef TESTBACKEND_H #define TESTBACKEND_H +#include #include class TestBackend : public QtMvvm::ISettingsAccessor @@ -23,6 +24,8 @@ public slots: public: QString _name; int _code; + + QVariantHash _data; }; #define SOME_EXPORT diff --git a/tests/auto/mvvmcore/settingsgenerator/tst_settingsgenerator.cpp b/tests/auto/mvvmcore/settingsgenerator/tst_settingsgenerator.cpp index 7739a21..f221b84 100644 --- a/tests/auto/mvvmcore/settingsgenerator/tst_settingsgenerator.cpp +++ b/tests/auto/mvvmcore/settingsgenerator/tst_settingsgenerator.cpp @@ -1,33 +1,85 @@ #include #include #include +#include +#include "generatortest.h" +#include "importtest.h" class SettingsGeneratorTest : public QObject { Q_OBJECT -public: - SettingsGeneratorTest(); - private Q_SLOTS: - void testSettingsGeneration_data(); - void testSettingsGeneration(); + void testSettingsGenerator(); + void testImportedSettings(); }; -SettingsGeneratorTest::SettingsGeneratorTest() -{ -} -void SettingsGeneratorTest::testSettingsGeneration_data() +void SettingsGeneratorTest::testSettingsGenerator() { - QTest::addColumn("data"); - QTest::newRow("0") << QString(); + auto settings = TestSettings::instance(); + QVERIFY(settings); + + // verify backend + auto tBackend = qobject_cast(settings->accessor()); + QVERIFY(tBackend); + QCOMPARE(tBackend->parent(), settings); + QCOMPARE(tBackend->_name, QStringLiteral("Test Backend")); + QCOMPARE(tBackend->_code, 42); + + //verify keys + QCOMPARE(settings->emptyEntry.key(), QStringLiteral("tests/emptyEntry")); + QCOMPARE(settings->advancedEntry.key(), QStringLiteral("tests/advancedEntry")); + QCOMPARE(settings->codeEntry.key(), QStringLiteral("tests/codeEntry")); + QCOMPARE(settings->parentNode.fullChildNode.replaceEntry.key(), QStringLiteral("tests/parentNode/fullChildNode/replaceEntry")); + QCOMPARE(settings->parentNode.parentEntry.key(), QStringLiteral("tests/parentNode/parentEntry")); + QCOMPARE(settings->parentNode.parentEntry.nodeWithCodeEntry.key(), QStringLiteral("tests/parentNode/parentEntry/nodeWithCodeEntry")); + QCOMPARE(settings->parentNode.parentEntry.leafEntry.key(), QStringLiteral("tests/parentNode/parentEntry/leafEntry")); + + //verify defaults + QCOMPARE(settings->emptyEntry.get(), false); + QCOMPARE(settings->advancedEntry.get(), QStringLiteral("Hello World")); + QCOMPARE(settings->codeEntry.get(), QUrl::fromLocalFile(QStringLiteral("/path/to/something"))); + QCOMPARE(settings->parentNode.fullChildNode.replaceEntry.get(), 42); + QCOMPARE(settings->parentNode.parentEntry.get(), true); + QCOMPARE(settings->parentNode.parentEntry.nodeWithCodeEntry.get(), 43); + QCOMPARE(settings->parentNode.parentEntry.leafEntry.get(), QStringLiteral("translate me")); + + //verify read/write (just on a single entry, they work all the same) + Q_ASSERT(tBackend->_data.isEmpty()); + QString cValue; + settings->advancedEntry.addChangeCallback([&](QString value) { + cValue = std::move(value); + }); + QString tValue = QStringLiteral("test42"); + auto tKey = QStringLiteral("tests/advancedEntry"); + QCOMPARE(settings->advancedEntry.key(), tKey); + settings->advancedEntry = tValue; + QCOMPARE(tBackend->_data.value(tKey).toString(), tValue); + QCOMPARE(cValue, tValue); + QVERIFY(settings->advancedEntry.isSet()); + QCOMPARE(static_cast(settings->advancedEntry), tValue); } -void SettingsGeneratorTest::testSettingsGeneration() +void SettingsGeneratorTest::testImportedSettings() { - QFETCH(QString, data); - QVERIFY2(true, "Failure"); + auto settings = TestImportSettings::instance(); + QVERIFY(settings); + QVERIFY(qobject_cast(settings->accessor())); + + //verify keys + QCOMPARE(settings->impRoot.importedEntry.key(), QStringLiteral("impRoot/importedEntry")); + QCOMPARE(settings->node1.key(), QStringLiteral("node1")); + QCOMPARE(settings->node1.entry1.key(), QStringLiteral("node1/entry1")); + QCOMPARE(settings->node1.entry2.key(), QStringLiteral("node1/entry2")); + QCOMPARE(settings->node2.entry1.key(), QStringLiteral("node2/entry1")); + + //verify defaults + QCOMPARE(settings->impRoot.importedEntry.get(), false); + QCOMPARE(settings->node1.get(), QUrl{}); + QCOMPARE(settings->node1.entry1.get(), true); + QCOMPARE(settings->node1.entry2.get(), 42); + QCOMPARE(settings->node2.entry1.get(), QString{}); } QTEST_MAIN(SettingsGeneratorTest) diff --git a/tools/settingsgenerator/qsettingsgenerator.xsd b/tools/settingsgenerator/qsettingsgenerator.xsd index 0cf9c48..624337b 100644 --- a/tools/settingsgenerator/qsettingsgenerator.xsd +++ b/tools/settingsgenerator/qsettingsgenerator.xsd @@ -53,6 +53,7 @@ + diff --git a/tools/settingsgenerator/settingsgenerator.cpp b/tools/settingsgenerator/settingsgenerator.cpp index afb5bdf..3491e06 100644 --- a/tools/settingsgenerator/settingsgenerator.cpp +++ b/tools/settingsgenerator/settingsgenerator.cpp @@ -257,7 +257,7 @@ void SettingsGenerator::writeHeader(const SettingsType &settings) << "\tstatic " << settings.name.value() << " *instance();\n\n" << "\tQtMvvm::ISettingsAccessor *accessor() const;\n\n"; - writeNodeElements(settings, settings.typeMappings); + writeNodeElementDeclarations(settings, settings.typeMappings); _hdr << "\nprivate:\n" << "\tQtMvvm::ISettingsAccessor *_accessor;\n" @@ -265,32 +265,34 @@ void SettingsGenerator::writeHeader(const SettingsType &settings) << "#endif //" << incGuard << '\n'; } -void SettingsGenerator::writeNodeElements(const NodeContentGroup &node, const QHash &typeMappings, int intendent) +void SettingsGenerator::writeNodeElementDeclarations(const NodeContentGroup &node, const QHash &typeMappings, int intendent) { for(const auto &cNode : node.contentNodes) { if(nonstd::holds_alternative(cNode)) - writeNode(nonstd::get(cNode), typeMappings, intendent); + writeNodeDeclaration(nonstd::get(cNode), typeMappings, intendent); else if(nonstd::holds_alternative(cNode)) - writeEntry(nonstd::get(cNode), typeMappings, intendent); + writeEntryDeclaration(nonstd::get(cNode), typeMappings, intendent); else if(nonstd::holds_alternative(cNode)) - writeNodeElements(nonstd::get(cNode), typeMappings, intendent); + writeNodeElementDeclarations(nonstd::get(cNode), typeMappings, intendent); } } -void SettingsGenerator::writeNode(const NodeType &node, const QHash &typeMappings, int intendent) +void SettingsGenerator::writeNodeDeclaration(const NodeType &node, const QHash &typeMappings, int intendent) { _hdr << TABS << "struct { //" << node.key << "\n"; - writeNodeElements(node, typeMappings, intendent + 1); + writeNodeElementDeclarations(node, typeMappings, intendent + 1); _hdr << TABS << "} " << node.key << ";\n"; } -void SettingsGenerator::writeEntry(const EntryType &entry, const QHash &typeMappings, int intendent) +void SettingsGenerator::writeEntryDeclaration(const EntryType &entry, const QHash &typeMappings, int intendent) { if(entry.contentNodes.isEmpty()) _hdr << TABS << "QtMvvm::SettingsEntry<" << typeMappings.value(entry.type, entry.type) << "> " << entry.key << ";\n"; else { - _hdr << TABS << "struct : QtMvvm::SettingsEntry<" << typeMappings.value(entry.type, entry.type) << "> { //" << entry.key << "\n"; - writeNodeElements(entry, typeMappings, intendent + 1); + const auto &mType = typeMappings.value(entry.type, entry.type); + _hdr << TABS << "struct : QtMvvm::SettingsEntry<" << mType << "> { //" << entry.key << "\n"; + writeNodeElementDeclarations(entry, typeMappings, intendent + 1); + _hdr << TABS << "\tinline auto &operator=(const " << mType << " &__value) { SettingsEntry<" << mType << ">::operator=(__value); return *this; }\n"; _hdr << TABS << "} " << entry.key << ";\n"; } } @@ -298,11 +300,25 @@ void SettingsGenerator::writeEntry(const EntryType &entry, const QHash\n"; + _src << "#include \n"; if(!settings.backend) _src << "#include \n"; _src << "\n"; auto backend = settings.backend.value_or(BackendType{QStringLiteral("QtMvvm::QSettingsAccessor"), {}}); + + _src << "namespace {\n\n" + << "void __generated_settings_setup()\n" + << "{\n" + << "\tQtMvvm::ServiceRegistry::instance()->registerObject<" << settings.name.value() << ">("; + if(backend.scope) + _src << "QtMvvm::ServiceRegistry::" << backend.scope.value(); + _src << ");\n" + << "}\n\n" + << "}\n" + << "Q_COREAPP_STARTUP_FUNCTION(__generated_settings_setup)\n\n"; + _src << settings.name.value() << "::" << settings.name.value() << "(QObject *parent) : \n" << "\t" << settings.name.value() << "{new " << backend.className << "{"; if(!backend.param.isEmpty()) { @@ -330,11 +346,13 @@ void SettingsGenerator::writeSource(const SettingsType &settings) _src << settings.name.value() << "::" << settings.name.value() << "(QtMvvm::ISettingsAccessor *accessor, QObject *parent) : \n" << "\tQObject{parent},\n" << "\t_accessor{accessor}\n" - << "{}\n\n"; + << "{\n"; + writeNodeElementDefinitions(settings, settings.typeMappings, settings.baseKey); + _src << "}\n\n"; _src << settings.name.value() << " *" << settings.name.value() << "::instance()\n" << "{\n" - << "\treturn nullptr;\n" + << "\treturn QtMvvm::ServiceRegistry::instance()->service<" << settings.name.value() << ">();\n" << "}\n\n"; _src << "QtMvvm::ISettingsAccessor *" << settings.name.value() << "::accessor() const\n" @@ -342,3 +360,35 @@ void SettingsGenerator::writeSource(const SettingsType &settings) << "\treturn _accessor;\n" << "}\n\n"; } + +void SettingsGenerator::writeNodeElementDefinitions(const SettingsGeneratorBase::NodeContentGroup &node, const QHash &typeMappings, const optional &baseKey, const QStringList &keyChain) +{ + for(const auto &cNode : node.contentNodes) { + if(nonstd::holds_alternative(cNode)) { + const auto &xNode = nonstd::get(cNode); + writeNodeElementDefinitions(xNode, typeMappings, baseKey, QStringList{keyChain} << xNode.key); + } else if(nonstd::holds_alternative(cNode)) + writeEntryDefinition(nonstd::get(cNode), typeMappings, baseKey, keyChain); + else if(nonstd::holds_alternative(cNode)) + writeNodeElementDefinitions(nonstd::get(cNode), typeMappings, baseKey, keyChain); + } +} + +void SettingsGenerator::writeEntryDefinition(const SettingsGeneratorBase::EntryType &entry, const QHash &typeMappings, const optional &baseKey, QStringList keyChain) +{ + keyChain.append(entry.key); + _src << "\t" << keyChain.join(QLatin1Char('.')) + << ".setup(QStringLiteral(\"" << (baseKey ? QStringList{baseKey.value()} + keyChain : keyChain).join(QLatin1Char('/')) << "\"), _accessor"; + if(entry.code) + _src << ", QVariant::fromValue<" << typeMappings.value(entry.type, entry.type) << ">(" << entry.code.value().trimmed() << ")"; + else if(entry.defaultValue) { + _src << ", QVariant{"; + if(entry.tr) + _src << "QCoreApplication::translate(\"" << entry.trContext.value_or(QStringLiteral("qtmvvm_settings_xml")) << "\", \"" << entry.defaultValue.value() << "\")"; + else + _src << "QStringLiteral(\"" << entry.defaultValue.value() << "\")"; + _src << "}.value<" << typeMappings.value(entry.type, entry.type) << ">()"; + } + _src << ");\n"; + writeNodeElementDefinitions(entry, typeMappings, baseKey, std::move(keyChain)); +} diff --git a/tools/settingsgenerator/settingsgenerator.h b/tools/settingsgenerator/settingsgenerator.h index cae6fcc..651931d 100644 --- a/tools/settingsgenerator/settingsgenerator.h +++ b/tools/settingsgenerator/settingsgenerator.h @@ -36,11 +36,13 @@ private: NodeContentGroup *replaceNodeByEntry(NodeContentGroup *cGrp, NodeContentGroup *node, EntryType &&entry); void writeHeader(const SettingsType &settings); - void writeNodeElements(const NodeContentGroup &node, const QHash &typeMappings, int intendent = 1); - void writeNode(const NodeType &node, const QHash &typeMappings, int intendent = 1); - void writeEntry(const EntryType &entry, const QHash &typeMappings, int intendent = 1); + void writeNodeElementDeclarations(const NodeContentGroup &node, const QHash &typeMappings, int intendent = 1); + void writeNodeDeclaration(const NodeType &node, const QHash &typeMappings, int intendent = 1); + void writeEntryDeclaration(const EntryType &entry, const QHash &typeMappings, int intendent = 1); void writeSource(const SettingsType &settings); + void writeNodeElementDefinitions(const NodeContentGroup &node, const QHash &typeMappings, const optional &baseKey, const QStringList &keyChain = {}); + void writeEntryDefinition(const EntryType &entry, const QHash &typeMappings, const optional &baseKey, QStringList keyChain); }; #endif // SETTINGSGENERATOR_H