diff --git a/bin/qsettingstranslator.py b/bin/qsettingstranslator.py index b533628..88166f8 100755 --- a/bin/qsettingstranslator.py +++ b/bin/qsettingstranslator.py @@ -21,6 +21,8 @@ def create_settings_ts(infile, outfile): strings.append(elem.attrib["tooltip"]) if "tr" in elem.attrib and elem.attrib["tr"].lower() == "true": strings.append(elem.text) + if "ztr" in elem.attrib and elem.attrib["ztr"].lower() == "true": # lazy translations + strings.append(elem.text) if "trdefault" in elem.attrib and elem.attrib["trdefault"].lower() == "true": strings.append(elem.attrib["default"]) # legaxy keys, because of a typo... diff --git a/examples/mvvmcore/SampleCore/settings.xml b/examples/mvvmcore/SampleCore/settings.xml index d089987..8a034c5 100644 --- a/examples/mvvmcore/SampleCore/settings.xml +++ b/examples/mvvmcore/SampleCore/settings.xml @@ -11,18 +11,21 @@ default="true"> property bool + true property Enter a nice name + Your current name: %1 + title="Open &system settings" + tooltip="Pressing this action will open the system settings"> Trigger Action You can use this to trigger whatever kind of action you need @@ -38,6 +41,7 @@ Variant B Variant C + Current value: %1 @@ -45,9 +49,11 @@ + tooltip="The value must be between 0 and 1" + default="0.0"> 0.0 1.0 + Current value: %L1 7 + Current value: %1 @@ -90,7 +97,9 @@ + default="42"> + Is set to %n unit(s) + getDelegate(entry.type, entry.properties); + auto url = _factory->getDelegate(entry.type, entry.properties); if(group.title.isEmpty()) // unnamed groups are presented first _entries.insert(rIndex++, EntryInfo{entry, url}); else @@ -83,6 +84,19 @@ QVariant SettingsEntryModel::data(const QModelIndex &index, int role) const return entry.group.title; case SearchKeysRole: return entry.searchKeys; + case PreviewRole: + if(entry.properties.contains(QStringLiteral("qtmvvm_preview"))) { + auto preview = entry.properties.value(QStringLiteral("qtmvvm_preview")); + if(preview.type() == QVariant::Bool) + return preview.toBool() ? entry.tooltip : QString{}; + else if(preview.type() == QVariant::String) { + return _factory->format(entry.type, + preview.toString(), + _viewModel->loadValue(entry.key, entry.defaultValue), + entry.properties); + } + } + return QString{}; default: return QVariant(); } @@ -101,7 +115,7 @@ bool SettingsEntryModel::setData(const QModelIndex &index, const QVariant &value return false; _viewModel->saveValue(_entries.value(index.row()).key, value); - emit dataChanged(index, index, {SettingsValueRole}); + emit dataChanged(index, index, {SettingsValueRole, PreviewRole}); return true; } @@ -115,7 +129,8 @@ QHash SettingsEntryModel::roleNames() const {ToolTipRole, "tooltip"}, {DelegateUrlRole, "delegateUrl"}, {SettingsValueRole, "inputValue"}, - {PropertiesRole, "properties"} + {PropertiesRole, "properties"}, + {PreviewRole, "preview"} }; } @@ -133,7 +148,7 @@ void SettingsEntryModel::entryChanged(const QString &key) for(auto i = 0; i < _entries.size(); i++) { if(_entries[i].key == key) { auto mIndex = index(i); - emit dataChanged(mIndex, mIndex, {SettingsValueRole}); + emit dataChanged(mIndex, mIndex, {SettingsValueRole, PreviewRole}); break; } } diff --git a/src/imports/mvvmquick/settingsentrymodel.h b/src/imports/mvvmquick/settingsentrymodel.h index eaf280a..95167fa 100644 --- a/src/imports/mvvmquick/settingsentrymodel.h +++ b/src/imports/mvvmquick/settingsentrymodel.h @@ -24,7 +24,9 @@ public: DelegateUrlRole, SettingsValueRole, PropertiesRole, - SearchKeysRole + SearchKeysRole, + + PreviewRole }; Q_ENUM(Roles) @@ -53,6 +55,7 @@ private: }; SettingsViewModel *_viewModel = nullptr; + InputViewFactory *_factory = nullptr; QList _entries; }; diff --git a/src/mvvmquick/BoolDelegate.qml b/src/mvvmquick/BoolDelegate.qml index 8e6012b..acdd4f3 100644 --- a/src/mvvmquick/BoolDelegate.qml +++ b/src/mvvmquick/BoolDelegate.qml @@ -1,6 +1,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 +import de.skycoder42.QtMvvm.Quick 1.1 CheckDelegate { id: _boolDelegate @@ -20,6 +21,11 @@ CheckDelegate { inputValue = checked; } + ToolTip.visible: pressed && tooltip != "" + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: tooltip + onPressAndHold: QuickPresenter.hapticLongPress() + contentItem: GridLayout { columns: 2 rows: 2 @@ -35,11 +41,11 @@ CheckDelegate { } Label { - id: _textLabel - visible: tooltip + id: _peviewLabel + visible: preview Layout.row: 1 Layout.column: 0 - text: tooltip + text: preview wrapMode: Text.WordWrap Layout.fillWidth: true } diff --git a/src/mvvmquick/ListDelegate.qml b/src/mvvmquick/ListDelegate.qml index a548db6..5277820 100644 --- a/src/mvvmquick/ListDelegate.qml +++ b/src/mvvmquick/ListDelegate.qml @@ -1,6 +1,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 +import de.skycoder42.QtMvvm.Quick 1.1 ItemDelegate { id: _listDelegate @@ -9,6 +10,11 @@ ItemDelegate { signal showInput(string key, string title, string type, var defaultValue, var properties); + ToolTip.visible: pressed && tooltip != "" + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: tooltip + onPressAndHold: QuickPresenter.hapticLongPress() + contentItem: ColumnLayout { Label { id: _titleLabel @@ -19,9 +25,9 @@ ItemDelegate { } Label { - id: _textLabel - visible: tooltip - text: tooltip + id: _previewLabel + visible: preview + text: preview wrapMode: Text.WordWrap Layout.fillWidth: true } diff --git a/src/mvvmquick/MsgDelegate.qml b/src/mvvmquick/MsgDelegate.qml index 43d354b..24ea166 100644 --- a/src/mvvmquick/MsgDelegate.qml +++ b/src/mvvmquick/MsgDelegate.qml @@ -1,6 +1,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.3 +import de.skycoder42.QtMvvm.Quick 1.1 ItemDelegate { id: _msgDelegate @@ -9,6 +10,11 @@ ItemDelegate { signal showInput(string key, string title, string type, var defaultValue, var properties); + ToolTip.visible: pressed && tooltip != "" + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + ToolTip.text: tooltip + onPressAndHold: QuickPresenter.hapticLongPress() + contentItem: ColumnLayout { Label { id: _titleLabel @@ -19,9 +25,9 @@ ItemDelegate { } Label { - id: _textLabel - visible: tooltip - text: tooltip + id: _previewLabel + visible: preview + text: preview wrapMode: Text.WordWrap Layout.fillWidth: true } diff --git a/src/mvvmquick/SwitchDelegate.qml b/src/mvvmquick/SwitchDelegate.qml index 29fc15c..ce98814 100644 --- a/src/mvvmquick/SwitchDelegate.qml +++ b/src/mvvmquick/SwitchDelegate.qml @@ -1,6 +1,7 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 as Controls import QtQuick.Layouts 1.3 +import de.skycoder42.QtMvvm.Quick 1.1 Controls.SwitchDelegate { id: _boolDelegate @@ -20,6 +21,11 @@ Controls.SwitchDelegate { inputValue = checked; } + Controls.ToolTip.visible: pressed && tooltip != "" + Controls.ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + Controls.ToolTip.text: tooltip + onPressAndHold: QuickPresenter.hapticLongPress() + contentItem: GridLayout { columns: 2 rows: 2 @@ -35,11 +41,11 @@ Controls.SwitchDelegate { } Controls.Label { - id: _textLabel - visible: tooltip + id: _previewLabel + visible: preview Layout.row: 1 Layout.column: 0 - text: tooltip + text: preview wrapMode: Text.WordWrap Layout.fillWidth: true } diff --git a/src/mvvmquick/formatters_p.h b/src/mvvmquick/formatters_p.h new file mode 100644 index 0000000..50e9cc8 --- /dev/null +++ b/src/mvvmquick/formatters_p.h @@ -0,0 +1,41 @@ +#ifndef QTMVVM_FORMATTERS_P_H +#define QTMVVM_FORMATTERS_P_H + +#include + +#include "inputviewfactory.h" + +namespace QtMvvm { + +class IntFormatter : public Formatter +{ +public: + QString format(const QString &formatString, const QVariant &value, const QVariantMap &viewProperties) const override { + if(viewProperties.value(QStringLiteral("qtmvvm_ztr"), formatString.contains(QStringLiteral("%n"))).toBool()) + return QCoreApplication::translate("qtmvvm_settings_xml", qUtf8Printable(formatString), nullptr, value.toInt()); + else + return formatString.arg(value.toInt()); + } +}; + +class ListFormatter : public Formatter +{ +public: + QString format(const QString &formatString, const QVariant &value, const QVariantMap &viewProperties) const override { + if(viewProperties.contains(QStringLiteral("listElements"))) { + for(const auto &element : viewProperties.value(QStringLiteral("listElements")).toList()) { + if(element.type() != QVariant::Map) + continue; + auto eMap = element.toMap(); + if(eMap.value(QStringLiteral("value")) == value) + return formatString.arg(eMap.value(QStringLiteral("name")).toString()); + } + } + + return formatString.arg(value.toString()); + } +}; + +} + +#endif // QTMVVM_FORMATTERS_P_H diff --git a/src/mvvmquick/inputviewfactory.cpp b/src/mvvmquick/inputviewfactory.cpp index 23c5993..2b50daa 100644 --- a/src/mvvmquick/inputviewfactory.cpp +++ b/src/mvvmquick/inputviewfactory.cpp @@ -8,6 +8,8 @@ #include +#include "formatters_p.h" + using namespace QtMvvm; InputViewFactory::InputViewFactory(QObject *parent) : @@ -24,32 +26,8 @@ QUrl InputViewFactory::getInputUrl(const QByteArray &type, const QVariantMap &vi Q_UNUSED(viewProperties) if(d->inputAliases.contains(type)) url = getInputUrl(d->inputAliases.value(type), viewProperties); - if(d->simpleInputs.contains(type)) + else if(d->simpleInputs.contains(type)) url = d->simpleInputs.value(type); - else if(type == QMetaType::typeName(QMetaType::Bool)) - url = QStringLiteral("qrc:/qtmvvm/inputs/CheckBox.qml"); - else if(type == "switch") - url = QStringLiteral("qrc:/qtmvvm/inputs/Switch.qml"); - else if(type == QMetaType::typeName(QMetaType::QString) || type == "string") - url = QStringLiteral("qrc:/qtmvvm/inputs/TextField.qml"); - else if(type == QMetaType::typeName(QMetaType::Int)) - url = QStringLiteral("qrc:/qtmvvm/inputs/SpinBox.qml"); - else if(type == QMetaType::typeName(QMetaType::Double)) - url = QStringLiteral("qrc:/qtmvvm/inputs/DoubleSpinBox.qml"); -// else if(type == QMetaType::typeName(QMetaType::QDate)) -// url = QUrl(); -// else if(type == QMetaType::typeName(QMetaType::QTime)) -// url = QUrl(); -// else if(type == QMetaType::typeName(QMetaType::QDateTime) || type == "date") -// url = QUrl(); - else if(type == QMetaType::typeName(QMetaType::QFont)) - url = QStringLiteral("qrc:/qtmvvm/inputs/FontEdit.qml"); - else if(type == QMetaType::typeName(QMetaType::QUrl) || type == "url") - url = QStringLiteral("qrc:/qtmvvm/inputs/UrlField.qml"); - else if(type == "selection" || type == "list") - url = QStringLiteral("qrc:/qtmvvm/inputs/ListEdit.qml"); - else if(type == "radiolist") - url = QStringLiteral("qrc:/qtmvvm/inputs/RadioListEdit.qml"); else { logCritical() << "Failed to find any input view for input type:" << type; return QUrl(); @@ -66,12 +44,8 @@ QUrl InputViewFactory::getDelegate(const QByteArray &type, const QVariantMap &vi Q_UNUSED(viewProperties) if(d->delegateAliases.contains(type)) url = getDelegate(d->delegateAliases.value(type), viewProperties); - if(d->simpleDelegates.contains(type)) + else if(d->simpleDelegates.contains(type)) url = d->simpleDelegates.value(type); - else if(type == QMetaType::typeName(QMetaType::Bool)) - url = QStringLiteral("qrc:/qtmvvm/delegates/BoolDelegate.qml"); - else if(type == "switch") - url = QStringLiteral("qrc:/qtmvvm/delegates/SwitchDelegate.qml"); else if((type == "selection" || type == "list") && !viewProperties.value(QStringLiteral("editable"), false).toBool()) url = QStringLiteral("qrc:/qtmvvm/delegates/ListDelegate.qml"); @@ -82,6 +56,16 @@ QUrl InputViewFactory::getDelegate(const QByteArray &type, const QVariantMap &vi return url; } +QString QtMvvm::InputViewFactory::format(const QByteArray &type, const QString &formatString, const QVariant &value, const QVariantMap &viewProperties) +{ + if(d->formatterAliases.contains(type)) + return format(d->formatterAliases.value(type), formatString, value, viewProperties); + else if(d->formatters.contains(type)) + return d->formatters.value(type)->format(formatString, value, viewProperties); + else + return formatString.arg(value.toString()); +} + void InputViewFactory::addSimpleInput(const QByteArray &type, const QUrl &qmlFileUrl) { d->simpleInputs.insert(type, qmlFileUrl); @@ -92,6 +76,11 @@ void InputViewFactory::addSimpleDelegate(const QByteArray &type, const QUrl &qml d->simpleDelegates.insert(type, qmlFileUrl); } +void InputViewFactory::addFormatter(const QByteArray &type, Formatter *formatter) +{ + d->formatters.insert(type, QSharedPointer{formatter}); +} + void InputViewFactory::addInputAlias(const QByteArray &alias, const QByteArray &targetType) { d->inputAliases.insert(alias, targetType); @@ -101,3 +90,49 @@ void InputViewFactory::addDelegateAlias(const QByteArray &alias, const QByteArra { d->delegateAliases.insert(alias, targetType); } + +void QtMvvm::InputViewFactory::addFormatterAlias(const QByteArray &alias, const QByteArray &targetType) +{ + d->formatterAliases.insert(alias, targetType); +} + + + +Formatter::Formatter() = default; + +Formatter::~Formatter() = default; + + + +InputViewFactoryPrivate::InputViewFactoryPrivate() : + simpleInputs{ + {QMetaType::typeName(QMetaType::Bool), QStringLiteral("qrc:/qtmvvm/inputs/CheckBox.qml")}, + {"switch", QStringLiteral("qrc:/qtmvvm/inputs/Switch.qml")}, + {QMetaType::typeName(QMetaType::QString), QStringLiteral("qrc:/qtmvvm/inputs/TextField.qml")}, + {"string", QStringLiteral("qrc:/qtmvvm/inputs/TextField.qml")}, + {QMetaType::typeName(QMetaType::Int), QStringLiteral("qrc:/qtmvvm/inputs/SpinBox.qml")}, + {QMetaType::typeName(QMetaType::Double), QStringLiteral("qrc:/qtmvvm/inputs/DoubleSpinBox.qml")}, +// {QMetaType::typeName(QMetaType::QDate), QStringLiteral("qrc:/qtmvvm/inputs/.qml")}, +// {QMetaType::typeName(QMetaType::QTime), QStringLiteral("qrc:/qtmvvm/inputs/.qml")}, +// {QMetaType::typeName(QMetaType::QDateTime), QStringLiteral("qrc:/qtmvvm/inputs/.qml")}, +// {"date", QStringLiteral("qrc:/qtmvvm/inputs/.qml")}, + {QMetaType::typeName(QMetaType::QFont), QStringLiteral("qrc:/qtmvvm/inputs/FontEdit.qml")}, + {QMetaType::typeName(QMetaType::QUrl), QStringLiteral("qrc:/qtmvvm/inputs/UrlField.qml")}, + {"selection", QStringLiteral("qrc:/qtmvvm/inputs/ListEdit.qml")}, + {"list", QStringLiteral("qrc:/qtmvvm/inputs/ListEdit.qml")}, + {"radiolist", QStringLiteral("qrc:/qtmvvm/inputs/RadioListEdit.qml")} + }, + simpleDelegates{ + {QMetaType::typeName(QMetaType::Bool), QStringLiteral("qrc:/qtmvvm/delegates/BoolDelegate.qml")}, + {"switch", QStringLiteral("qrc:/qtmvvm/inputs/SwitchDelegate.qml")} + }, + formatters{ + {QMetaType::typeName(QMetaType::Int), QSharedPointer::create()}, + {QMetaType::typeName(QMetaType::Double), QSharedPointer>::create()}, + } +{ + auto listFormatter = QSharedPointer::create(); + formatters.insert("selection", listFormatter); + formatters.insert("list", listFormatter); + formatters.insert("radiolist", listFormatter); +} diff --git a/src/mvvmquick/inputviewfactory.h b/src/mvvmquick/inputviewfactory.h index a0bd5bf..96f359f 100644 --- a/src/mvvmquick/inputviewfactory.h +++ b/src/mvvmquick/inputviewfactory.h @@ -5,11 +5,34 @@ #include #include #include +#include #include "QtMvvmQuick/qtmvvmquick_global.h" namespace QtMvvm { +class Q_MVVMQUICK_EXPORT Formatter +{ + Q_DISABLE_COPY(Formatter) + +public: + Formatter(); + virtual ~Formatter(); + + virtual QString format(const QString &formatString, + const QVariant &value, + const QVariantMap &viewProperties) const = 0; +}; + +template +class SimpleFormatter : public Formatter +{ +public: + inline QString format(const QString &formatString, + const QVariant &value, + const QVariantMap &viewProperties) const override; +}; + class InputViewFactoryPrivate; //! A factory class to generate input edit views by their type names class Q_MVVMQUICK_EXPORT InputViewFactory : public QObject @@ -26,6 +49,11 @@ public: //! Find the input list delegate URL of the given input type Q_INVOKABLE virtual QUrl getDelegate(const QByteArray &type, const QVariantMap &viewProperties); + Q_REVISION(1) Q_INVOKABLE QString format(const QByteArray &type, + const QString &formatString, + const QVariant &value, + const QVariantMap &viewProperties); + //! Adds a new QML file to create views for the given type template inline void addSimpleInput(const QUrl &qmlFileUrl); @@ -38,6 +66,10 @@ public: //! @copybrief addSimpleDelegate(const QUrl &) Q_INVOKABLE virtual void addSimpleDelegate(const QByteArray &type, const QUrl &qmlFileUrl); + template + inline void addFormatter(Formatter *formatter); + void addFormatter(const QByteArray &type, Formatter *formatter); + //! Adds a type name alias for views template inline void addInputAlias(); @@ -50,6 +82,10 @@ public: //! @copybrief addDelegateAlias() Q_INVOKABLE virtual void addDelegateAlias(const QByteArray &alias, const QByteArray &targetType); + template + inline void addFormatterAlias(); + Q_REVISION(1) Q_INVOKABLE void addFormatterAlias(const QByteArray &alias, const QByteArray &targetType); + private: QScopedPointer d; }; @@ -66,6 +102,12 @@ void InputViewFactory::addSimpleDelegate(const QUrl &qmlFileUrl) addSimpleDelegate(QMetaType::typeName(qMetaTypeId()), qmlFileUrl); } +template +void InputViewFactory::addFormatter(Formatter *formatter) +{ + addFormatter(QMetaType::typeName(qMetaTypeId()), formatter); +} + template inline void InputViewFactory::addInputAlias() { @@ -78,6 +120,21 @@ void InputViewFactory::addDelegateAlias() addDelegateAlias(QMetaType::typeName(qMetaTypeId()), QMetaType::typeName(qMetaTypeId())); } +template +void InputViewFactory::addFormatterAlias() +{ + addFormatterAlias(QMetaType::typeName(qMetaTypeId()), QMetaType::typeName(qMetaTypeId())); +} + + + +template +QString SimpleFormatter::format(const QString &formatString, const QVariant &value, const QVariantMap &viewProperties) const +{ + Q_UNUSED(viewProperties); + return formatString.arg(value.template value()); +} + } #endif // QTMVVM_INPUTVIEWFACTORY_H diff --git a/src/mvvmquick/inputviewfactory_p.h b/src/mvvmquick/inputviewfactory_p.h index 55901d8..052f961 100644 --- a/src/mvvmquick/inputviewfactory_p.h +++ b/src/mvvmquick/inputviewfactory_p.h @@ -9,10 +9,14 @@ namespace QtMvvm { class InputViewFactoryPrivate { public: + InputViewFactoryPrivate(); + QHash simpleInputs; QHash simpleDelegates; + QHash> formatters; QHash inputAliases; QHash delegateAliases; + QHash formatterAliases; }; } diff --git a/src/mvvmquick/mvvmquick.pro b/src/mvvmquick/mvvmquick.pro index f920d71..29d5c06 100644 --- a/src/mvvmquick/mvvmquick.pro +++ b/src/mvvmquick/mvvmquick.pro @@ -7,7 +7,8 @@ HEADERS += \ quickpresenter.h \ quickpresenter_p.h \ inputviewfactory.h \ - inputviewfactory_p.h + inputviewfactory_p.h \ + formatters_p.h SOURCES += \ quickpresenter.cpp \