Browse Source

added widget presenter, but segfaults...

pull/2/head
Skycoder42 7 years ago
parent
commit
597621d302
  1. 5
      examples/mvvmcore/SampleCore/samplecoreapp.cpp
  2. 1
      examples/mvvmcore/SampleCore/samplecoreapp.h
  3. 10
      examples/mvvmcore/SampleCore/sampleviewmodel.cpp
  4. 9
      examples/mvvmcore/SampleCore/sampleviewmodel.h
  5. 10
      examples/mvvmwidgets/SampleWidgets/SampleWidgets.pro
  6. 9
      examples/mvvmwidgets/SampleWidgets/main.cpp
  7. 14
      examples/mvvmwidgets/SampleWidgets/mainwindow.cpp
  8. 22
      examples/mvvmwidgets/SampleWidgets/mainwindow.h
  9. 21
      examples/mvvmwidgets/SampleWidgets/mainwindow.ui
  10. 15
      examples/mvvmwidgets/SampleWidgets/sampleview.cpp
  11. 24
      examples/mvvmwidgets/SampleWidgets/sampleview.h
  12. 24
      examples/mvvmwidgets/SampleWidgets/sampleview.ui
  13. 46
      src/mvvmcore/coreapp.cpp
  14. 3
      src/mvvmcore/coreapp.h
  15. 12
      src/mvvmcore/coreapp_p.h
  16. 23
      src/mvvmcore/ipresenter.h
  17. 3
      src/mvvmcore/mvvmcore.pro
  18. 8
      src/mvvmcore/qtmvvm_logging_p.h
  19. 2
      src/mvvmcore/qtmvvmcore_global.cpp
  20. 11
      src/mvvmcore/qtmvvmcore_global.h
  21. 109
      src/mvvmcore/serviceregistry.cpp
  22. 1
      src/mvvmcore/serviceregistry.h
  23. 3
      src/mvvmcore/serviceregistry_p.h
  24. 17
      src/mvvmcore/viewmodel.cpp
  25. 15
      src/mvvmcore/viewmodel.h
  26. 6
      src/mvvmwidgets/builddummy.cpp
  27. 12
      src/mvvmwidgets/builddummy.h
  28. 20
      src/mvvmwidgets/ipresentingview.h
  29. 10
      src/mvvmwidgets/mvvmwidgets.pro
  30. 222
      src/mvvmwidgets/widgetspresenter.cpp
  31. 76
      src/mvvmwidgets/widgetspresenter.h
  32. 25
      src/mvvmwidgets/widgetspresenter_p.h

5
examples/mvvmcore/SampleCore/samplecoreapp.cpp

@ -7,6 +7,11 @@ SampleCoreApp::SampleCoreApp(QObject *parent) :
CoreApp(parent)
{}
void SampleCoreApp::performRegistrations()
{
QtMvvm::registerInterfaceConverter<IEventService>();
}
int SampleCoreApp::startApp(const QStringList &arguments)
{
QCommandLineParser parser;

1
examples/mvvmcore/SampleCore/samplecoreapp.h

@ -11,6 +11,7 @@ public:
SampleCoreApp(QObject *parent = nullptr);
protected:
void performRegistrations() override;
int startApp(const QStringList &arguments) override;
};

10
examples/mvvmcore/SampleCore/sampleviewmodel.cpp

@ -1,4 +1,5 @@
#include "sampleviewmodel.h"
#include <QDebug>
SampleViewModel::SampleViewModel(QObject *parent) :
ViewModel(parent),
@ -58,9 +59,9 @@ void SampleViewModel::clearEvents()
emit eventsChanged(_events);
}
void SampleViewModel::onInit()
void SampleViewModel::onInit(const QVariantHash &params)
{
qInfo(Q_FUNC_INFO);
qInfo() << Q_FUNC_INFO << params;
Q_ASSERT(_eventService);
connect(dynamic_cast<QObject*>(_eventService), SIGNAL(eventTriggered(QString)),
this, SLOT(addEvent(QString)));
@ -91,3 +92,8 @@ void SampleViewModel::addEvent(const QString &event)
_events.append(event);
emit eventsChanged(_events);
}
void SampleViewModel::setEventService(IEventService *eventService)
{
_eventService = eventService;
}

9
examples/mvvmcore/SampleCore/sampleviewmodel.h

@ -13,7 +13,8 @@ class SampleViewModel : public QtMvvm::ViewModel
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
Q_PROPERTY(QStringList events READ events NOTIFY eventsChanged RESET clearEvents)
QTMVVM_INJECT_PROP(IEventService*, eventService, _eventService)
Q_PROPERTY(IEventService* eventService MEMBER _eventService WRITE setEventService)
QTMVVM_INJECT(IEventService*, eventService)
public:
Q_INVOKABLE explicit SampleViewModel(QObject *parent = nullptr);
@ -33,7 +34,7 @@ Q_SIGNALS:
void eventsChanged(QStringList events);
protected:
void onInit() override;
void onInit(const QVariantHash &params) override;
void onDestroy() override;
void onShow() override;
void onClose() override;
@ -43,11 +44,13 @@ private Q_SLOTS:
private:
QString _name;
int _active;
bool _active;
QStringList _events;
IEventService* _eventService;
int _eventId;
void setEventService(IEventService *eventService);
};
#endif // SAMPLEVIEWMODEL_H

10
examples/mvvmwidgets/SampleWidgets/SampleWidgets.pro

@ -5,16 +5,16 @@ QT += core gui widgets mvvmwidgets
TARGET = SampleWidgets
HEADERS += \
mainwindow.h \
widgetseventservice.h
widgetseventservice.h \
sampleview.h
SOURCES += \
main.cpp \
mainwindow.cpp \
widgetseventservice.cpp
widgetseventservice.cpp \
sampleview.cpp
FORMS += \
mainwindow.ui
sampleview.ui
target.path = $$[QT_INSTALL_EXAMPLES]/mvvmwidgets/$$TARGET
INSTALLS += target

9
examples/mvvmwidgets/SampleWidgets/main.cpp

@ -1,9 +1,10 @@
#include "mainwindow.h"
#include <QApplication>
#include <QtMvvmCore/ServiceRegistry>
#include <QtMvvmWidgets/WidgetsPresenter>
#include <samplecoreapp.h>
#include "widgetseventservice.h"
#include "sampleview.h"
#define TEST_DIRECT 0
#define TEST_FN 1
@ -16,6 +17,9 @@ int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QtMvvm::WidgetsPresenter::registerAsPresenter();
QtMvvm::WidgetsPresenter::registerView<SampleView>();
if(TEST_CURRENT == TEST_DIRECT)
QtMvvm::ServiceRegistry::instance()->registerObject<EchoService>();
if(TEST_CURRENT == TEST_FN)
@ -34,8 +38,5 @@ int main(int argc, char *argv[])
if(TEST_CURRENT == TEST_INST)
QtMvvm::ServiceRegistry::instance()->registerInterface<IEventService>(new WidgetsEventService(QtMvvm::ServiceRegistry::instance()->service<EchoService>()));
//debug test
auto event = QtMvvm::ServiceRegistry::instance()->service<IEventService>();
return a.exec();
}

14
examples/mvvmwidgets/SampleWidgets/mainwindow.cpp

@ -1,14 +0,0 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}

22
examples/mvvmwidgets/SampleWidgets/mainwindow.h

@ -1,22 +0,0 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

21
examples/mvvmwidgets/SampleWidgets/mainwindow.ui

@ -1,21 +0,0 @@
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle" >
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget" />
</widget>
<layoutDefault spacing="6" margin="11" />
<pixmapfunction></pixmapfunction>
<resources/>
<connections/>
</ui>

15
examples/mvvmwidgets/SampleWidgets/sampleview.cpp

@ -0,0 +1,15 @@
#include "sampleview.h"
#include "ui_sampleview.h"
SampleView::SampleView(QtMvvm::ViewModel *viewModel, QWidget *parent) :
QMainWindow(parent),
_viewModel(static_cast<SampleViewModel*>(viewModel)),
ui(new Ui::SampleView)
{
ui->setupUi(this);
}
SampleView::~SampleView()
{
delete ui;
}

24
examples/mvvmwidgets/SampleWidgets/sampleview.h

@ -0,0 +1,24 @@
#ifndef SAMPLEVIEW_H
#define SAMPLEVIEW_H
#include <QtWidgets/QMainWindow>
#include <sampleviewmodel.h>
namespace Ui {
class SampleView;
}
class SampleView : public QMainWindow
{
Q_OBJECT
public:
Q_INVOKABLE explicit SampleView(QtMvvm::ViewModel *viewModel, QWidget *parent = nullptr);
~SampleView();
private:
SampleViewModel *_viewModel;
Ui::SampleView *ui;
};
#endif // SAMPLEVIEW_H

24
examples/mvvmwidgets/SampleWidgets/sampleview.ui

@ -0,0 +1,24 @@
<ui version="4.0">
<author/>
<comment/>
<exportmacro/>
<class>SampleView</class>
<widget name="SampleView" class="QMainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget name="menubar" class="QMenuBar"/>
<widget name="centralwidget" class="QWidget"/>
<widget name="statusbar" class="QStatusBar"/>
</widget>
<pixmapfunction/>
<connections/>
</ui>

46
src/mvvmcore/coreapp.cpp

@ -1,7 +1,10 @@
#include "coreapp.h"
#include "coreapp_p.h"
#include "qtmvvm_logging_p.h"
#include "serviceregistry_p.h"
#include <QtCore/QCommandLineParser>
#include <QtCore/QRegularExpression>
#include <QtGui/QGuiApplication>
@ -19,6 +22,14 @@ CoreApp *CoreApp::instance()
return CoreAppPrivate::instance;
}
void CoreApp::setMainPresenter(IPresenter *presenter)
{
if(!CoreAppPrivate::instance)
logCritical() << "Failed to set presenter - no core app has been created yet";
else
CoreAppPrivate::instance->d->presenter.reset(presenter);
}
void CoreApp::disableAutoBoot()
{
CoreAppPrivate::bootEnabled = false;
@ -36,6 +47,8 @@ void CoreApp::registerApp()
void CoreApp::bootApp()
{
if(!d->presenter)
logWarning() << "No presenter has been set before the app start";
auto res = startApp(QCoreApplication::arguments());
if(res == EXIT_SUCCESS) {
connect(qApp, &QCoreApplication::aboutToQuit,
@ -73,7 +86,38 @@ bool CoreApp::autoParse(QCommandLineParser &parser, const QStringList &arguments
bool CoreAppPrivate::bootEnabled = true;
QPointer<CoreApp> CoreAppPrivate::instance = nullptr;
CoreAppPrivate::CoreAppPrivate()
CoreAppPrivate::CoreAppPrivate() :
presenter(nullptr)
{}
QScopedPointer<CoreAppPrivate> &CoreAppPrivate::dInstance()
{
return instance->d;
}
void CoreAppPrivate::showViewModel(const QMetaObject *metaObject, const QVariantHash &params, QPointer<ViewModel> parent)
{
if(presenter) {
try {
auto obj = ServiceRegistryPrivate::constructInjected(metaObject);
auto vm = qobject_cast<ViewModel*>(obj);
if(!vm)
throw ServiceConstructionException("Invalid types - not at QtMvvm::ViewModel");
presenter->present(vm, params, parent);
} catch(QException &e) {
logCritical() << "Failed to present viewmodel of type"
<< metaObject->className()
<< "with error:"
<< e.what();
}
} else {
logCritical() << "Failed to present viewmodel of type"
<< metaObject->className()
<< "- no presenter was set";
}
}
IPresenter *CoreAppPrivate::currentPresenter() const
{
return presenter.data();
}

3
src/mvvmcore/coreapp.h

@ -8,6 +8,7 @@ class QCommandLineParser;
#include "QtMvvmCore/qtmvvmcore_global.h"
#include "QtMvvmCore/viewmodel.h"
#include "QtMvvmCore/ipresenter.h"
namespace QtMvvm {
@ -21,6 +22,7 @@ public:
~CoreApp();
static CoreApp *instance();
static void setMainPresenter(IPresenter *presenter);
static void disableAutoBoot();
void registerApp();
@ -38,6 +40,7 @@ protected:
inline void show(const QVariantHash &params = {}) const;
private:
friend class QtMvvm::CoreAppPrivate;
QScopedPointer<CoreAppPrivate> d;
};

12
src/mvvmcore/coreapp_p.h

@ -8,13 +8,23 @@
namespace QtMvvm {
class CoreAppPrivate
class Q_MVVMCORE_EXPORT CoreAppPrivate
{
friend class QtMvvm::CoreApp;
public:
static QScopedPointer<CoreAppPrivate> &dInstance();
void showViewModel(const QMetaObject *metaObject, const QVariantHash &params, QPointer<ViewModel> parent);
IPresenter *currentPresenter() const;
private:
CoreAppPrivate();
static bool bootEnabled;
static QPointer<CoreApp> instance;
QScopedPointer<IPresenter> presenter;
};
}

23
src/mvvmcore/ipresenter.h

@ -0,0 +1,23 @@
#ifndef QTMVVM_IPRESENTER_H
#define QTMVVM_IPRESENTER_H
#include "QtMvvmCore/qtmvvmcore_global.h"
#include "QtMvvmCore/viewmodel.h"
namespace QtMvvm {
class Q_MVVMCORE_EXPORT IPresenter
{
public:
inline virtual ~IPresenter() = default;
virtual void present(ViewModel *viewModel, const QVariantHash &params, QPointer<ViewModel> parent = nullptr) = 0;
};
}
#define QtMvvm_IPresenterIid "de.skycoder42.qtmvvm.core.IPresenter"
Q_DECLARE_INTERFACE(QtMvvm::IPresenter, QtMvvm_IPresenterIid)
Q_DECLARE_METATYPE(QtMvvm::IPresenter*)
#endif // QTMVVM_IPRESENTER_H

3
src/mvvmcore/mvvmcore.pro

@ -11,7 +11,8 @@ HEADERS += \
serviceregistry.h \
serviceregistry_p.h \
qtmvvmcore_helpertypes.h \
qtmvvmcore_logging_p.h
ipresenter.h \
qtmvvm_logging_p.h
SOURCES += \
viewmodel.cpp \

8
src/mvvmcore/qtmvvmcore_logging_p.h → src/mvvmcore/qtmvvm_logging_p.h

@ -1,5 +1,5 @@
#ifndef LOGGING_P_H
#define LOGGING_P_H
#ifndef QTMVVM_LOGGING_P_H
#define QTMVVM_LOGGING_P_H
#include <QtCore/QLoggingCategory>
@ -7,7 +7,7 @@
namespace QtMvvm {
Q_DECLARE_LOGGING_CATEGORY(mvvmcore)
Q_MVVMCORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(mvvmcore)
}
@ -16,4 +16,4 @@ Q_DECLARE_LOGGING_CATEGORY(mvvmcore)
#define logWarning(...) qCWarning(mvvmcore, __VA_ARGS__)
#define logCritical(...) qCCritical(mvvmcore, __VA_ARGS__)
#endif // LOGGING_P_H
#endif // QTMVVM_LOGGING_P_H

2
src/mvvmcore/qtmvvmcore_global.cpp

@ -1,5 +1,5 @@
#include "qtmvvmcore_global.h"
#include "qtmvvmcore_logging_p.h"
#include "qtmvvm_logging_p.h"
namespace QtMvvm {

11
src/mvvmcore/qtmvvmcore_global.h

@ -11,6 +11,17 @@
# define Q_MVVMCORE_EXPORT Q_DECL_IMPORT
#endif
namespace QtMvvm {
template <typename TInterface>
inline void registerInterfaceConverter() {
QMetaType::registerConverter<QObject*, TInterface*>([](QObject *o) {
return qobject_cast<TInterface*>(o);
});
}
}
#define QTMVVM_INJECT(classType, name) \
static inline QByteArray __qtmvvm_inject_##name() { \
return QtMvvm::__helpertypes::inject_iid<classType>(); \

109
src/mvvmcore/serviceregistry.cpp

@ -1,6 +1,6 @@
#include "serviceregistry.h"
#include "serviceregistry_p.h"
#include "qtmvvmcore_logging_p.h"
#include "qtmvvm_logging_p.h"
#include <QtCore/QGlobalStatic>
#include <QtCore/QRegularExpression>
@ -53,6 +53,62 @@ ServiceRegistryPrivate::ServiceRegistryPrivate() :
services()
{}
QObject *ServiceRegistryPrivate::constructInjected(const QMetaObject *metaObject)
{
auto &d = ServiceRegistry::instance()->d;
QMutexLocker _(&d->serviceMutex);
return d->constructInjectedLocked(metaObject);
}
QObject *ServiceRegistryPrivate::constructInjectedLocked(const QMetaObject *metaObject)
{
auto instance = metaObject->newInstance();
if(!instance) {
throw ServiceConstructionException(QByteArrayLiteral("Failed to construct object of type ") +
metaObject->className() +
QByteArrayLiteral(" - make shure there is an invokable constructor of the format: Q_INVOKABLE MyClass(QObject*)"));
}
static QRegularExpression nameRegex(QStringLiteral(R"__(^__qtmvvm_inject_(.+)$)__"),
QRegularExpression::OptimizeOnFirstUsageOption);
try {
for(auto i = 0; i < metaObject->propertyCount(); i++) {
auto prop = metaObject->property(i);
auto match = nameRegex.match(QString::fromUtf8(prop.name()));
if(match.hasMatch()) {
auto tPropIndex = metaObject->indexOfProperty(qUtf8Printable(match.captured(1)));
if(tPropIndex == -1) {
logWarning().noquote() << "Found hint to inject property"
<< match.captured(1)
<< "but no property of that name was found";
continue;
}
auto tProp = metaObject->property(tPropIndex);
auto iid = prop.read(instance).toByteArray();
auto ref = services.value(iid);
if(!ref)
throw ServiceDependencyException(iid);
auto injObj = ref->instance(this, iid);
auto variant = QVariant::fromValue(injObj);
if(!variant.convert(tProp.userType())) {
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*\"");
}
tProp.write(instance, variant); //TODO check if casting works
}
}
return instance;
} catch (...) {
instance->deleteLater();
throw;
}
}
ServiceRegistryPrivate::ServiceInfo::ServiceInfo() :
@ -88,7 +144,7 @@ ServiceRegistryPrivate::FnServiceInfo::FnServiceInfo(const std::function<QObject
QObject *ServiceRegistryPrivate::FnServiceInfo::construct(ServiceRegistryPrivate *d) const
{
QObjectList params;
foreach(auto iid, injectables) {
for(auto iid : injectables) {
auto ref = d->services.value(iid);
if(!ref)
throw ServiceDependencyException(iid);
@ -105,50 +161,13 @@ ServiceRegistryPrivate::MetaServiceInfo::MetaServiceInfo(const QMetaObject *meta
QObject *ServiceRegistryPrivate::MetaServiceInfo::construct(ServiceRegistryPrivate *d) const
{
auto instance = metaObject->newInstance();
if(!instance) {
throw ServiceConstructionException(QByteArrayLiteral("Failed to construct object of type ") +
metaObject->className() +
QByteArrayLiteral(" - make shure there is an invokable constructor of the format: Q_INVOKABLE MyClass(QObject*)"));
}
static QRegularExpression nameRegex(QStringLiteral(R"__(^__qtmvvm_inject_(.+)$)__"),
QRegularExpression::OptimizeOnFirstUsageOption);
try {
for(auto i = 0; i < metaObject->propertyCount(); i++) {
auto prop = metaObject->property(i);
auto match = nameRegex.match(QString::fromUtf8(prop.name()));
if(match.hasMatch()) {
auto tPropIndex = metaObject->indexOfProperty(qUtf8Printable(match.captured(1)));
if(tPropIndex == -1) {
logWarning().noquote() << "Found hint to inject property"
<< match.captured(1)
<< "but no property of that name was found";
continue;
}
auto tProp = metaObject->property(tPropIndex);
auto iid = prop.read(instance).toByteArray();
auto ref = d->services.value(iid);
if(!ref)
throw ServiceDependencyException(iid);
auto injObj = ref->instance(d, iid);
tProp.write(instance, QVariant::fromValue(injObj)); //TODO check if casting works
}
}
auto initMethod = metaObject->indexOfMethod("qtmvvm_init()"); //TODO document
if(initMethod != -1) {
auto method = metaObject->method(initMethod);
method.invoke(instance);
}
return instance;
} catch (...) {
instance->deleteLater();
throw;
auto instance = d->constructInjectedLocked(metaObject);
auto initMethod = metaObject->indexOfMethod("qtmvvm_init()"); //TODO document
if(initMethod != -1) {
auto method = metaObject->method(initMethod);
method.invoke(instance);
}
return instance;
}
// ------------- Exceptions Implementation -------------

1
src/mvvmcore/serviceregistry.h

@ -43,6 +43,7 @@ public:
QObject *serviceObj(const QByteArray &iid);
private:
friend class QtMvvm::ServiceRegistryPrivate;
QScopedPointer<ServiceRegistryPrivate> d;
};

3
src/mvvmcore/serviceregistry_p.h

@ -53,6 +53,9 @@ public:
QHash<QByteArray, QSharedPointer<ServiceInfo>> services;
ServiceRegistryPrivate();
static QObject *constructInjected(const QMetaObject *metaObject);
QObject *constructInjectedLocked(const QMetaObject *metaObject);
};
}

17
src/mvvmcore/viewmodel.cpp

@ -1,5 +1,6 @@
#include "viewmodel.h"
#include "viewmodel_p.h"
#include "coreapp_p.h"
using namespace QtMvvm;
ViewModel::ViewModel(QObject *parent) :
@ -11,7 +12,7 @@ ViewModel::~ViewModel() {}
ViewModel *ViewModel::parentViewModel() const
{
return qobject_cast<ViewModel*>(parent());
return qobject_cast<ViewModel*>(parent()); //TODO not working that way, parent is always the view...
}
bool ViewModel::deleteOnClose() const
@ -28,7 +29,7 @@ void ViewModel::setDeleteOnClose(bool deleteOnClose)
emit deleteOnCloseChanged(deleteOnClose, {});
}
void ViewModel::onInit() {}
void ViewModel::onInit(const QVariantHash &) {}
void ViewModel::onDestroy() {}
@ -36,9 +37,17 @@ void ViewModel::onShow() {}
void ViewModel::onClose() {}
void ViewModel::showImp(const QMetaObject *mo, const QVariantHash &params, ViewModel *parent)
void ViewModel::updateVisible(bool visible)
{
Q_UNIMPLEMENTED();
if(visible)
onShow();
else
onClose();
}
void ViewModel::showImp(const QMetaObject *mo, const QVariantHash &params, QPointer<ViewModel> parent)
{
CoreAppPrivate::dInstance()->showViewModel(mo, params, parent);
}
// ------------- Private Implementation -------------

15
src/mvvmcore/viewmodel.h

@ -6,6 +6,7 @@
#include <QtCore/qobject.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qvariant.h>
#include <QtCore/qpointer.h>
#include "QtMvvmCore/qtmvvmcore_global.h"
@ -32,22 +33,24 @@ public:
public Q_SLOTS:
void setDeleteOnClose(bool deleteOnClose);
Q_SIGNALS:
void deleteOnCloseChanged(bool deleteOnClose, QPrivateSignal);
protected:
virtual void onInit();
virtual void onInit(const QVariantHash &params);
virtual void onDestroy();
virtual void onShow();
virtual void onClose();
void updateVisible(bool visible);
Q_SIGNALS:
void deleteOnCloseChanged(bool deleteOnClose, QPrivateSignal);
protected:
template <typename TViewModel>
inline void showChild(const QVariantHash &params = {}) const;
private:
QScopedPointer<ViewModelPrivate> d;
static void showImp(const QMetaObject *mo, const QVariantHash &params, ViewModel *parent);
static void showImp(const QMetaObject *mo, const QVariantHash &params, QPointer<ViewModel> parent);
};
template<typename TViewModel>

6
src/mvvmwidgets/builddummy.cpp

@ -1,6 +0,0 @@
#include "builddummy.h"
builddummy::builddummy()
{
}

12
src/mvvmwidgets/builddummy.h

@ -1,12 +0,0 @@
#ifndef BUILDDUMMY_H
#define BUILDDUMMY_H
#include "qtmvvmwidgets_global.h"
class Q_MVVMWIDGETS_EXPORT builddummy
{
public:
builddummy();
};
#endif // BUILDDUMMY_H

20
src/mvvmwidgets/ipresentingview.h

@ -0,0 +1,20 @@
#ifndef QTMVVM_IPRESENTINGVIEW_H
#define QTMVVM_IPRESENTINGVIEW_H
#include <QtWidgets/qwidget.h>
#include "QtMvvmWidgets/qtmvvmwidgets_global.h"
namespace QtMvvm {
class Q_MVVMWIDGETS_EXPORT IPresentingView
{
public:
inline virtual ~IPresentingView() = default;
virtual bool tryPresent(QWidget *view) = 0;
};
}
#endif // QTMVVM_IPRESENTINGVIEW_H

10
src/mvvmwidgets/mvvmwidgets.pro

@ -1,13 +1,15 @@
TARGET = QtMvvmWidgets
QT = core gui widgets mvvmcore
QT = core gui widgets mvvmcore mvvmcore-private
HEADERS += \
qtmvvmwidgets_global.h \
builddummy.h
qtmvvmwidgets_global.h \
widgetspresenter.h \
ipresentingview.h \
widgetspresenter_p.h
SOURCES += \
builddummy.cpp
widgetspresenter.cpp
TRANSLATIONS += \
translations/qtmvvmwidgets_de.ts \

222
src/mvvmwidgets/widgetspresenter.cpp

@ -0,0 +1,222 @@
#include "widgetspresenter.h"
#include "widgetspresenter_p.h"
#include "ipresentingview.h"
#include <QtMvvmCore/private/coreapp_p.h>
#include <QtMvvmCore/private/qtmvvm_logging_p.h>
#include <QtWidgets/QDialog>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QDockWidget>
#include <QtWidgets/QMdiSubWindow>
#include <QtWidgets/QMdiArea>
#include <QtWidgets/QApplication>
using namespace QtMvvm;
WidgetsPresenter::WidgetsPresenter(QObject *parent) :
QObject(parent),
IPresenter(),
d(new WidgetsPresenterPrivate())
{}
WidgetsPresenter::~WidgetsPresenter() {}
void WidgetsPresenter::registerView(const QMetaObject *viewType)
{
Q_ASSERT_X(viewType->inherits(&QWidget::staticMetaObject), Q_FUNC_INFO, "viewType must be a QWidget class");
WidgetsPresenterPrivate::currentPresenter()->d->implicitMappings.insert(viewType);
}
void WidgetsPresenter::registerViewExplicitly(const QMetaObject *viewModelType, const QMetaObject *viewType)
{
Q_ASSERT_X(viewModelType->inherits(&ViewModel::staticMetaObject), Q_FUNC_INFO, "viewModelType must be a QtMvvm::ViewModel class");
Q_ASSERT_X(viewType->inherits(&QWidget::staticMetaObject), Q_FUNC_INFO, "viewType must be a QWidget class");
WidgetsPresenterPrivate::currentPresenter()->d->explicitMappings.insert(viewModelType->className(), viewType);
}
void WidgetsPresenter::present(ViewModel *viewModel, const QVariantHash &params, QPointer<ViewModel> parent)
{
// 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;
}
auto parentView = parent ?
qobject_cast<QWidget*>(parent->parent()) :
nullptr;
auto view = qobject_cast<QWidget*>(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;
}
// initialize viewmodel and view relationship
QMetaObject::Connection noCycleDestroyCon;
viewModel->setParent(view);
auto hasCycle = setupLifeCycle(viewModel, view);
if(hasCycle) {
QObject::connect(view, &QWidget::destroyed,
viewModel, &ViewModel::onDestroy,
Qt::DirectConnection);
} else {
view->setAttribute(Qt::WA_DeleteOnClose);
noCycleDestroyCon = QObject::connect(view, &QWidget::destroyed, viewModel, [viewModel](){
viewModel->onClose();
viewModel->onDestroy();
}, Qt::DirectConnection);
}
viewModel->onInit(params);
// present the view
auto presented = false;
auto tPresenter = dynamic_cast<IPresentingView*>(parentView);
if(tPresenter)
presented = tPresenter->tryPresent(view);
if(!presented)
presented = tryPresent(view, parentView);
//handle the present result
if(presented) {
//if no lifecycle explicit on show is needed
if(!hasCycle)
viewModel->onShow();
} else {
logCritical() << "Unable to find a method that is able to present a view of type"
<< viewMetaObject->className();
//if no lifecycle only destroy on error, not close and destroy
if(!hasCycle) {
view->disconnect(noCycleDestroyCon);
viewModel->onDestroy();
}
view->deleteLater();
}
}
const QMetaObject *WidgetsPresenter::findWidgetMetaObject(const QMetaObject *viewModelMetaObject)
{
auto currentMeta = viewModelMetaObject;
while(currentMeta && currentMeta->inherits(&ViewModel::staticMetaObject)) {
QByteArray cName = currentMeta->className();
if(d->explicitMappings.contains(cName))
return d->explicitMappings.value(cName);
else {
auto lIndex = cName.lastIndexOf("ViewModel");
if(lIndex > 0)
cName.truncate(lIndex);
for(auto metaObject : d->implicitMappings) {
QByteArray vName = metaObject->className();
if(vName.startsWith(cName))
return metaObject;
}
}
currentMeta = currentMeta->superClass();
}
return nullptr;
}
bool WidgetsPresenter::tryPresent(QWidget *view, QWidget *parentView)
{
auto metaObject = view->metaObject();
// Check if QDialog
if(metaObject->inherits(&QDialog::staticMetaObject)) {
qobject_cast<QDialog*>(view)->open();
return true;
}
//Check if QMainWindow
QWidget *central = nullptr;
if(parentView && parentView->metaObject()->inherits(&QMainWindow::staticMetaObject)) {
auto mainWindow = qobject_cast<QMainWindow*>(parentView);
// Check if child is QDockWidget
if(metaObject->inherits(&QDockWidget::staticMetaObject)) {
auto dockWidget = qobject_cast<QDockWidget*>(view);
if(!mainWindow->restoreDockWidget(dockWidget))
mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dockWidget);
return true;
}
//extract central widget for futher parts
central = mainWindow->centralWidget();
}
// for both, the parent and the central view, depending on which is available
for(auto pView : QList<QWidget*>{} << parentView << central) {
if(!pView)
continue;
// Check if QMdiArea and child is QMdiSubWindow
if(pView->metaObject()->inherits(&QMdiArea::staticMetaObject) &&
metaObject->inherits(&QMdiSubWindow::staticMetaObject)) {
auto mdiArea = qobject_cast<QMdiArea*>(pView);
mdiArea->addSubWindow(view);
return true;
}
}
//none of the special cases -> simply "show" it
showForeground(view);
return true;
}
bool WidgetsPresenter::setupLifeCycle(ViewModel *viewModel, QWidget *view)
{
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;
}
return false;
}
void WidgetsPresenter::showForeground(QWidget *view) const
{
if(view->windowState().testFlag(Qt::WindowMinimized))
view->setWindowState(view->windowState() & ~Qt::WindowMinimized);
view->show();
view->raise();
QApplication::alert(view);
view->activateWindow();
}
// ------------- Private Implementation -------------
WidgetsPresenterPrivate::WidgetsPresenterPrivate() :
implicitMappings(),
explicitMappings()
{}
WidgetsPresenter *WidgetsPresenterPrivate::currentPresenter()
{
#ifndef Q_NO_DEBUG
Q_ASSERT_X(dynamic_cast<WidgetsPresenter*>(CoreAppPrivate::dInstance()->currentPresenter()),
Q_FUNC_INFO,
"Cannot register widgets if the current presenter does not extend QtMvvm::WidgetsPresenter");
#endif
auto presenter = static_cast<WidgetsPresenter*>(CoreAppPrivate::dInstance()->currentPresenter());
if(!presenter) {
presenter = new WidgetsPresenter();
CoreApp::setMainPresenter(presenter);
}
return presenter;
}

76
src/mvvmwidgets/widgetspresenter.h

@ -0,0 +1,76 @@
#ifndef QTMVVM_WIDGETSPRESENTER_H
#define QTMVVM_WIDGETSPRESENTER_H
#include <QtCore/qobject.h>
#include <QtCore/qscopedpointer.h>
#include <QtMvvmCore/ipresenter.h>
#include <QtMvvmCore/coreapp.h>
#include <QtWidgets/qwidget.h>
#include "QtMvvmWidgets/qtmvvmwidgets_global.h"
namespace QtMvvm {
class WidgetsPresenterPrivate;
class Q_MVVMWIDGETS_EXPORT WidgetsPresenter : public QObject, public IPresenter
{
Q_OBJECT
Q_INTERFACES(QtMvvm::IPresenter)
public:
explicit WidgetsPresenter(QObject *parent = nullptr);
~WidgetsPresenter();
template <typename TPresenter = WidgetsPresenter>
static void registerAsPresenter();
template <typename TView>
static void registerView();
static void registerView(const QMetaObject *viewType);
template <typename TViewModel, typename TView>
static void registerViewExplicitly();
static void registerViewExplicitly(const QMetaObject *viewModelType, const QMetaObject *viewType);
void present(ViewModel *viewModel, const QVariantHash &params, QPointer<ViewModel> parent) override;
protected:
virtual const QMetaObject *findWidgetMetaObject(const QMetaObject *viewModelMetaObject);
virtual bool tryPresent(QWidget *view, QWidget *parentView);
virtual bool setupLifeCycle(ViewModel *viewModel, QWidget *view);
virtual void showForeground(QWidget *view) const;
private:
QScopedPointer<WidgetsPresenterPrivate> d;
};
// ------------- Generic Implementation -------------
template<typename TPresenter>
void WidgetsPresenter::registerAsPresenter()
{
static_assert(std::is_base_of<WidgetsPresenter, TPresenter>::value, "TPresenter must inherit QtMvvm::WidgetsPresenter!");
CoreApp::setMainPresenter(new TPresenter());
}
template<typename TView>
void WidgetsPresenter::registerView()
{
static_assert(std::is_base_of<QWidget, TView>::value, "TWidget must inherit QWidget!");
registerView(&TView::staticMetaObject);
}
template<typename TViewModel, typename TView>
void WidgetsPresenter::registerViewExplicitly()
{
static_assert(std::is_base_of<QWidget, TView>::value, "TWidget must inherit QWidget!");
static_assert(std::is_base_of<ViewModel, TViewModel>::value, "TViewModel must inherit ViewModel!");
registerViewExplicitly(&TViewModel::staticMetaObject, &TView::staticMetaObject);
}
}
#endif // QTMVVM_WIDGETSPRESENTER_H

25
src/mvvmwidgets/widgetspresenter_p.h

@ -0,0 +1,25 @@
#ifndef QTMVVM_WIDGETSPRESENTER_P_H
#define QTMVVM_WIDGETSPRESENTER_P_H
#include <QtCore/qset.h>
#include <QtCore/qhash.h>
#include "qtmvvmwidgets_global.h"
#include "widgetspresenter.h"
namespace QtMvvm {
class WidgetsPresenterPrivate
{
public:
WidgetsPresenterPrivate();
static WidgetsPresenter *currentPresenter();
QSet<const QMetaObject*> implicitMappings;
QHash<QByteArray, const QMetaObject*> explicitMappings;
};
}
#endif // QTMVVM_WIDGETSPRESENTER_P_H
Loading…
Cancel
Save