diff --git a/examples/mvvmwidgets/SampleWidgets/widgetseventservice.cpp b/examples/mvvmwidgets/SampleWidgets/widgetseventservice.cpp index 6ec1018..35bc6aa 100644 --- a/examples/mvvmwidgets/SampleWidgets/widgetseventservice.cpp +++ b/examples/mvvmwidgets/SampleWidgets/widgetseventservice.cpp @@ -27,7 +27,7 @@ int WidgetsEventService::addEvent(const QString &name) _events.insert(_cnt, timer); connect(timer.data(), &QTimer::timeout, this, [this, name]() { - _echoService->ping(name);//TODO event triggered signal + _echoService->ping(name); }); timer->start(1000); diff --git a/src/mvvmcore/coreapp.cpp b/src/mvvmcore/coreapp.cpp index 5faa9ee..c9e2e0a 100644 --- a/src/mvvmcore/coreapp.cpp +++ b/src/mvvmcore/coreapp.cpp @@ -53,6 +53,35 @@ IPresenter *CoreApp::presenter() const return d->presenter.data(); } +void CoreApp::show(const char *viewModelName, const QVariantHash ¶ms) +{ + auto metaId = QMetaType::type(viewModelName); + auto metaObject = QMetaType::metaObjectForType(metaId); + if(!metaObject) { + throw PresenterException(QByteArrayLiteral("Given name (") + + viewModelName + + QByteArrayLiteral(") does not name a type with meta data")); + } + show(metaObject, params); +} + +void CoreApp::show(const QMetaObject *viewMetaObject, const QVariantHash ¶ms) +{ + if(!viewMetaObject->inherits(&ViewModel::staticMetaObject)) { + throw PresenterException(QByteArrayLiteral("Given type (") + + viewMetaObject->className() + + QByteArrayLiteral(") is not a class that extends QtMvvm::ViewModel")); + } + showImp(viewMetaObject, params); +} + +MessageResult *CoreApp::showDialog(const MessageConfig &config) +{ + auto result = new MessageResult(); + CoreAppPrivate::dInstance()->showDialog(config, result); + return result; +} + void CoreApp::bootApp() { if(!d->presenter) @@ -89,26 +118,13 @@ bool CoreApp::autoParse(QCommandLineParser &parser, const QStringList &arguments } } -void CoreApp::show(const char *viewModelName, const QVariantHash ¶ms) const -{ - auto metaId = QMetaType::type(viewModelName); - auto metaObject = QMetaType::metaObjectForType(metaId); - if(!metaObject) { - throw PresenterException(QByteArrayLiteral("Given name (") + - viewModelName + - QByteArrayLiteral(") does not name a type with meta data")); - } - show(metaObject, params); -} - -void CoreApp::show(const QMetaObject *viewMetaObject, const QVariantHash ¶ms) const +void CoreApp::showImp(const QMetaObject *metaObject, const QVariantHash ¶ms) { - if(!viewMetaObject->inherits(&ViewModel::staticMetaObject)) { - throw PresenterException(QByteArrayLiteral("Given type (") + - viewMetaObject->className() + - QByteArrayLiteral(") is not a class that extends QtMvvm::ViewModel")); - } - ViewModel::showImp(viewMetaObject, params, nullptr); + QMetaObject::invokeMethod(CoreAppPrivate::dInstance().data(), "showViewModel", Qt::QueuedConnection, + Q_ARG(const QMetaObject*, metaObject), + Q_ARG(const QVariantHash&, params), + Q_ARG(QPointer, nullptr), + Q_ARG(quint32, 0)); } // ------------- Private Implementation ------------- @@ -125,21 +141,12 @@ QScopedPointer &CoreAppPrivate::dInstance() return instance->d; } -void CoreAppPrivate::showViewModel(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent, quint32 requestCode) -{ - QMetaObject::invokeMethod(this, "showViewModelPrivate", Qt::QueuedConnection, - Q_ARG(const QMetaObject*, metaObject), - Q_ARG(const QVariantHash&, params), - Q_ARG(QPointer, parent), - Q_ARG(quint32, requestCode)); -} - IPresenter *CoreAppPrivate::currentPresenter() const { return presenter.data(); } -void CoreAppPrivate::showViewModelPrivate(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent, quint32 requestCode) +void CoreAppPrivate::showViewModel(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent, quint32 requestCode) { if(presenter) { QPointer vm; @@ -172,3 +179,14 @@ void CoreAppPrivate::showViewModelPrivate(const QMetaObject *metaObject, const Q << "- no presenter was set"; } } + +void CoreAppPrivate::showDialog(const MessageConfig &config, MessageResult *result) +{ + if(presenter) + presenter->showDialog(config, result); + else { + logCritical() << "Failed to show dialog ff type" + << config.type() << ":" << config.subType() + << "- no presenter was set"; + } +} diff --git a/src/mvvmcore/coreapp.h b/src/mvvmcore/coreapp.h index e810eee..e973374 100644 --- a/src/mvvmcore/coreapp.h +++ b/src/mvvmcore/coreapp.h @@ -9,6 +9,7 @@ class QCommandLineParser; #include "QtMvvmCore/qtmvvmcore_global.h" #include "QtMvvmCore/viewmodel.h" #include "QtMvvmCore/ipresenter.h" +#include "QtMvvmCore/message.h" namespace QtMvvm { @@ -28,6 +29,13 @@ public: void registerApp(); IPresenter *presenter() const; + template + static inline void show(const QVariantHash ¶ms = {}); + static void show(const char *viewModelName, const QVariantHash ¶ms = {}); + static void show(const QMetaObject *viewMetaObject, const QVariantHash ¶ms = {}); + + static MessageResult *showDialog(const MessageConfig &config); + public Q_SLOTS: void bootApp(); @@ -37,18 +45,16 @@ protected: virtual void closeApp(); bool autoParse(QCommandLineParser &parser, const QStringList &arguments); - template - inline void show(const QVariantHash ¶ms = {}) const; - void show(const char *viewModelName, const QVariantHash ¶ms = {}) const; - void show(const QMetaObject *viewMetaObject, const QVariantHash ¶ms = {}) const; private: friend class QtMvvm::CoreAppPrivate; QScopedPointer d; + + static void showImp(const QMetaObject *metaObject, const QVariantHash ¶ms); }; template -inline void CoreApp::show(const QVariantHash ¶ms) const +inline void CoreApp::show(const QVariantHash ¶ms) { static_assert(std::is_base_of::value, "TViewModel must extend QtMvvm::ViewModel"); ViewModel::showImp(&TViewModel::staticMetaObject, params, nullptr); diff --git a/src/mvvmcore/coreapp_p.h b/src/mvvmcore/coreapp_p.h index 3987270..57c9483 100644 --- a/src/mvvmcore/coreapp_p.h +++ b/src/mvvmcore/coreapp_p.h @@ -15,18 +15,15 @@ class Q_MVVMCORE_EXPORT CoreAppPrivate : public QObject public: static QScopedPointer &dInstance(); + + IPresenter *currentPresenter() const; + +public Q_SLOTS: void showViewModel(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent, quint32 requestCode); - - IPresenter *currentPresenter() const; - -private Q_SLOTS: - void showViewModelPrivate(const QMetaObject *metaObject, - const QVariantHash ¶ms, - QPointer parent, - quint32 requestCode); + void showDialog(const MessageConfig &config, MessageResult *result); private: CoreAppPrivate(); diff --git a/src/mvvmcore/ipresenter.h b/src/mvvmcore/ipresenter.h index f20edd9..1456175 100644 --- a/src/mvvmcore/ipresenter.h +++ b/src/mvvmcore/ipresenter.h @@ -5,6 +5,7 @@ #include "QtMvvmCore/qtmvvmcore_global.h" #include "QtMvvmCore/viewmodel.h" +#include "QtMvvmCore/message.h" namespace QtMvvm { @@ -30,6 +31,7 @@ public: inline virtual ~IPresenter() = default; virtual void present(ViewModel *viewModel, const QVariantHash ¶ms, QPointer parent = nullptr) = 0; + virtual void showDialog(const MessageConfig &config, MessageResult *result) = 0; }; } diff --git a/src/mvvmcore/message.cpp b/src/mvvmcore/message.cpp index 85dec4b..a36362e 100644 --- a/src/mvvmcore/message.cpp +++ b/src/mvvmcore/message.cpp @@ -1,13 +1,32 @@ #include "message.h" #include "message_p.h" +#include "coreapp.h" + +#include + using namespace QtMvvm; -MessageConfig::MessageConfig(MessageType type) : - d(new MessageConfigPrivate(type)) +const QByteArray MessageConfig::TypeMessageBox = "msgbox"; +const QByteArray MessageConfig::TypeInputDialog = "input"; +const QByteArray MessageConfig::TypeFileDialog = "file"; + +const QByteArray MessageConfig::SubTypeInformation = "information"; +const QByteArray MessageConfig::SubTypeWarning = "warning"; +const QByteArray MessageConfig::SubTypeCritical = "critical"; +const QByteArray MessageConfig::SubTypeQuestion = "question"; +const QByteArray MessageConfig::SubTypeAbout = "about"; + +const QByteArray MessageConfig::SubTypeDir = "dir"; +const QByteArray MessageConfig::SubTypeOpenFile = "open"; +const QByteArray MessageConfig::SubTypeOpenFiles = "files"; +const QByteArray MessageConfig::SubTypeSaveFile = "save"; + +MessageConfig::MessageConfig(const QByteArray &type, const QByteArray &subType) : + d(new MessageConfigPrivate(type, subType)) { - resetPositiveAction(); - resetNegativeAction(); - resetNeutralAction(); + if(subType.isEmpty()) + resetSubType(); + resetButtons(); } MessageConfig::MessageConfig(const MessageConfig &other) : @@ -22,11 +41,16 @@ MessageConfig &MessageConfig::operator=(const MessageConfig &other) return (*this); } -MessageConfig::MessageType MessageConfig::type() const +QByteArray MessageConfig::type() const { return d->type; } +QByteArray MessageConfig::subType() const +{ + return d->subType; +} + QString MessageConfig::title() const { return d->title; @@ -37,24 +61,14 @@ QString MessageConfig::text() const return d->text; } -QString MessageConfig::positiveAction() const -{ - return d->positiveAction; -} - -QString MessageConfig::negativeAction() const +MessageConfig::StandardButtons MessageConfig::buttons() const { - return d->negativeAction; + return d->buttons; } -QString MessageConfig::neutralAction() const +QHash MessageConfig::buttonTexts() const { - return d->neutralAction; -} - -QByteArray MessageConfig::inputType() const -{ - return d->inputType; + return d->buttonTexts; } QVariant MessageConfig::defaultValue() const @@ -62,113 +76,174 @@ QVariant MessageConfig::defaultValue() const return d->defaultValue; } -QVariantMap MessageConfig::editProperties() const +QVariantMap MessageConfig::viewProperties() const { return d->editProperties; } -void MessageConfig::setType(MessageConfig::MessageType type) +void MessageConfig::setType(const QByteArray &type) { d->type = type; - resetPositiveAction(); - resetNegativeAction(); - resetNeutralAction(); } -void MessageConfig::setTitle(QString title) +void MessageConfig::setSubType(const QByteArray &subType) { - d->title = title; + d->subType = subType; } -void MessageConfig::setText(QString text) +void MessageConfig::setTitle(const QString &title) { - d->text = text; + d->title = title; } -void MessageConfig::setPositiveAction(QString positiveAction) +void MessageConfig::setText(const QString &text) { - d->positiveAction = positiveAction; + d->text = text; } -void MessageConfig::setNegativeAction(QString negativeAction) +void MessageConfig::setButtons(StandardButtons buttons) { - d->negativeAction = negativeAction; + d->buttons = buttons; } -void MessageConfig::setNeutralAction(QString neutralAction) +void MessageConfig::setButtonTexts(const QHash &buttonTexts) { - d->neutralAction = neutralAction; + d->buttonTexts = buttonTexts; } -void MessageConfig::setInputType(QByteArray inputType) +void MessageConfig::setButtonText(MessageConfig::StandardButton button, const QString &text) { - d->inputType = inputType; + d->buttonTexts.insert(button, text); } -void MessageConfig::setDefaultValue(QVariant defaultValue) +void MessageConfig::setDefaultValue(const QVariant &defaultValue) { d->defaultValue = defaultValue; } -void MessageConfig::setEditProperties(QVariantMap editProperties) +void MessageConfig::setViewProperties(const QVariantMap &editProperties) { d->editProperties = editProperties; } -void MessageConfig::resetPositiveAction() +void MessageConfig::setViewProperty(const QString &key, const QVariant &value) { - switch (d->type) { - case Information: - case Warning: - case Critical: - case Input: - d->positiveAction = tr("Ok"); - break; - case Question: - d->positiveAction = tr("Yes"); - break; - default: - Q_UNREACHABLE(); - break; - } + d->editProperties.insert(key, value); +} + +void MessageConfig::resetSubType() +{ + if(d->type == TypeMessageBox) + d->subType = SubTypeInformation; + else if(d->type == TypeInputDialog) + d->subType = QMetaType::typeName(QMetaType::QString); + else + d->subType.clear(); +} + +void MessageConfig::resetButtons() +{ + if(d->type == TypeMessageBox) { + if(d->subType == SubTypeQuestion) + d->buttons = Yes | No; + else if(d->subType == SubTypeAbout) + d->buttons = Close; + else + d->buttons = Ok; + } else if(d->type == TypeInputDialog) + d->buttons = Ok | Cancel; + else + d->buttons = Ok; + + d->buttonTexts.clear(); +} + + + +MessageResult::MessageResult() : + QObject(nullptr), + d(new MessageResultPrivate()) +{} + +MessageResult::~MessageResult() {} + +bool MessageResult::hasResult() const +{ + return d->result.isValid(); +} + +QVariant MessageResult::result() const +{ + return d->result; +} + +bool MessageResult::autoDelete() const +{ + return d->autoDelete; } -void MessageConfig::resetNegativeAction() +void MessageResult::setCloseTarget(QObject *closeObject, const QMetaMethod &closeMethod) { - switch (d->type) { - case Input: - d->negativeAction = tr("Cancel"); + Q_ASSERT_X(closeObject, Q_FUNC_INFO, "closeObject must not be null"); + d->closeObject = closeObject; + d->closeMethod = closeMethod; + if(d->closeRequested) + d->closeMethod.invoke(d->closeObject, Qt::QueuedConnection); +} + +void MessageResult::complete(MessageResult::ResultType result) +{ + switch (result) { + case PositiveResult: + emit positiveAction(); break; - case Question: - d->negativeAction = tr("No"); + case NegativeResult: + emit negativeAction(); break; - case Information: - case Warning: - case Critical: - d->negativeAction.clear(); + case NeutralResult: + emit neutralAction(); break; default: Q_UNREACHABLE(); - break; } + + emit anyAction(result); + + if(d->autoDelete) + deleteLater(); } -void MessageConfig::resetNeutralAction() +void MessageResult::discardMessage() { - d->neutralAction.clear(); + if(d->closeObject && !d->closeRequested) + d->closeMethod.invoke(d->closeObject, Qt::QueuedConnection); + d->closeRequested = true; +} + +void MessageResult::setResult(QVariant result) +{ + d->result = result; +} + +void MessageResult::setAutoDelete(bool autoDelete) +{ + if (d->autoDelete == autoDelete) + return; + + d->autoDelete = autoDelete; + emit autoDeleteChanged(autoDelete); } // ------------- Private Implementation ------------- -QtMvvm::MessageConfigPrivate::MessageConfigPrivate(MessageConfig::MessageType type) : +QtMvvm::MessageConfigPrivate::MessageConfigPrivate(const QByteArray &type, const QByteArray &subType) : QSharedData(), type(type), + subType(subType), title(), text(), - positiveAction(), - negativeAction(), - neutralAction(), - inputType(), + buttons(MessageConfig::Ok), + buttonTexts(), defaultValue(), editProperties() {} @@ -176,12 +251,325 @@ QtMvvm::MessageConfigPrivate::MessageConfigPrivate(MessageConfig::MessageType ty QtMvvm::MessageConfigPrivate::MessageConfigPrivate(const QtMvvm::MessageConfigPrivate &other) : QSharedData(other), type(other.type), + subType(other.subType), title(other.title), text(other.text), - positiveAction(other.positiveAction), - negativeAction(other.negativeAction), - neutralAction(other.neutralAction), - inputType(other.inputType), + buttons(other.buttons), + buttonTexts(other.buttonTexts), defaultValue(other.defaultValue), editProperties(other.editProperties) {} + + + +MessageResultPrivate::MessageResultPrivate() : + closeObject(nullptr), + closeMethod(), + closeRequested(false), + result(), + autoDelete(true) +{} + +// ------------- Namespace methods implementation ------------- + +MessageResult *QtMvvm::information(const QString &title, const QString &text, const QString &okText) +{ + MessageConfig config(MessageConfig::TypeMessageBox, MessageConfig::SubTypeInformation); + config.setTitle(title); + config.setText(text); + if(!okText.isNull()) + config.setButtonText(MessageConfig::Ok, okText); + return CoreApp::showDialog(config); +} + +void QtMvvm::information(const QString &title, const QString &text, QObject *scope, std::function onResult, const QString &okText) +{ + auto result = information(title, text, okText); + if(result) { + QObject::connect(result, &MessageResult::anyAction, + scope, onResult, + Qt::QueuedConnection); + } +} + +void QtMvvm::information(const QString &title, const QString &text, std::function onResult, const QString &okText) +{ + information(title, text, CoreApp::instance(), onResult, okText); +} + +MessageResult *QtMvvm::question(const QString &title, const QString &text, const QString &yesText, const QString &noText) +{ + MessageConfig config(MessageConfig::TypeMessageBox, MessageConfig::SubTypeQuestion); + config.setTitle(title); + config.setText(text); + if(!yesText.isNull()) + config.setButtonText(MessageConfig::Yes, yesText); + if(!noText.isNull()) + config.setButtonText(MessageConfig::No, noText); + return CoreApp::showDialog(config); +} + +void QtMvvm::question(const QString &title, const QString &text, QObject *scope, std::function onResult, const QString &yesText, const QString &noText) +{ + auto result = question(title, text, yesText, noText); + if(result) { + QObject::connect(result, &MessageResult::anyAction, + scope, [onResult](MessageResult::ResultType type) { + onResult(type == MessageResult::PositiveResult); + }, Qt::QueuedConnection); + } +} + +void QtMvvm::question(const QString &title, const QString &text, std::function onResult, const QString &yesText, const QString &noText) +{ + question(title, text, CoreApp::instance(), onResult, yesText, noText); +} + +MessageResult *QtMvvm::warning(const QString &title, const QString &text, const QString &okText) +{ + MessageConfig config(MessageConfig::TypeMessageBox, MessageConfig::SubTypeWarning); + config.setTitle(title); + config.setText(text); + if(!okText.isNull()) + config.setButtonText(MessageConfig::Ok, okText); + return CoreApp::showDialog(config); +} + +void QtMvvm::warning(const QString &title, const QString &text, QObject *scope, std::function onResult, const QString &okText) +{ + auto result = warning(title, text, okText); + if(result) { + QObject::connect(result, &MessageResult::anyAction, + scope, onResult, + Qt::QueuedConnection); + } +} + +void QtMvvm::warning(const QString &title, const QString &text, std::function onResult, const QString &okText) +{ + warning(title, text, CoreApp::instance(), onResult, okText); +} + +MessageResult *QtMvvm::critical(const QString &title, const QString &text, const QString &okText) +{ + MessageConfig config(MessageConfig::TypeMessageBox, MessageConfig::SubTypeCritical); + config.setTitle(title); + config.setText(text); + if(!okText.isNull()) + config.setButtonText(MessageConfig::Ok, okText); + return CoreApp::showDialog(config); +} + +void QtMvvm::critical(const QString &title, const QString &text, QObject *scope, std::function onResult, const QString &okText) +{ + auto result = critical(title, text, okText); + if(result) { + QObject::connect(result, &MessageResult::anyAction, + scope, onResult, + Qt::QueuedConnection); + } +} + +void QtMvvm::critical(const QString &title, const QString &text, std::function onResult, const QString &okText) +{ + critical(title, text, CoreApp::instance(), onResult, okText); +} + +MessageResult *QtMvvm::about(const QString &description, const QUrl &websiteUrl, const QString &licenseName, const QUrl &licenseUrl, const QString &companyName, bool addQtVersion, const QStringList &extraTopInfos, const QString &extraBottomInfos) +{ + static const QString pBegin = QStringLiteral("

"); + static const QString pEnd = QStringLiteral("

"); + static const QString br = QStringLiteral("
"); + + MessageConfig config(MessageConfig::TypeMessageBox, MessageConfig::SubTypeAbout); + config.setViewProperty(QStringLiteral("addQtVersion"), addQtVersion); + + config.setTitle(MessageConfig::tr("%1 — Version %2") + .arg(QGuiApplication::applicationDisplayName()) + .arg(QCoreApplication::applicationVersion())); + + //create the content string: + //basic text + QString text = pBegin + description + pEnd; + //qt version info + extra infos + if(addQtVersion || !extraTopInfos.isEmpty()) { + text += pBegin; + if(addQtVersion) { + auto runtimeVers = qVersion(); + auto compileVers = QT_VERSION_STR; + QString qtVersion; + if(qstrcmp(runtimeVers, compileVers) == 0) + qtVersion = QString::fromUtf8(runtimeVers); + else { + qtVersion = MessageConfig::tr("%1 (Built with %2)") + .arg(QString::fromUtf8(runtimeVers)) + .arg(QString::fromUtf8(runtimeVers)); + } + text += MessageConfig::tr("Qt-Version: %2") + .arg(qtVersion); + } + if(!extraTopInfos.isEmpty()) { + auto withBr = addQtVersion; + foreach(auto info, extraTopInfos){ + text += info + (withBr ? br : QString()); + withBr = true; + } + } + text += pEnd; + } + //Developer info + text += pBegin + MessageConfig::tr("Developed by: %1") + .arg(companyName.isEmpty() ? QCoreApplication::organizationName() : companyName); + if(websiteUrl.isValid()) { + text += br + MessageConfig::tr("Project Website: %2") + .arg(QString::fromUtf8(websiteUrl.toEncoded())) + .arg(websiteUrl.toString()); + } + if(!licenseName.isEmpty()) { + if(licenseUrl.isValid()) { + text += br + MessageConfig::tr("License: %2") + .arg(QString::fromUtf8(licenseUrl.toEncoded())) + .arg(licenseName); + } else { + text += br + MessageConfig::tr("License: %1") + .arg(licenseName); + } + } + text += pEnd; + //extra bottom infos + if(!extraBottomInfos.isEmpty()) + text += pBegin + extraBottomInfos + pEnd; + //set in the config + config.setText(text); + + return CoreApp::showDialog(config); +} + +MessageResult *QtMvvm::getInput(const QString &title, const QString &text, const char *inputType, const QVariant &defaultValue, const QVariantMap &viewProperties, const QString &okText, const QString &cancelText) +{ + MessageConfig config(MessageConfig::TypeInputDialog, inputType); + config.setTitle(title); + config.setText(text); + config.setDefaultValue(defaultValue); + config.setViewProperties(viewProperties); + if(!okText.isNull()) + config.setButtonText(MessageConfig::Ok, okText); + if(!cancelText.isNull()) + config.setButtonText(MessageConfig::Cancel, cancelText); + return CoreApp::showDialog(config); +} + +void QtMvvm::getInput(const QString &title, const QString &text, const char *inputType, QObject *scope, std::function onResult, const QVariant &defaultValue, const QVariantMap &viewProperties, const QString &okText, const QString &cancelText) +{ + auto result = getInput(title, text, inputType, defaultValue, viewProperties, okText, cancelText); + if(result) { + QObject::connect(result, &MessageResult::anyAction, + scope, [onResult, result](MessageResult::ResultType type) { + onResult(type == MessageResult::PositiveResult ? result->result() : QVariant()); + }, Qt::QueuedConnection); + } +} + +void QtMvvm::getInput(const QString &title, const QString &text, const char *inputType, std::function onResult, const QVariant &defaultValue, const QVariantMap &viewProperties, const QString &okText, const QString &cancelText) +{ + getInput(title, text, inputType, CoreApp::instance(), onResult, defaultValue, viewProperties, okText, cancelText); +} + +MessageResult *QtMvvm::getExistingDirectory(const QString &title, const QUrl &dir) +{ + MessageConfig config(MessageConfig::TypeFileDialog, MessageConfig::SubTypeDir); + config.setTitle(title); + config.setDefaultValue(dir); + return CoreApp::showDialog(config); +} + +void QtMvvm::getExistingDirectory(QObject *scope, std::function onResult, const QString &title, const QUrl &dir) +{ + auto result = getExistingDirectory(title, dir); + if(result) { + QObject::connect(result, &MessageResult::anyAction, + scope, [onResult, result](MessageResult::ResultType type) { + onResult(type == MessageResult::PositiveResult ? result->result().toUrl() : QUrl()); + }, Qt::QueuedConnection); + } +} + +void QtMvvm::getExistingDirectory(std::function onResult, const QString &title, const QUrl &dir) +{ + getExistingDirectory(CoreApp::instance(), onResult, title, dir); +} + +MessageResult *QtMvvm::getOpenFile(const QString &title, const QStringList &supportedMimeTypes, const QUrl &dir) +{ + MessageConfig config(MessageConfig::TypeFileDialog, MessageConfig::SubTypeOpenFile); + config.setTitle(title); + config.setDefaultValue(dir); + config.setViewProperty(QStringLiteral("mimeTypes"), supportedMimeTypes); + return CoreApp::showDialog(config); +} + +void QtMvvm::getOpenFile(QObject *scope, std::function onResult, const QString &title, const QStringList &supportedMimeTypes, const QUrl &dir) +{ + auto result = getOpenFile(title, supportedMimeTypes, dir); + if(result) { + QObject::connect(result, &MessageResult::anyAction, + scope, [onResult, result](MessageResult::ResultType type) { + onResult(type == MessageResult::PositiveResult ? result->result().toUrl() : QUrl()); + }, Qt::QueuedConnection); + } +} + +void QtMvvm::getOpenFile(std::function onResult, const QString &title, const QStringList &supportedMimeTypes, const QUrl &dir) +{ + getOpenFile(CoreApp::instance(), onResult, title, supportedMimeTypes, dir); +} + +MessageResult *QtMvvm::getOpenFiles(const QString &title, const QStringList &supportedMimeTypes, const QUrl &dir) +{ + MessageConfig config(MessageConfig::TypeFileDialog, MessageConfig::SubTypeOpenFiles); + config.setTitle(title); + config.setDefaultValue(dir); + config.setViewProperty(QStringLiteral("mimeTypes"), supportedMimeTypes); + return CoreApp::showDialog(config); +} + +void QtMvvm::getOpenFiles(QObject *scope, std::function)> onResult, const QString &title, const QStringList &supportedMimeTypes, const QUrl &dir) +{ + auto result = getOpenFiles(title, supportedMimeTypes, dir); + if(result) { + QObject::connect(result, &MessageResult::anyAction, + scope, [onResult, result](MessageResult::ResultType type) { + onResult(type == MessageResult::PositiveResult ? result->result().value>() : QList()); + }, Qt::QueuedConnection); + } +} + +void QtMvvm::getOpenFiles(std::function)> onResult, const QString &title, const QStringList &supportedMimeTypes, const QUrl &dir) +{ + getOpenFiles(CoreApp::instance(), onResult, title, supportedMimeTypes, dir); +} + +MessageResult *QtMvvm::getSaveFile(const QString &title, const QStringList &supportedMimeTypes, const QUrl &dir) +{ + MessageConfig config(MessageConfig::TypeFileDialog, MessageConfig::SubTypeOpenFile); + config.setTitle(title); + config.setDefaultValue(dir); + config.setViewProperty(QStringLiteral("mimeTypes"), supportedMimeTypes); + return CoreApp::showDialog(config); +} + +void QtMvvm::getSaveFile(QObject *scope, std::function onResult, const QString &title, const QStringList &supportedMimeTypes, const QUrl &dir) +{ + auto result = getSaveFile(title, supportedMimeTypes, dir); + if(result) { + QObject::connect(result, &MessageResult::anyAction, + scope, [onResult, result](MessageResult::ResultType type) { + onResult(type == MessageResult::PositiveResult ? result->result().toUrl() : QUrl()); + }, Qt::QueuedConnection); + } +} + +void QtMvvm::getSaveFile(std::function onResult, const QString &title, const QStringList &supportedMimeTypes, const QUrl &dir) +{ + getSaveFile(CoreApp::instance(), onResult, title, supportedMimeTypes, dir); +} diff --git a/src/mvvmcore/message.h b/src/mvvmcore/message.h index f9ecdf1..05c0e29 100644 --- a/src/mvvmcore/message.h +++ b/src/mvvmcore/message.h @@ -1,8 +1,13 @@ #ifndef QTMVVM_MESSAGE_H #define QTMVVM_MESSAGE_H +#include + #include #include +#include +#include +#include #include "QtMvvmCore/qtmvvmcore_global.h" @@ -14,65 +19,320 @@ class Q_MVVMCORE_EXPORT MessageConfig Q_GADGET Q_DECLARE_TR_FUNCTIONS(MessageConfig) - Q_PROPERTY(MessageType type READ type WRITE setType) + Q_PROPERTY(QByteArray type READ type WRITE setType) + Q_PROPERTY(QByteArray subType READ subType WRITE setSubType RESET resetSubType) + Q_PROPERTY(QString title READ title WRITE setTitle) Q_PROPERTY(QString text READ text WRITE setText) - Q_PROPERTY(QString positiveAction READ positiveAction WRITE setPositiveAction RESET resetPositiveAction) - Q_PROPERTY(QString negativeAction READ negativeAction WRITE setNegativeAction RESET resetNegativeAction) - Q_PROPERTY(QString neutralAction READ neutralAction WRITE setNeutralAction RESET resetNeutralAction) - Q_PROPERTY(QByteArray inputType READ inputType WRITE setInputType) + Q_PROPERTY(StandardButtons buttons READ buttons WRITE setButtons RESET resetButtons) + Q_PROPERTY(QHash buttonTexts READ buttonTexts WRITE setButtonTexts RESET resetButtons) + Q_PROPERTY(QVariant defaultValue READ defaultValue WRITE setDefaultValue) - Q_PROPERTY(QVariantMap editProperties READ editProperties WRITE setEditProperties) + Q_PROPERTY(QVariantMap viewProperties READ viewProperties WRITE setViewProperties) public: - enum MessageType { - Information, - Question, - Warning, - Critical, - Input + enum StandardButton { + // keep this in sync with QPlatformDialogHelper::StandardButton + NoButton = 0x00000000, + Ok = 0x00000400, + Save = 0x00000800, + SaveAll = 0x00001000, + Open = 0x00002000, + Yes = 0x00004000, + YesToAll = 0x00008000, + No = 0x00010000, + NoToAll = 0x00020000, + Abort = 0x00040000, + Retry = 0x00080000, + Ignore = 0x00100000, + Close = 0x00200000, + Cancel = 0x00400000, + Discard = 0x00800000, + Help = 0x01000000, + Apply = 0x02000000, + Reset = 0x04000000, + RestoreDefaults = 0x08000000 }; - Q_ENUM(MessageType) + Q_DECLARE_FLAGS(StandardButtons, StandardButton) + Q_FLAG(StandardButtons) + + static const QByteArray TypeMessageBox; + static const QByteArray TypeInputDialog; + static const QByteArray TypeFileDialog; + + static const QByteArray SubTypeInformation; + static const QByteArray SubTypeWarning; + static const QByteArray SubTypeCritical; + static const QByteArray SubTypeQuestion; + static const QByteArray SubTypeAbout; - MessageConfig(MessageType type = Information); + static const QByteArray SubTypeDir; + static const QByteArray SubTypeOpenFile; + static const QByteArray SubTypeOpenFiles; + static const QByteArray SubTypeSaveFile; + + MessageConfig(const QByteArray &type = TypeMessageBox, const QByteArray &subType = {}); MessageConfig(const MessageConfig &other); ~MessageConfig(); MessageConfig &operator=(const MessageConfig &other); - MessageType type() const; + QByteArray type() const; + QByteArray subType() const; QString title() const; QString text() const; - QString positiveAction() const; - QString negativeAction() const; - QString neutralAction() const; + StandardButtons buttons() const; + QHash buttonTexts() const; QByteArray inputType() const; QVariant defaultValue() const; - QVariantMap editProperties() const; - - void setType(MessageType type); - void setTitle(QString title); - void setText(QString text); - void setPositiveAction(QString positiveAction); - void setNegativeAction(QString negativeAction); - void setNeutralAction(QString neutralAction); - void setInputType(QByteArray inputType); - void setDefaultValue(QVariant defaultValue); - void setEditProperties(QVariantMap editProperties); - - void resetPositiveAction(); - void resetNegativeAction(); - void resetNeutralAction(); + QVariantMap viewProperties() const; + + void setType(const QByteArray &type); + void setSubType(const QByteArray &subType); + void setTitle(const QString &title); + void setText(const QString &text); + void setButtons(StandardButtons buttons); + void setButtonTexts(const QHash &buttonTexts); + void setButtonText(StandardButton button, const QString &text); + void setDefaultValue(const QVariant &defaultValue); + void setViewProperties(const QVariantMap &viewProperties); + void setViewProperty(const QString &key, const QVariant &value); + + void resetSubType(); + void resetButtons(); private: QSharedDataPointer d; }; +class MessageResultPrivate; +class Q_MVVMCORE_EXPORT MessageResult : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QVariant result READ result WRITE setResult) + Q_PROPERTY(bool autoDelete READ autoDelete WRITE setAutoDelete NOTIFY autoDeleteChanged) + +public: + enum ResultType { + PositiveResult, + NegativeResult, + NeutralResult + }; + Q_ENUM(ResultType) + + explicit MessageResult(); + ~MessageResult(); + + bool hasResult() const; + QVariant result() const; + bool autoDelete() const; + + //USE IN GUI ONLY + //TODO USE IN GUI ONLY in doc + Q_INVOKABLE void setCloseTarget(QObject *closeObject, const QMetaMethod &closeMethod); + Q_INVOKABLE void complete(MessageResult::ResultType result); + Q_INVOKABLE inline void complete(MessageResult::ResultType result, const QVariant &resultValue) { + setResult(resultValue); + complete(result); + } + +public Q_SLOTS: + void discardMessage(); + + void setResult(QVariant result); + void setAutoDelete(bool autoDelete); + +Q_SIGNALS: + void positiveAction(); + void negativeAction(); + void neutralAction(); + void anyAction(ResultType result); + + void autoDeleteChanged(bool autoDelete); + +private: + QScopedPointer d; +}; + +Q_MVVMCORE_EXPORT MessageResult *information(const QString &title, + const QString &text, + const QString &okText = {}); +Q_MVVMCORE_EXPORT void information(const QString &title, + const QString &text, + QObject *scope, + std::function onResult, + const QString &okText = {}); +Q_MVVMCORE_EXPORT void information(const QString &title, + const QString &text, + std::function onResult, + const QString &okText = {}); + +Q_MVVMCORE_EXPORT MessageResult *question(const QString &title, + const QString &text, + const QString &yesText = {}, + const QString &noText = {}); +Q_MVVMCORE_EXPORT void question(const QString &title, + const QString &text, + QObject *scope, + std::function onResult, + const QString &yesText = {}, + const QString &noText = {}); +Q_MVVMCORE_EXPORT void question(const QString &title, + const QString &text, + std::function onResult, + const QString &yesText = {}, + const QString &noText = {}); + +Q_MVVMCORE_EXPORT MessageResult *warning(const QString &title, + const QString &text, + const QString &okText = {}); +Q_MVVMCORE_EXPORT void warning(const QString &title, + const QString &text, + QObject *scope, + std::function onResult, + const QString &okText = {}); +Q_MVVMCORE_EXPORT void warning(const QString &title, + const QString &text, + std::function onResult, + const QString &okText = {}); + +Q_MVVMCORE_EXPORT MessageResult *critical(const QString &title, + const QString &text, + const QString &okText = {}); +Q_MVVMCORE_EXPORT void critical(const QString &title, + const QString &text, + QObject *scope, + std::function onResult, + const QString &okText = {}); +Q_MVVMCORE_EXPORT void critical(const QString &title, + const QString &text, + std::function onResult, + const QString &okText = {}); + +Q_MVVMCORE_EXPORT MessageResult *about(const QString &description, + const QUrl &websiteUrl = QUrl(), + const QString &licenseName = QString(), + const QUrl &licenseUrl = QUrl(), + const QString &companyName = QString(), + bool addQtVersion = true, + const QStringList &extraTopInfos = QStringList(), + const QString &extraBottomInfos = QString()); + +Q_MVVMCORE_EXPORT MessageResult *getInput(const QString &title, + const QString &text, + const char *inputType, + const QVariant &defaultValue = {}, + const QVariantMap &viewProperties = {}, + const QString &okText = {}, + const QString &cancelText = {}); +Q_MVVMCORE_EXPORT void getInput(const QString &title, + const QString &text, + const char *inputType, + QObject *scope, + std::function onResult, + const QVariant &defaultValue = {}, + const QVariantMap &viewProperties = {}, + const QString &okText = {}, + const QString &cancelText = {}); +Q_MVVMCORE_EXPORT void getInput(const QString &title, + const QString &text, + const char *inputType, + std::function onResult, + const QVariant &defaultValue = {}, + const QVariantMap &viewProperties = {}, + const QString &okText = {}, + const QString &cancelText = {}); + +template +inline MessageResult *getInput(const QString &title, + const QString &text, + const QVariant &defaultValue = {}, + const QVariantMap &viewProperties = {}, + const QString &okText = {}, + const QString &cancelText = {}) { + return getInput(title, text, QMetaType::typeName(qMetaTypeId()), defaultValue, viewProperties, okText, cancelText); +} +template +inline void getInput(const QString &title, + const QString &text, + QObject *scope, + std::function onResult, + const QVariant &defaultValue = {}, + const QVariantMap &viewProperties = {}, + const QString &okText = {}, + const QString &cancelText = {}) { + getInput(title, text, QMetaType::typeName(qMetaTypeId()), scope, [onResult](QVariant v) { + onResult(v.template value()); + }, defaultValue, viewProperties, okText, cancelText); +} +template +inline void getInput(const QString &title, + const QString &text, + std::function onResult, + const QVariant &defaultValue = {}, + const QVariantMap &viewProperties = {}, + const QString &okText = {}, + const QString &cancelText = {}) { + getInput(title, text, QMetaType::typeName(qMetaTypeId()), [onResult](QVariant v) { + onResult(v.template value()); + }, defaultValue, viewProperties, okText, cancelText); +} + +Q_MVVMCORE_EXPORT MessageResult *getExistingDirectory(const QString &title = {}, + const QUrl &dir = {}); +Q_MVVMCORE_EXPORT void getExistingDirectory(QObject *scope, + std::function onResult, + const QString &title = {}, + const QUrl &dir = {}); +Q_MVVMCORE_EXPORT void getExistingDirectory(std::function onResult, + const QString &title = {}, + const QUrl &dir = {}); + +Q_MVVMCORE_EXPORT MessageResult *getOpenFile(const QString &title = {}, + const QStringList &supportedMimeTypes = {}, + const QUrl &dir = {}); +Q_MVVMCORE_EXPORT void getOpenFile(QObject *scope, + std::function onResult, + const QString &title = {}, + const QStringList &supportedMimeTypes = {}, + const QUrl &dir = {}); +Q_MVVMCORE_EXPORT void getOpenFile(std::function onResult, + const QString &title = {}, + const QStringList &supportedMimeTypes = {}, + const QUrl &dir = {}); + +Q_MVVMCORE_EXPORT MessageResult *getOpenFiles(const QString &title = {}, + const QStringList &supportedMimeTypes = {}, + const QUrl &dir = {}); +Q_MVVMCORE_EXPORT void getOpenFiles(QObject *scope, + std::function)> onResult, + const QString &title = {}, + const QStringList &supportedMimeTypes = {}, + const QUrl &dir = {}); +Q_MVVMCORE_EXPORT void getOpenFiles(std::function)> onResult, + const QString &title = {}, + const QStringList &supportedMimeTypes = {}, + const QUrl &dir = {}); +Q_MVVMCORE_EXPORT MessageResult *getSaveFile(const QString &title = {}, + const QStringList &supportedMimeTypes = {}, + const QUrl &dir = {}); +Q_MVVMCORE_EXPORT void getSaveFile(QObject *scope, + std::function onResult, + const QString &title = {}, + const QStringList &supportedMimeTypes = {}, + const QUrl &dir = {}); +Q_MVVMCORE_EXPORT void getSaveFile(std::function onResult, + const QString &title = {}, + const QStringList &supportedMimeTypes = {}, + const QUrl &dir = {}); } Q_DECLARE_METATYPE(QtMvvm::MessageConfig) Q_DECLARE_TYPEINFO(QtMvvm::MessageConfig, Q_MOVABLE_TYPE); +Q_DECLARE_METATYPE(QtMvvm::MessageResult*) +Q_DECLARE_OPERATORS_FOR_FLAGS(QtMvvm::MessageConfig::StandardButtons) #endif // QTMVVM_MESSAGE_H diff --git a/src/mvvmcore/message_p.h b/src/mvvmcore/message_p.h index d3c8ffb..5dc1c56 100644 --- a/src/mvvmcore/message_p.h +++ b/src/mvvmcore/message_p.h @@ -9,20 +9,31 @@ namespace QtMvvm { class MessageConfigPrivate : public QSharedData { public: - MessageConfigPrivate(MessageConfig::MessageType type); + MessageConfigPrivate(const QByteArray &type, const QByteArray &subType); MessageConfigPrivate(const MessageConfigPrivate &other); - MessageConfig::MessageType type; + QByteArray type; + QByteArray subType; QString title; QString text; - QString positiveAction; - QString negativeAction; - QString neutralAction; - QByteArray inputType; + MessageConfig::StandardButtons buttons; + QHash buttonTexts; QVariant defaultValue; QVariantMap editProperties; }; +class MessageResultPrivate +{ +public: + MessageResultPrivate(); + + QPointer closeObject; + QMetaMethod closeMethod; + bool closeRequested; + QVariant result; + bool autoDelete; +}; + } #endif // QTMVVM_MESSAGE_P_H diff --git a/src/mvvmcore/serviceregistry.cpp b/src/mvvmcore/serviceregistry.cpp index bc7f701..0ee073b 100644 --- a/src/mvvmcore/serviceregistry.cpp +++ b/src/mvvmcore/serviceregistry.cpp @@ -96,9 +96,9 @@ QObject *ServiceRegistryPrivate::constructInjectedLocked(const QMetaObject *meta throw ServiceConstructionException("Failed to convert QObject to interface with iid \"" + iid + "\". Use QtMvvm::registerInterfaceConverter to make it convertable " - "or change the properties type to \"QObject*\""); + "or change the property's type to \"QObject*\""); } - tProp.write(instance, variant); //TODO check if casting works + tProp.write(instance, variant); } } diff --git a/src/mvvmcore/viewmodel.cpp b/src/mvvmcore/viewmodel.cpp index 454244d..cc93647 100644 --- a/src/mvvmcore/viewmodel.cpp +++ b/src/mvvmcore/viewmodel.cpp @@ -61,18 +61,16 @@ void ViewModel::showForResult(quint32 requestCode, const QMetaObject *viewMetaOb viewMetaObject->className() + QByteArrayLiteral(") is not a class that extends QtMvvm::ViewModel")); } - showResultImp(requestCode, viewMetaObject, params); + showImp(viewMetaObject, params, const_cast(this), requestCode); } -void ViewModel::showImp(const QMetaObject *mo, const QVariantHash ¶ms, QPointer parent) +void ViewModel::showImp(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent, quint32 requestCode) { - CoreAppPrivate::dInstance()->showViewModel(mo, params, parent, 0); -} - -void ViewModel::showResultImp(quint32 requestCode, const QMetaObject *mo, const QVariantHash ¶ms) const -{ - Q_ASSERT_X(requestCode != 0, Q_FUNC_INFO, "requestCode must not be 0"); - CoreAppPrivate::dInstance()->showViewModel(mo, params, const_cast(this), requestCode); + QMetaObject::invokeMethod(CoreAppPrivate::dInstance().data(), "showViewModel", Qt::QueuedConnection, + Q_ARG(const QMetaObject*, metaObject), + Q_ARG(const QVariantHash&, params), + Q_ARG(QPointer, parent), + Q_ARG(quint32, requestCode)); } // ------------- Private Implementation ------------- diff --git a/src/mvvmcore/viewmodel.h b/src/mvvmcore/viewmodel.h index d63670c..db72841 100644 --- a/src/mvvmcore/viewmodel.h +++ b/src/mvvmcore/viewmodel.h @@ -45,8 +45,7 @@ private: QScopedPointer d; - static void showImp(const QMetaObject *mo, const QVariantHash ¶ms, QPointer parent); - void showResultImp(quint32 requestCode, const QMetaObject *mo, const QVariantHash ¶ms) const; + static void showImp(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent, quint32 requestCode = 0); }; // ------------- Generic Implementation ------------- @@ -62,7 +61,7 @@ template void ViewModel::showForResult(quint32 requestCode, const QVariantHash ¶ms) const { static_assert(std::is_base_of::value, "TViewModel must extend QtMvvm::ViewModel"); - showResultImp(requestCode, &TViewModel::staticMetaObject, params); + showImp(&TViewModel::staticMetaObject, params, const_cast(this), requestCode); } } diff --git a/src/mvvmwidgets/widgetspresenter.cpp b/src/mvvmwidgets/widgetspresenter.cpp index 219896f..a349e5c 100644 --- a/src/mvvmwidgets/widgetspresenter.cpp +++ b/src/mvvmwidgets/widgetspresenter.cpp @@ -77,6 +77,11 @@ void WidgetsPresenter::present(ViewModel *viewModel, const QVariantHash ¶ms, } } +void WidgetsPresenter::showDialog(const MessageConfig &config, MessageResult *result) +{ + Q_UNIMPLEMENTED(); +} + const QMetaObject *WidgetsPresenter::findWidgetMetaObject(const QMetaObject *viewModelMetaObject) { auto currentMeta = viewModelMetaObject; @@ -149,22 +154,9 @@ bool WidgetsPresenter::tryPresent(QWidget *view, QWidget *parentView) bool WidgetsPresenter::setupLifeCycle(ViewModel *viewModel, QWidget *view) { - //TODO fix - return false; - - auto viewMo = view->metaObject(); - if(viewMo->indexOfSignal("qtmvvm_visibleChanged(bool)") != -1) { - connect(view, SIGNAL(qtmvvm_visibleChanged(bool)), - viewModel, SLOT(updateVisible(bool))); - return true; - } - - if(viewMo->indexOfSignal("visibleChanged(bool)") != -1) { - connect(view, SIGNAL(visibleChanged(bool)), - viewModel, SLOT(updateVisible(bool))); - return true; - } - + //TODO implement as soon as quick part is ready as well + Q_UNUSED(viewModel) + Q_UNUSED(view) return false; } diff --git a/src/mvvmwidgets/widgetspresenter.h b/src/mvvmwidgets/widgetspresenter.h index 6b1fdbd..0bb2014 100644 --- a/src/mvvmwidgets/widgetspresenter.h +++ b/src/mvvmwidgets/widgetspresenter.h @@ -35,6 +35,7 @@ public: static void registerViewExplicitly(const QMetaObject *viewModelType, const QMetaObject *viewType); void present(ViewModel *viewModel, const QVariantHash ¶ms, QPointer parent) override; + void showDialog(const MessageConfig &config, MessageResult *result) override; protected: virtual const QMetaObject *findWidgetMetaObject(const QMetaObject *viewModelMetaObject);