diff --git a/src/mvvmcore/binding.cpp b/src/mvvmcore/binding.cpp index b5f65c3..f7e3b3b 100644 --- a/src/mvvmcore/binding.cpp +++ b/src/mvvmcore/binding.cpp @@ -85,6 +85,10 @@ Binding QtMvvm::bind(QObject *viewModel, const QMetaProperty &viewModelProperty, } +Binding::Binding() : + d(nullptr) +{} + Binding::Binding(QPointer d_ptr) : d(d_ptr) {} diff --git a/src/mvvmcore/binding.h b/src/mvvmcore/binding.h index d8fa9a3..c60b870 100644 --- a/src/mvvmcore/binding.h +++ b/src/mvvmcore/binding.h @@ -26,7 +26,8 @@ public: Q_DECLARE_FLAGS(BindingDirection, BindingDirectionFlag) Q_FLAG(BindingDirection) - Binding(QPointer d_ptr = nullptr); + Binding(); + Binding(QPointer d_ptr); ~Binding(); bool isValid() const; diff --git a/src/mvvmcore/coreapp.cpp b/src/mvvmcore/coreapp.cpp index 0a272f9..f119937 100644 --- a/src/mvvmcore/coreapp.cpp +++ b/src/mvvmcore/coreapp.cpp @@ -95,20 +95,28 @@ QScopedPointer &CoreAppPrivate::dInstance() return instance->d; } -void CoreAppPrivate::showViewModel(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent) +void CoreAppPrivate::showViewModel(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent, quint32 requestCode) { if(presenter) { + QPointer vm; try { auto obj = ServiceRegistryPrivate::constructInjected(metaObject); - auto vm = qobject_cast(obj); + vm = qobject_cast(obj); if(!vm) throw ServiceConstructionException("Invalid types - not at QtMvvm::ViewModel"); presenter->present(vm, params, parent); + if(requestCode != 0) { + QObject::connect(vm, &ViewModel::resultReady, + parent, &ViewModel::onResult, + Qt::UniqueConnection); + } } catch(QException &e) { logCritical() << "Failed to present viewmodel of type" << metaObject->className() << "with error:" << e.what(); + if(vm) + vm->deleteLater(); } } else { logCritical() << "Failed to present viewmodel of type" diff --git a/src/mvvmcore/coreapp.h b/src/mvvmcore/coreapp.h index 0886340..bbe5916 100644 --- a/src/mvvmcore/coreapp.h +++ b/src/mvvmcore/coreapp.h @@ -47,7 +47,8 @@ private: template inline void CoreApp::show(const QVariantHash ¶ms) const { - ViewModel::show(params); + 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 0eaaa18..f4b024a 100644 --- a/src/mvvmcore/coreapp_p.h +++ b/src/mvvmcore/coreapp_p.h @@ -14,7 +14,7 @@ class Q_MVVMCORE_EXPORT CoreAppPrivate public: static QScopedPointer &dInstance(); - void showViewModel(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent); + void showViewModel(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent, quint32 requestCode); IPresenter *currentPresenter() const; diff --git a/src/mvvmcore/ipresenter.cpp b/src/mvvmcore/ipresenter.cpp new file mode 100644 index 0000000..a2a6f1e --- /dev/null +++ b/src/mvvmcore/ipresenter.cpp @@ -0,0 +1,25 @@ +#include "ipresenter.h" +using namespace QtMvvm; + +PresenterException::PresenterException(const QByteArray &what) : + _what(what) +{} + +PresenterException::PresenterException(const PresenterException * const other) : + _what(other->_what) +{} + +const char *PresenterException::what() const noexcept +{ + return _what.constData(); +} + +void PresenterException::raise() const +{ + throw (*this); +} + +QException *PresenterException::clone() const +{ + return new PresenterException(this); +} diff --git a/src/mvvmcore/ipresenter.h b/src/mvvmcore/ipresenter.h index 9066447..f20edd9 100644 --- a/src/mvvmcore/ipresenter.h +++ b/src/mvvmcore/ipresenter.h @@ -1,11 +1,29 @@ #ifndef QTMVVM_IPRESENTER_H #define QTMVVM_IPRESENTER_H +#include + #include "QtMvvmCore/qtmvvmcore_global.h" #include "QtMvvmCore/viewmodel.h" namespace QtMvvm { +class Q_MVVMCORE_EXPORT PresenterException : public QException +{ +public: + PresenterException(const QByteArray &what); + + const char *what() const noexcept override; + + void raise() const override; + QException *clone() const override; + +protected: + PresenterException(const PresenterException * const other); + + const QByteArray _what; +}; + class Q_MVVMCORE_EXPORT IPresenter { public: diff --git a/src/mvvmcore/message.cpp b/src/mvvmcore/message.cpp new file mode 100644 index 0000000..85dec4b --- /dev/null +++ b/src/mvvmcore/message.cpp @@ -0,0 +1,187 @@ +#include "message.h" +#include "message_p.h" +using namespace QtMvvm; + +MessageConfig::MessageConfig(MessageType type) : + d(new MessageConfigPrivate(type)) +{ + resetPositiveAction(); + resetNegativeAction(); + resetNeutralAction(); +} + +MessageConfig::MessageConfig(const MessageConfig &other) : + d(other.d) +{} + +MessageConfig::~MessageConfig() {} + +MessageConfig &MessageConfig::operator=(const MessageConfig &other) +{ + d = other.d; + return (*this); +} + +MessageConfig::MessageType MessageConfig::type() const +{ + return d->type; +} + +QString MessageConfig::title() const +{ + return d->title; +} + +QString MessageConfig::text() const +{ + return d->text; +} + +QString MessageConfig::positiveAction() const +{ + return d->positiveAction; +} + +QString MessageConfig::negativeAction() const +{ + return d->negativeAction; +} + +QString MessageConfig::neutralAction() const +{ + return d->neutralAction; +} + +QByteArray MessageConfig::inputType() const +{ + return d->inputType; +} + +QVariant MessageConfig::defaultValue() const +{ + return d->defaultValue; +} + +QVariantMap MessageConfig::editProperties() const +{ + return d->editProperties; +} + +void MessageConfig::setType(MessageConfig::MessageType type) +{ + d->type = type; + resetPositiveAction(); + resetNegativeAction(); + resetNeutralAction(); +} + +void MessageConfig::setTitle(QString title) +{ + d->title = title; +} + +void MessageConfig::setText(QString text) +{ + d->text = text; +} + +void MessageConfig::setPositiveAction(QString positiveAction) +{ + d->positiveAction = positiveAction; +} + +void MessageConfig::setNegativeAction(QString negativeAction) +{ + d->negativeAction = negativeAction; +} + +void MessageConfig::setNeutralAction(QString neutralAction) +{ + d->neutralAction = neutralAction; +} + +void MessageConfig::setInputType(QByteArray inputType) +{ + d->inputType = inputType; +} + +void MessageConfig::setDefaultValue(QVariant defaultValue) +{ + d->defaultValue = defaultValue; +} + +void MessageConfig::setEditProperties(QVariantMap editProperties) +{ + d->editProperties = editProperties; +} + +void MessageConfig::resetPositiveAction() +{ + 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; + } +} + +void MessageConfig::resetNegativeAction() +{ + switch (d->type) { + case Input: + d->negativeAction = tr("Cancel"); + break; + case Question: + d->negativeAction = tr("No"); + break; + case Information: + case Warning: + case Critical: + d->negativeAction.clear(); + break; + default: + Q_UNREACHABLE(); + break; + } +} + +void MessageConfig::resetNeutralAction() +{ + d->neutralAction.clear(); +} + +// ------------- Private Implementation ------------- + +QtMvvm::MessageConfigPrivate::MessageConfigPrivate(MessageConfig::MessageType type) : + QSharedData(), + type(type), + title(), + text(), + positiveAction(), + negativeAction(), + neutralAction(), + inputType(), + defaultValue(), + editProperties() +{} + +QtMvvm::MessageConfigPrivate::MessageConfigPrivate(const QtMvvm::MessageConfigPrivate &other) : + QSharedData(other), + type(other.type), + title(other.title), + text(other.text), + positiveAction(other.positiveAction), + negativeAction(other.negativeAction), + neutralAction(other.neutralAction), + inputType(other.inputType), + defaultValue(other.defaultValue), + editProperties(other.editProperties) +{} diff --git a/src/mvvmcore/message.h b/src/mvvmcore/message.h new file mode 100644 index 0000000..f9ecdf1 --- /dev/null +++ b/src/mvvmcore/message.h @@ -0,0 +1,78 @@ +#ifndef QTMVVM_MESSAGE_H +#define QTMVVM_MESSAGE_H + +#include +#include + +#include "QtMvvmCore/qtmvvmcore_global.h" + +namespace QtMvvm { + +class MessageConfigPrivate; +class Q_MVVMCORE_EXPORT MessageConfig +{ + Q_GADGET + Q_DECLARE_TR_FUNCTIONS(MessageConfig) + + Q_PROPERTY(MessageType type READ type WRITE setType) + 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(QVariant defaultValue READ defaultValue WRITE setDefaultValue) + Q_PROPERTY(QVariantMap editProperties READ editProperties WRITE setEditProperties) + +public: + enum MessageType { + Information, + Question, + Warning, + Critical, + Input + }; + Q_ENUM(MessageType) + + MessageConfig(MessageType type = Information); + MessageConfig(const MessageConfig &other); + ~MessageConfig(); + + MessageConfig &operator=(const MessageConfig &other); + + MessageType type() const; + QString title() const; + QString text() const; + QString positiveAction() const; + QString negativeAction() const; + QString neutralAction() 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(); + +private: + QSharedDataPointer d; +}; + + + +} + +Q_DECLARE_METATYPE(QtMvvm::MessageConfig) +Q_DECLARE_TYPEINFO(QtMvvm::MessageConfig, Q_MOVABLE_TYPE); + +#endif // QTMVVM_MESSAGE_H diff --git a/src/mvvmcore/message_p.h b/src/mvvmcore/message_p.h new file mode 100644 index 0000000..d3c8ffb --- /dev/null +++ b/src/mvvmcore/message_p.h @@ -0,0 +1,28 @@ +#ifndef QTMVVM_MESSAGE_P_H +#define QTMVVM_MESSAGE_P_H + +#include "qtmvvmcore_global.h" +#include "message.h" + +namespace QtMvvm { + +class MessageConfigPrivate : public QSharedData +{ +public: + MessageConfigPrivate(MessageConfig::MessageType type); + MessageConfigPrivate(const MessageConfigPrivate &other); + + MessageConfig::MessageType type; + QString title; + QString text; + QString positiveAction; + QString negativeAction; + QString neutralAction; + QByteArray inputType; + QVariant defaultValue; + QVariantMap editProperties; +}; + +} + +#endif // QTMVVM_MESSAGE_P_H diff --git a/src/mvvmcore/mvvmcore.pro b/src/mvvmcore/mvvmcore.pro index 2176f33..11396bb 100644 --- a/src/mvvmcore/mvvmcore.pro +++ b/src/mvvmcore/mvvmcore.pro @@ -14,14 +14,18 @@ HEADERS += \ ipresenter.h \ qtmvvm_logging_p.h \ binding.h \ - binding_p.h + binding_p.h \ + message.h \ + message_p.h SOURCES += \ viewmodel.cpp \ coreapp.cpp \ serviceregistry.cpp \ qtmvvmcore_global.cpp \ - binding.cpp + binding.cpp \ + message.cpp \ + ipresenter.cpp TRANSLATIONS += \ translations/qtmvvmcore_de.ts \ diff --git a/src/mvvmcore/viewmodel.cpp b/src/mvvmcore/viewmodel.cpp index 25fb350..5d490a1 100644 --- a/src/mvvmcore/viewmodel.cpp +++ b/src/mvvmcore/viewmodel.cpp @@ -1,6 +1,7 @@ #include "viewmodel.h" #include "viewmodel_p.h" #include "coreapp_p.h" +#include "qtmvvm_logging_p.h" using namespace QtMvvm; ViewModel::ViewModel(QObject *parent) : @@ -10,34 +11,27 @@ ViewModel::ViewModel(QObject *parent) : ViewModel::~ViewModel() {} -ViewModel *ViewModel::parentViewModel() const -{ - return qobject_cast(parent()); //TODO not working that way, parent is always the view... -} +void ViewModel::onInit(const QVariantHash &) {} -bool ViewModel::deleteOnClose() const +void ViewModel::onResult(quint32 requestCode, const QVariant &result) { - return d->deleteOnClose; + Q_UNUSED(result) + logDebug() << "Unused result on" << metaObject()->className() + << "with request code" << requestCode; } -void ViewModel::setDeleteOnClose(bool deleteOnClose) +void ViewModel::showImp(const QMetaObject *mo, const QVariantHash ¶ms, QPointer parent) { - if (d->deleteOnClose == deleteOnClose) - return; - - d->deleteOnClose = deleteOnClose; - emit deleteOnCloseChanged(deleteOnClose, {}); + CoreAppPrivate::dInstance()->showViewModel(mo, params, parent, 0); } -void ViewModel::onInit(const QVariantHash &) {} - -void ViewModel::showImp(const QMetaObject *mo, const QVariantHash ¶ms, QPointer parent) +void ViewModel::showResultImp(quint32 requestCode, const QMetaObject *mo, const QVariantHash ¶ms) const { - CoreAppPrivate::dInstance()->showViewModel(mo, params, parent); + Q_ASSERT_X(requestCode != 0, Q_FUNC_INFO, "requestCode must not be 0"); + CoreAppPrivate::dInstance()->showViewModel(mo, params, const_cast(this), requestCode); } // ------------- Private Implementation ------------- -ViewModelPrivate::ViewModelPrivate() : - deleteOnClose(true) +ViewModelPrivate::ViewModelPrivate() {} diff --git a/src/mvvmcore/viewmodel.h b/src/mvvmcore/viewmodel.h index 44db6fb..96b63f8 100644 --- a/src/mvvmcore/viewmodel.h +++ b/src/mvvmcore/viewmodel.h @@ -12,54 +12,53 @@ namespace QtMvvm { +class CoreApp; + class ViewModelPrivate; class Q_MVVMCORE_EXPORT ViewModel : public QObject { Q_OBJECT - Q_PROPERTY(bool deleteOnClose READ deleteOnClose WRITE setDeleteOnClose NOTIFY deleteOnCloseChanged) - public: explicit ViewModel(QObject *parent = nullptr); ~ViewModel(); - virtual ViewModel *parentViewModel() const; - - bool deleteOnClose() const; - - template - inline static void show(const QVariantHash ¶ms = {}); - public Q_SLOTS: - void setDeleteOnClose(bool deleteOnClose); - virtual void onInit(const QVariantHash ¶ms); + virtual void onResult(quint32 requestCode, const QVariant &result); Q_SIGNALS: - void deleteOnCloseChanged(bool deleteOnClose, QPrivateSignal); + void resultReady(quint32 requestCode, const QVariant &result); protected: template - inline void showChild(const QVariantHash ¶ms = {}) const; + inline void show(const QVariantHash ¶ms = {}) const; + template + inline void showForResult(quint32 requestCode, const QVariantHash ¶ms = {}) const; private: + friend class QtMvvm::CoreApp; + QScopedPointer d; static void showImp(const QMetaObject *mo, const QVariantHash ¶ms, QPointer parent); + void showResultImp(quint32 requestCode, const QMetaObject *mo, const QVariantHash ¶ms) const; }; +// ------------- Generic Implementation ------------- + template -inline void ViewModel::show(const QVariantHash ¶ms) +inline void ViewModel::show(const QVariantHash ¶ms) const { static_assert(std::is_base_of::value, "TViewModel must extend QtMvvm::ViewModel"); - showImp(&TViewModel::staticMetaObject, params, nullptr); + showImp(&TViewModel::staticMetaObject, params, const_cast(this)); } template -inline void ViewModel::showChild(const QVariantHash ¶ms) const +void ViewModel::showForResult(quint32 requestCode, const QVariantHash ¶ms) const { static_assert(std::is_base_of::value, "TViewModel must extend QtMvvm::ViewModel"); - showImp(&TViewModel::staticMetaObject, params, const_cast(this)); + showResultImp(requestCode, &TViewModel::staticMetaObject, params); } } diff --git a/src/mvvmcore/viewmodel_p.h b/src/mvvmcore/viewmodel_p.h index 943eb6c..d08c3f1 100644 --- a/src/mvvmcore/viewmodel_p.h +++ b/src/mvvmcore/viewmodel_p.h @@ -10,8 +10,6 @@ class ViewModelPrivate { public: ViewModelPrivate(); - - bool deleteOnClose; }; } diff --git a/src/mvvmwidgets/widgetspresenter.cpp b/src/mvvmwidgets/widgetspresenter.cpp index 28c8d67..219896f 100644 --- a/src/mvvmwidgets/widgetspresenter.cpp +++ b/src/mvvmwidgets/widgetspresenter.cpp @@ -39,12 +39,8 @@ void WidgetsPresenter::present(ViewModel *viewModel, const QVariantHash ¶ms, { // find and create view auto viewMetaObject = findWidgetMetaObject(viewModel->metaObject()); - if(!viewMetaObject) { - logCritical() << "Unable to find view for viewmodel of type" - << viewModel->metaObject()->className(); - viewModel->deleteLater(); - return; - } + if(!viewMetaObject) + throw PresenterException("Unable to find view any matching view"); auto parentView = parent ? qobject_cast(parent->parent()) : @@ -52,11 +48,10 @@ void WidgetsPresenter::present(ViewModel *viewModel, const QVariantHash ¶ms, auto view = qobject_cast(viewMetaObject->newInstance(Q_ARG(QtMvvm::ViewModel*, viewModel), Q_ARG(QWidget*, parentView))); if(!view) { - logCritical() << "Failed to create view of type" - << viewMetaObject->className() - << "(did you mark the constructor as Q_INVOKABLE? Required signature: \"Q_INVOKABLE Contructor(QtMvvm::ViewModel *, QWidget*);\")"; - viewModel->deleteLater(); - return; + throw PresenterException(QByteArrayLiteral("Failed to create view of type") + + viewMetaObject->className() + + QByteArrayLiteral("(did you mark the constructor as Q_INVOKABLE? " + "Required signature: \"Q_INVOKABLE Contructor(QtMvvm::ViewModel *, QWidget*);\")")); } // initialize viewmodel and view relationship @@ -75,13 +70,10 @@ void WidgetsPresenter::present(ViewModel *viewModel, const QVariantHash ¶ms, presented = tryPresent(view, parentView); //handle the present result - if(presented) { - //if no lifecycle explicit on show is needed - //TODO needed? - } else { - logCritical() << "Unable to find a method that is able to present a view of type" - << viewMetaObject->className(); + if(!presented) { view->deleteLater(); + throw PresenterException(QByteArrayLiteral("Unable to find a method that is able to present a view of type") + + viewMetaObject->className()); } }