From 9787a8092f6c0342865fe8f59f1f95b94609cdf2 Mon Sep 17 00:00:00 2001 From: Skycoder42 Date: Thu, 21 Jun 2018 14:09:33 +0200 Subject: [PATCH] Added support for container based presentation --- .../mvvmcore/SampleCore/drawerviewmodel.cpp | 4 +- .../mvvmcore/SampleCore/samplecoreapp.cpp | 2 + .../mvvmcore/SampleCore/sampleviewmodel.cpp | 4 +- examples/mvvmcore/SampleCore/tabviewmodel.h | 4 + src/mvvmcore/coreapp.cpp | 128 +++++++++++++----- src/mvvmcore/coreapp_p.h | 8 ++ src/mvvmcore/viewmodel.h | 2 + src/mvvmquick/quickpresenter.cpp | 20 ++- 8 files changed, 135 insertions(+), 37 deletions(-) diff --git a/examples/mvvmcore/SampleCore/drawerviewmodel.cpp b/examples/mvvmcore/SampleCore/drawerviewmodel.cpp index 7b1446a..4214a0b 100644 --- a/examples/mvvmcore/SampleCore/drawerviewmodel.cpp +++ b/examples/mvvmcore/SampleCore/drawerviewmodel.cpp @@ -30,7 +30,9 @@ void DrawerViewModel::open(int index) show(); break; case 1: - show(); + show({ + {QStringLiteral("title"), QStringLiteral("Root Tab")} + }); break; case 2: show(); diff --git a/examples/mvvmcore/SampleCore/samplecoreapp.cpp b/examples/mvvmcore/SampleCore/samplecoreapp.cpp index b10daff..6898e73 100644 --- a/examples/mvvmcore/SampleCore/samplecoreapp.cpp +++ b/examples/mvvmcore/SampleCore/samplecoreapp.cpp @@ -1,6 +1,7 @@ #include "samplecoreapp.h" #include "sampleviewmodel.h" #include "drawerviewmodel.h" +#include "tabviewmodel.h" #include #include @@ -33,6 +34,7 @@ void SampleCoreApp::performRegistrations() { Q_INIT_RESOURCE(sample_core); + qRegisterMetaType(); QtMvvm::registerInterfaceConverter(); QLoggingCategory::setFilterRules(QStringLiteral("qtmvvm.debug=true")); diff --git a/examples/mvvmcore/SampleCore/sampleviewmodel.cpp b/examples/mvvmcore/SampleCore/sampleviewmodel.cpp index 0765010..ee7f830 100644 --- a/examples/mvvmcore/SampleCore/sampleviewmodel.cpp +++ b/examples/mvvmcore/SampleCore/sampleviewmodel.cpp @@ -68,7 +68,9 @@ void SampleViewModel::setActive(bool active) void SampleViewModel::showTabs() { - show(); + show({ + {QStringLiteral("title"), QStringLiteral("Root Tab")} + }); } void SampleViewModel::showSettings() diff --git a/examples/mvvmcore/SampleCore/tabviewmodel.h b/examples/mvvmcore/SampleCore/tabviewmodel.h index e419bd5..33d95e0 100644 --- a/examples/mvvmcore/SampleCore/tabviewmodel.h +++ b/examples/mvvmcore/SampleCore/tabviewmodel.h @@ -23,6 +23,8 @@ class TabItemViewModel : public QtMvvm::ViewModel Q_PROPERTY(QString title READ title NOTIFY titleChanged) + QTMVVM_CONTAINER_VM(TabViewModel) + public: Q_INVOKABLE explicit TabItemViewModel(QObject *parent = nullptr); ~TabItemViewModel(); @@ -39,4 +41,6 @@ private: QString _title; }; +Q_DECLARE_METATYPE(TabViewModel*) + #endif // TABVIEWMODEL_H diff --git a/src/mvvmcore/coreapp.cpp b/src/mvvmcore/coreapp.cpp index 962c2b5..a416e46 100644 --- a/src/mvvmcore/coreapp.cpp +++ b/src/mvvmcore/coreapp.cpp @@ -171,23 +171,107 @@ QScopedPointer &CoreAppPrivate::dInstance() } void CoreAppPrivate::showViewModel(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent, quint32 requestCode) +{ + showViewModelWithReturn(metaObject, params, std::move(parent), requestCode); +} + +void CoreAppPrivate::showDialog(const MessageConfig &config, MessageResult *result) +{ + if(presenter) { + try { + presenter->showDialog(config, result); + logDebug() << "Successfully presented dialog of type" << config.type(); + } catch(QException &e) { + logCritical() << "Failed to show dialog for type" + << config.type() << ":" << config.subType() + << "with error:" + << e.what(); + result->complete(MessageConfig::NoButton); + } + } else { + logCritical() << "Failed to show dialog ff type" + << config.type() << ":" << config.subType() + << "- no presenter was set"; + result->complete(MessageConfig::NoButton); + } +} + +bool CoreAppPrivate::isSingleton(const QMetaObject *metaObject) const +{ + auto sInfoIndex = metaObject->indexOfClassInfo("qtmvvm_singleton"); + if(sInfoIndex != -1) { + auto sInfo = metaObject->classInfo(sInfoIndex); + Q_ASSERT(qstrcmp(sInfo.name(), "qtmvvm_singleton") == 0); + return qstrcmp(sInfo.value(), "true") == 0; + } else + return false; +} + +const QMetaObject *CoreAppPrivate::getContainer(const QMetaObject *metaObject) const +{ + auto cInfoIndex = metaObject->indexOfClassInfo("qtmvvm_container_viewmodel"); + if(cInfoIndex != -1) { + auto cInfo = metaObject->classInfo(cInfoIndex); + Q_ASSERT(qstrcmp(cInfo.name(), "qtmvvm_container_viewmodel") == 0); + auto typeId = QMetaType::type(QByteArray{cInfo.value() + QByteArray{"*"}}.constData()); + if(typeId == QMetaType::UnknownType) { + throw PresenterException { + QByteArrayLiteral("Unabled to find the qtmvvm_container_viewmodel of type \"") + cInfo.value() + + QByteArrayLiteral("\" for viewmodel of type \"") + metaObject->className() + QByteArrayLiteral("\"") + }; + } + auto containerMo = QMetaType::metaObjectForType(typeId); + if(!containerMo) { + throw PresenterException { + QByteArrayLiteral("The qtmvvm_container_viewmodel of type \"") + cInfo.value() + + QByteArrayLiteral("\" for viewmodel of type \"") + metaObject->className() + + QByteArrayLiteral("\" does exists, but does not have a QMetaObject") + }; + } + if(!containerMo->inherits(&ViewModel::staticMetaObject)) { + throw PresenterException { + QByteArrayLiteral("The qtmvvm_container_viewmodel of type \"") + cInfo.value() + + QByteArrayLiteral("\" for viewmodel of type \"") + metaObject->className() + + QByteArrayLiteral("\" does exists, but does not extend QtMvvm::ViewModel") + }; + } + return containerMo; + } else + return nullptr; +} + +QPointer CoreAppPrivate::showViewModelWithReturn(const QMetaObject *metaObject, const QVariantHash ¶ms, QPointer parent, quint32 requestCode) { if(presenter) { - //first: check for single instance - auto isSingleton = false; - auto sInfoIndex = metaObject->indexOfClassInfo("qtmvvm_singleton"); - if(sInfoIndex != -1) { - auto sInfo = metaObject->classInfo(sInfoIndex); - Q_ASSERT(qstrcmp(sInfo.name(), "qtmvvm_singleton") == 0); - isSingleton = qstrcmp(sInfo.value(), "true") == 0; + // first: check if it has a container, and if yes: show the container + try { + auto container = getContainer(metaObject); + if(container) { + logDebug() << "Found container type for" << metaObject->className() + << "as" << container->className(); + parent = showViewModelWithReturn(container, { //TODO document special parameters + {QStringLiteral("qtmvvm_container_for"), QByteArray{metaObject->className()}}, + {QStringLiteral("qtmvvm_child_params"), params} + }, std::move(parent), 0); + if(!parent) + throw PresenterException{"Failed to present parent container"}; + } + } catch(PresenterException &e) { + logCritical() << "Failed to present viewmodel of type" + << metaObject->className() + << "with error:" + << e.what(); + return nullptr; } - // next handle the singleton - if(isSingleton) { + // next: handle a singleton + auto isSingle = isSingleton(metaObject); + if(isSingle) { auto viewModel = singleInstances.value(metaObject); if(viewModel) { + logDebug() << "Found existing single instance for" << metaObject->className(); emit viewModel->instanceInvoked(ViewModel::QPrivateSignal{}); - return; + return viewModel; } } @@ -210,8 +294,9 @@ void CoreAppPrivate::showViewModel(const QMetaObject *metaObject, const QVariant logDebug() << "Successfully presented" << metaObject->className(); // if singleton -> store it - if(isSingleton) + if(isSingle) singleInstances.insert(metaObject, vm); + return vm; } catch(QException &e) { logCritical() << "Failed to present viewmodel of type" << metaObject->className() @@ -225,25 +310,6 @@ void CoreAppPrivate::showViewModel(const QMetaObject *metaObject, const QVariant << metaObject->className() << "- no presenter was set"; } -} -void CoreAppPrivate::showDialog(const MessageConfig &config, MessageResult *result) -{ - if(presenter) { - try { - presenter->showDialog(config, result); - logDebug() << "Successfully presented dialog of type" << config.type(); - } catch(QException &e) { - logCritical() << "Failed to show dialog ff type" - << config.type() << ":" << config.subType() - << "with error:" - << e.what(); - result->complete(MessageConfig::NoButton); - } - } else { - logCritical() << "Failed to show dialog ff type" - << config.type() << ":" << config.subType() - << "- no presenter was set"; - result->complete(MessageConfig::NoButton); - } + return nullptr; } diff --git a/src/mvvmcore/coreapp_p.h b/src/mvvmcore/coreapp_p.h index 9ce5cf1..bd0866b 100644 --- a/src/mvvmcore/coreapp_p.h +++ b/src/mvvmcore/coreapp_p.h @@ -29,6 +29,14 @@ private: IPresenter *presenter = nullptr; QHash> singleInstances; + + bool isSingleton(const QMetaObject *metaObject) const; + const QMetaObject * getContainer(const QMetaObject *metaObject) const; + + QPointer showViewModelWithReturn(const QMetaObject *metaObject, + const QVariantHash ¶ms, + QPointer parent, + quint32 requestCode); }; } diff --git a/src/mvvmcore/viewmodel.h b/src/mvvmcore/viewmodel.h index 306d1a7..3d0b3ff 100644 --- a/src/mvvmcore/viewmodel.h +++ b/src/mvvmcore/viewmodel.h @@ -67,6 +67,8 @@ private: //TODO doc #define QTMVVM_SINGLETON Q_CLASSINFO("qtmvvm_singleton", "true") +#define QTMVVM_CONTAINER_VM(x) Q_CLASSINFO("qtmvvm_container_viewmodel", #x) + // ------------- Generic Implementation ------------- template diff --git a/src/mvvmquick/quickpresenter.cpp b/src/mvvmquick/quickpresenter.cpp index 9dbb7c2..cef2ee1 100644 --- a/src/mvvmquick/quickpresenter.cpp +++ b/src/mvvmquick/quickpresenter.cpp @@ -166,21 +166,33 @@ int QuickPresenter::presentMethodIndex(const QMetaObject *presenterMetaObject, Q { auto index = -1; if(viewObject->inherits("QQuickPopup")) { - logDebug() << "Presenting" << viewObject->metaObject()->className() << "as popup"; index = presenterMetaObject->indexOfMethod("presentPopup(QVariant)"); + if(index != -1) { + logDebug() << "Presenting" << viewObject->metaObject()->className() + << "as popup with presenter" << presenterMetaObject->className(); + } } if(viewObject->inherits("QQuickItem")) { if(nameOrClassContains(viewObject, QStringLiteral("Drawer"))) { - logDebug() << "Presenting" << viewObject->metaObject()->className() << "as drawer"; index = presenterMetaObject->indexOfMethod("presentDrawerContent(QVariant)"); + if(index != -1) { + logDebug() << "Presenting" << viewObject->metaObject()->className() + << "as drawer with presenter" << presenterMetaObject->className(); + } } if(index == -1 && nameOrClassContains(viewObject, QStringLiteral("Tab"))) { - logDebug() << "Presenting" << viewObject->metaObject()->className() << "as tab"; index = presenterMetaObject->indexOfMethod("presentTab(QVariant)"); + if(index != -1) { + logDebug() << "Presenting" << viewObject->metaObject()->className() + << "as tab with presenter" << presenterMetaObject->className(); + } } if(index == -1) { - logDebug() << "Presenting" << viewObject->metaObject()->className() << "as item"; index = presenterMetaObject->indexOfMethod("presentItem(QVariant)"); + if(index != -1) { + logDebug() << "Presenting" << viewObject->metaObject()->className() + << "as item with presenter" << presenterMetaObject->className(); + } } } return index;