32 changed files with 632 additions and 156 deletions
@ -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; |
|
||||
} |
|
@ -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
|
|
@ -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> |
|
@ -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; |
||||
|
} |
@ -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
|
@ -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> |
@ -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 ¶ms, 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
|
@ -1,6 +0,0 @@ |
|||||
#include "builddummy.h" |
|
||||
|
|
||||
builddummy::builddummy() |
|
||||
{ |
|
||||
|
|
||||
} |
|
@ -1,12 +0,0 @@ |
|||||
#ifndef BUILDDUMMY_H |
|
||||
#define BUILDDUMMY_H |
|
||||
|
|
||||
#include "qtmvvmwidgets_global.h" |
|
||||
|
|
||||
class Q_MVVMWIDGETS_EXPORT builddummy |
|
||||
{ |
|
||||
public: |
|
||||
builddummy(); |
|
||||
}; |
|
||||
|
|
||||
#endif // BUILDDUMMY_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
|
@ -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 ¶ms, 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; |
||||
|
} |
@ -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 ¶ms, 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
|
@ -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…
Reference in new issue