diff --git a/deploy.json b/deploy.json index fa7d357..bd3f8b3 100644 --- a/deploy.json +++ b/deploy.json @@ -20,5 +20,8 @@ "installs": { "ProjectTemplate": "Tools/QtCreator/share/qtcreator/templates/wizards/projects/qtmvvm", "qbs/Qt/settingsgenerator": "Tools/QtCreator/share/qtcreator/qbs/share/qbs/modules/Qt/settingsgenerator" - } + }, + "hostbuilds": [ + "bin/qsettingsgenerator*" + ] } diff --git a/doc/Doxyfile b/doc/Doxyfile index 23ba9f6..c99a0fa 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -999,7 +999,9 @@ EXCLUDE_SYMBOLS = QtMvvm::__helpertypes \ # command). EXAMPLE_PATH = ../examples \ - ./snippets + ./snippets \ + ../src/settingsconfig \ + ../tools/settingsgenerator # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and diff --git a/doc/qsettingsgenerator.dox b/doc/qsettingsgenerator.dox new file mode 100644 index 0000000..e8d5d51 --- /dev/null +++ b/doc/qsettingsgenerator.dox @@ -0,0 +1,336 @@ +/*! +@page settings_generator The qsettingsgenerator Tool +@brief A tool to generate static, typesafe settings access without having to type key strings + +@tableofcontents + +The general idea is that instead of this: +@code{.cpp} +QSettins settings; +int value = settings.value("my/key", 42).toInt(); +@endcode + +You create a settings file like this: +@code{.xml} + + + + + +@endcode + +And can now access the data like this: +@code{.cpp} +int value = Settings::instance()->my.key; +@endcode + +So in summary: First, using QSettings requires you to type in +strings as keys - which cannot be checked by the compiler. With this tool you get a c++ member +hirachie instead - which is checked by the compiler and thus typo free. Also, the values in +the settings are made statically typed by using this generator. The second point was to support +multiple backends, not only QSettings, without having to change the frontend code. + +@section settings_generator_features Features +- Access settings via code - no more typos and redundant strings! +- Static typing gives additionl saftey and is easier to use (no `.toType()` calls needed) +- Default values in one single, central place - less error prone and easier to maintain + - Can be specified as strings in the xml + - Can be specified as C++ code in the xml +- Use as a global single instance or create local instances +- Support for custom settings backends instead of QSettings alone + - QtMvvm::QSettingsAccessor for usage with QSettings (the default) + - QtMvvm::DataSyncSettingsAccessor to sync settings via [QtDataSync](https://github.com/Skycoder42/QtDataSync) + - QtMvvm::AndroidSettingsAccessor (android only) to access the SharedPreferences +- Can be used as an injectable service + +@section settings_generator_usage Usage +Using QtMvvmCore adds a custom qmake compiler to you project. All you need to do is to create the settings xml +definition and then add it to the pro file as `SETTINGS_DEFINITIONS += mysettings.xml`. This will create a header +named "mysettings.h" you can include. That header contains the C++ generated settings class to access the settings. + +@subsection settings_generator_usage_example Example +Create a pro file with and add the lines: +@code{.pro} +QT += mvvmcore + +SETTINGS_DEFINITIONS += mysettings.xml +@endcode + +Create a file named `mysettings.xml` and fill it: +@code{.xml} + + + + + + +@endcode + +Finally, adjust you main to look like this: +@code{.cpp} +#include +#include +#include "mysettings.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + int myKey = MySettings::instance()->my.key; + qDebug() << myKey; + + return 0; +} +@endcode + +@section settings_generator_qml QML-Bindings +The tool can also generate QML-Bindings, so you can access the settings in the same way from +QML. To do so, add to your pro file (in addition to `SETTINGS_DEFINITIONS`): +@code{.pro} +QML_SETTINGS_DEFINITIONS += mysettings.xml +@endcode + +And also add some QML metadata using the Qml element in the settings +@code{.xml} + + + + + +@endcode + +And with that, you can use the specified uri to access the settings from QML: +@code{.qml} +import com.example.settings 1.0 + +SomeItem { + someProperty: MySettings.my.key +} +@endcode + +@section settings_generator_format XML-Format Documentation +The following sections document the different elements and attributes in the XML format used +by the generator. + +@subsection settings_generator_elements Elements +The possible elements of such a file. + +@subsubsection settings_generator_elements_Settings Settings +The root Element of the XML document. It defines meta-stuff and the actual nodes and entries. + +@paragraph settings_generator_elements_Settings_attributes Attributes + Name | Type | Default/Required | Description +------------|-----------------------------------------------|-----------------------|------------- + name | string | _filename_ | The C++ class name of the generated class + prefix | string | _empty_ | Specify an export macro. The value is put before the class definition, if you want to export the generated class from a dynamic library + baseKey | string | _empty_ | A base settings key that is prepended to all keys + scope | @ref settings_generator_values_InstanceScope | DestroyOnAppDestroy | The destruction scope of the create service instance + +@paragraph settings_generator_elements_Settings_children Child elements + Name | XML-Type | Limits | Description +----------------|-----------------------------------------------|-----------|------------- + Include | @ref settings_generator_elements_Include | 0 - ∞ | A header file to be included + Backend | @ref settings_generator_elements_Backend | 0 - 1 | The definition of the settings backend to use + Qml | @ref settings_generator_elements_Qml | 0 - 1 | Special configuration parameters for the QML bindings + TypeMapping | @ref settings_generator_elements_TypeMapping | 0 - ∞ | Type mappings to map abstract type names to C++ types + <node> | @ref settings_generator_elements_NodeContent | 0 - ∞ | General "node-like" elements, that can be inside any node-like element + +@subsubsection settings_generator_elements_Include Include +Describes a file to be included via a c++ include directive. You can use them if you want to +make use of special classes in your generated classes. Both global includes (#include
) +and local includes (#include "header") are supported.
+ +@paragraph settings_generator_elements_Include_attributes Attributes + Name | Type | Default/Required | Description +--------|-------|-------------------|------------- + local | bool | false | Specifies whether the include is a global or a local include + +@paragraph settings_generator_elements_Include_children Content +The content of this element must be a string. More specific the header to be included. It +becomes the content of the include directive. Specifiy it without the braces or quores, as thoes +are added automatically. + +@subsubsection settings_generator_elements_Backend Backend +The backend element can be used to select a different QtMvvm::ISettingsAccessor instead of the +standard QtMvvm::QSettingsAccessor. + +@paragraph settings_generator_elements_Backend_attributes Attributes + Name | Type | Default/Required | Description +--------|-----------|-------------------|------------- + class | string | _required_ | The C++ class name of the backend to be used. Must be a class that implements QtMvvm::ISettingsAccessor + +@paragraph settings_generator_elements_Backend_children Child elements + Name | XML-Type | Limits | Description +--------|-------------------------------------------|-----------|------------- + Param | @ref settings_generator_elements_Param | 0 - ∞ | A parameter to be passed to the backends constructor, before the QObject* parent + +@subsubsection settings_generator_elements_Param Param +A generic parameter to be passed to C++ method. + +@paragraph settings_generator_elements_Param_attributes Attributes + Name | Type | Default/Required | Description +--------|-----------|-------------------|------------- + type | string | _required_ | The C++ class name of type of the parameter. + asStr | bool | false | Specify how the element content should be interpreted + +@paragraph settings_generator_elements_Param_children Content +The content of a param represents the value of the parameter. How the content is interpreted +depends on the asStr attribute. + +If it is set to false (the default), the content must be c++ code and is copied to the generated +class as is. The code must be an expression that evalutes to a single value that is implicitly +convertible to the given type. If type was for example int, valid expressions could be: + +@code{.xml} +-1 +10 + 20 +qRand() +@endcode + +If set to true, the content is assumed to be a string. You don't need to specify quotation +marks around the string. That string is used to initialize a QVariant that is then converted to +type - in other words, the type you use must be variant convertible from a string. This can be +used to, for example, create a QDate from a string with the value of +`2018-05-09`.
+ +@subsubsection settings_generator_elements_Qml Qml +The Qml element contains special properties that are only used for QML binding generation. + +@paragraph settings_generator_elements_Qml_attributes Attributes + Name | Type | Default/Required | Description +------------|-------------------------------------------------------|-------------------|------------- + uri | string | _required_ | The QML-Import URI for the QML module + major | int | 1 | The major version of the generated module + minor | int | 0 | The minor version of the generated module + type | @ref settings_generator_values_QmlRegistrationMode | Singleton | The mode on how to register the instance in QML + register | bool | true | Specify, if the type should be registered automatically + header | string | _empty_ | A custom path to the generate C++-Code of the settings generator, if it can't be the default path + +@subsubsection settings_generator_elements_TypeMapping TypeMapping +Type mappings allow you to specify the C++-type of an arbitrary type to be replaced by in the +generated code. This can be useful when importing a @ref settings_xml "Settings-XML" file. + +@paragraph settings_generator_elements_TypeMapping_attributes Attributes + Name | Type | Default/Required | Description +--------|-----------|-------------------|------------- + key | string | _required_ | The fake type name + type | string | _required_ | The C++-type to replace it with + +@subsubsection settings_generator_elements_NodeContent NodeContent Elements +Elements that have the NodeContent as child type can have a combination of the following +actual child elements. Can can be unordered and mixed in any way: + +- @ref settings_generator_elements_Node +- @ref settings_generator_elements_Entry +- @ref settings_generator_elements_ListNode +- @ref settings_generator_elements_Import + +@subsubsection settings_generator_elements_Node Node +The node represents a sub-group within the settings. All elements within the node will have +the node's key prepended to their key (seperated by a `/`) + +@paragraph settings_generator_elements_Node_attributes Attributes + Name | Type | Default/Required | Description +--------|-----------|-------------------|------------- + key | string | _required_ | The name (and group key) of the node + +@paragraph settings_generator_elements_Node_children Child elements + Name | XML-Type | Limits | Description +----------------|-----------------------------------------------|-----------|------------- + <node> | @ref settings_generator_elements_NodeContent | 0 - ∞ | General "node-like" elements, that can be inside any node-like element + +@subsubsection settings_generator_elements_Entry Entry +The Entry element is an extension of the @ref settings_generator_elements_Node type. This +means it's the same as a Node, but with additional properties. + +The entry represents a leaf entry in the settings, with a value that can be loaded and stored +from the settings via the entry's key (within the node subgroups). Entries themselves can also +be used as a node as well, and thus contain child elements, too. + +The default value can be specified in 2 ways. Either as a string (via the attribute) that is +converted to the target type using QVariant, or via the `` child element, that contains +actual C++-Code that evaluates to a default value. + +@sa QtMvvm::SettingsEntry + +@paragraph settings_generator_elements_Entry_attributes Attributes + Name | Type | Default/Required | Description +------------|-----------|-------------------|------------- +type | string | _required_ | The C++-type that this entry saves and loads. Can be a virtual type, that is resolved by a @ref settings_generator_elements_TypeMapping +qmlGroupKey | string | `Group` | The name the property that holds the entries sub-nodes for the QML binding +default | string | _empty_ | A default value (as string) to be returned if the entry is not stored in the settings +tr | bool | false | Specify whether the default value should be translated +trContext | string | _filename_ | A custom translation context to use instead of the filename + +@paragraph settings_generator_elements_Entry_children Additional Child elements + Name | XML-Type | Limits | Description +--------|---------------------------------------|-----------|------------- + Code | @ref settings_generator_elements_Code | 0 - 1 | C++-Code to create a default value + +@subsubsection settings_generator_elements_ListNode ListNode +The ListNode element is an extension of the @ref settings_generator_elements_Node type. This +means it's the same as a Node, but with additional properties. + +The ListNode is a special node that allows you to easily store lists of elements. All children +that are defined within a listnode are part of the "array elements", so you will access +elements in a list node via `listNode[2].group.entry` and can append and remove elements from +that list. + +@sa QtMvvm::SettingsListNode + +@subsubsection settings_generator_elements_ImportType ImportType +The Imports allow you to include other files within this one as sub elements. You can include +either a Settings-Generator-XML (this file) or a @ref settings_xml "Settings-XML". The +imported file is then treated like it's contents where simply defined instead of the import +element, allowing seemless combination of different files. + +You can use the rootNode to instead of importing the root element in the importet file, go +down to the node that matches the key defined by rootNode and only import that part. + +@paragraph settings_generator_elements_ImportType_attributes Attributes + Name | Type | Default/Required | Description +------------|-----------|-------------------|------------- + required | bool | true | Specify, if the import is required or optional + rootNode | string | _empty_ | A root node withing the imported file to use as a starting point + +@paragraph settings_generator_elements_ImportType_children Content +The content of the element is a string - the path to the file to be imported. If the path is +a relative path, it is resolved relative to the file that is importing it, aka "this" file. + +@subsection settings_generator_values XML-Types +The XML-Types are not elements, but values of attributes etc. that have been defined for the +file. + +@subsubsection settings_generator_values_InstanceScope InstanceScope +InstanceScope is a simple enum with the following allowed values: + + Name | Description +----------------------------|------------- + DestroyOnAppQuit | Sets the scope of the created mvvm service to QtMvvm::ServiceRegistry::DestroyOnAppQuit + DestroyOnAppDestroy | Sets the scope of the created mvvm service to QtMvvm::ServiceRegistry::DestroyOnAppDestroy + DestroyOnRegistryDestroy | Sets the scope of the created mvvm service to QtMvvm::ServiceRegistry::DestroyOnRegistryDestroy + DestroyNever | Sets the scope of the created mvvm service to QtMvvm::ServiceRegistry::DestroyNever + +@subsubsection settings_generator_values_QmlRegistrationMode QmlRegistrationMode +InstanceScope is a simple enum with the following allowed values: + + Name | Description +----------------|------------- + Singleton | Register the QML type as a singleton instance + Uncreatable | Register the QML type as a uncreatable type + Creatable | Register the QML type as a normal, constructable type + +@section settings_generator_sample Sample settings generator XML file +The following code block is a sample of a settings generator XML file. It's a little bigger to +show of the capabilities + +@include generatortest.xml + +@section settings_xml_xsd The XSD for the settings files +The following file is the XSD the parser operates on. You can use it to verify your settings +xml files. + +@include qsettingsgenerator.xsd +*/ diff --git a/doc/settingsxml.dox b/doc/settingsxml.dox index 3398a87..96deca8 100644 --- a/doc/settingsxml.dox +++ b/doc/settingsxml.dox @@ -9,18 +9,24 @@ QtMvvm::ISettingsSetupLoader. The files are read via that service and mapped to QtMvvm::SettingsElements::Setup. @section settings_xml_translator Translations -All the settings files you have can be easily translated via a standard ts/qm file. In order for -this to work, all you have to do is add the following lines to your pro file: +All the settings files you have can be easily translated via a standard ts/qm file. The following +explains how exactly. + +The mvvm core module comes with a special make taget, called `lupdate` that will automatically +run lupdate when your sources change to update the TRANSLATIONS files. To not run this automatically, +call qmake with `qmake CONFIG+=no_auto_lupdate ...`. In both cases, you can always run `make lupdate` +to explicitly update the dependencies. + +With this step enabled, the only thing left to do is to add the settings xml file to the translation +targets so it gets included as a source when running the lupdate target. To do so, simply add it +to the `SETTINGS_TRANSLATIONS` qmake variable: @code{.pro} -QTMVVM_TS_SETTINGS += settings.xml -_never_true_condition: SOURCES += $$files($$PWD/.ts-dummy/*) -PRE_TARGETDEPS += qtmvvm-tsgen #optional +SETTINGS_TRANSLATIONS += settings.xml @endcode -This will automatically generate a dummy cpp file in the `.ts-dummy` folder for each file -added to the `QTMVVM_TS_SETTINGS` variable. These files contain dummy code that will then be -read by lupdate automatically and added to all your ts files. +This will internally generate a cpp file that is parsed and included in lupdate when running it +via the builtin lupdate target. @section settings_xml_elements Elements The possible elements of such a file @@ -162,6 +168,7 @@ long as for the last two their type is `object` key | string | Required | no | The key of the property type | @ref settings_xml_types_type | Required | no | The type of the properties value tr | bool | `false` | no | Specify whether the properties value (content) should be translated. Does not apply to `list` or `object` types + ztr | bool | `false` | no | Like tr, but does not translate the text, only generate the translation in the ts file @subsubsection settings_xml_elements_property_elements Content @@ -185,6 +192,7 @@ same. --------|-------------------------------|-------------------|---------------|------------- type | @ref settings_xml_types_type | Required | no | The type of the list element tr | bool | `false` | no | Specify whether the element value (content) should be translated. Does not apply to `list` or `object` types + ztr | bool | `false` | no | Like tr, but does not translate the text, only generate the translation in the ts file @subsubsection settings_xml_elements_element_elements Content The content depend on the `type` attribute. The following tables shows which type leads @@ -206,9 +214,11 @@ element must be a `` element (Each element that support includes as one c marked as primary). It is possible to specify an include element in another included document. @subsubsection settings_xml_elements_include_attribs Attributes - Name | Type | Default | Translated | Description -------------|-------|-----------|---------------|------------- - optional | bool | `false` | no | An optional include. If the file cannot be found it is skipped instead of aborting with an error + Name | Type | Default | Translated | Description +------------|---------------------------------------|---------------|---------------|------------- + optional | bool | `false` | no | An optional include. If the file cannot be found it is skipped instead of aborting with an error + frontends | @ref settings_xml_types_descriptor | Empty | no | @copybrief QtMvvm::SettingsElements::Entry::frontends + selectors | @ref settings_xml_types_descriptor | Empty | no | @copybrief QtMvvm::SettingsElements::Entry::selectors @subsubsection settings_xml_elements_include_elements Content The content of the include element must be a path to an XML file to be read as include. If the @@ -300,4 +310,10 @@ The following code block is a sample settings XML file. It is the same that is b the example application. @include settings.xml + +@section settings_xml_xsd The XSD for the settings files +The following file is the XSD the parser operates on. You can use it to verify your settings +xml files. + +@include settingsconfig.xsd */ diff --git a/doc/snippets/generatortest.xml b/doc/snippets/generatortest.xml new file mode 100644 index 0000000..1c681f6 --- /dev/null +++ b/doc/snippets/generatortest.xml @@ -0,0 +1,86 @@ + + + QtCore/QDateTime + QtCore/QUrl + testbackend.h + + + Test Backend + 42 + + + + + + + + + QUrl::fromLocalFile(QStringLiteral("/path/to/something")) + + + + + + + + + + + + + qRound(42.8) + + + + + + + + + + {42} + + + + + + + + + + + + + + + diff --git a/mkspecs/features/qsettingstranslator.prf b/mkspecs/features/qsettingstranslator.prf index ce645a6..ba2d1f9 100644 --- a/mkspecs/features/qsettingstranslator.prf +++ b/mkspecs/features/qsettingstranslator.prf @@ -31,7 +31,7 @@ QSETTINGSTRANSLATOR_DIR = $$QSETTINGSTRANSLATOR_DIR$$SUFFIX lupdate_trcond_c.variable_out = TRANSLATIONS_CONDITION lupdate_trcond_c.commands = echo created > ${QMAKE_FILE_OUT} lupdate_trcond_c.output = $$QSETTINGSTRANSLATOR_DIR/trcond.cpp - lupdate_trcond_c.CONFIG += combine no_link + lupdate_trcond_c.CONFIG += combine no_link explicit_dependencies QMAKE_EXTRA_COMPILERS += lupdate_trcond_c lupdate_fakepri_c.name = fakepri ${QMAKE_FILE_IN} diff --git a/src/mvvmcore/mvvmcore.pro b/src/mvvmcore/mvvmcore.pro index c590548..a705e7e 100644 --- a/src/mvvmcore/mvvmcore.pro +++ b/src/mvvmcore/mvvmcore.pro @@ -42,7 +42,7 @@ SOURCES += \ settingsconfigloader.cpp android { - #QT += androidextras + QT += androidextras HEADERS += \ androidsettingsaccessor.h \ diff --git a/tools/settingsgenerator/qsettingsgenerator.xsd b/tools/settingsgenerator/qsettingsgenerator.xsd index 8d7ce02..deb35a2 100644 --- a/tools/settingsgenerator/qsettingsgenerator.xsd +++ b/tools/settingsgenerator/qsettingsgenerator.xsd @@ -100,9 +100,7 @@ - - - +