Skycoder42
7 years ago
16 changed files with 553 additions and 19 deletions
@ -0,0 +1,31 @@ |
|||
#include "containerviewmodel.h" |
|||
|
|||
ContainerViewModel::ContainerViewModel(QObject *parent) : |
|||
ViewModel(parent) |
|||
{} |
|||
|
|||
ContainerViewModel::~ContainerViewModel() |
|||
{ |
|||
qInfo(Q_FUNC_INFO); |
|||
} |
|||
|
|||
QString ContainerViewModel::vmType() const |
|||
{ |
|||
return QString::fromUtf8(QMetaType::typeName(qMetaTypeId<ChildViewModel*>())); |
|||
} |
|||
|
|||
void ContainerViewModel::loadChild() |
|||
{ |
|||
show<ChildViewModel>(); |
|||
} |
|||
|
|||
|
|||
|
|||
ChildViewModel::ChildViewModel(QObject *parent) : |
|||
ViewModel(parent) |
|||
{} |
|||
|
|||
ChildViewModel::~ChildViewModel() |
|||
{ |
|||
qInfo(Q_FUNC_INFO); |
|||
} |
@ -0,0 +1,34 @@ |
|||
#ifndef CONTAINERVIEWMODEL_H |
|||
#define CONTAINERVIEWMODEL_H |
|||
|
|||
#include <QtMvvmCore/ViewModel> |
|||
|
|||
class ChildViewModel : public QtMvvm::ViewModel |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
public: |
|||
Q_INVOKABLE explicit ChildViewModel(QObject *parent = nullptr); |
|||
~ChildViewModel(); |
|||
}; |
|||
|
|||
class ContainerViewModel : public QtMvvm::ViewModel |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
Q_PROPERTY(QString vmType READ vmType CONSTANT) |
|||
|
|||
public: |
|||
Q_INVOKABLE explicit ContainerViewModel(QObject *parent = nullptr); |
|||
~ContainerViewModel(); |
|||
|
|||
QString vmType() const; |
|||
|
|||
public Q_SLOTS: |
|||
void loadChild(); |
|||
}; |
|||
|
|||
Q_DECLARE_METATYPE(ChildViewModel*) |
|||
Q_DECLARE_METATYPE(ContainerViewModel*) |
|||
|
|||
#endif // CONTAINERVIEWMODEL_H
|
@ -0,0 +1,29 @@ |
|||
import QtQuick 2.10 |
|||
import QtQuick.Controls 2.3 |
|||
import de.skycoder42.QtMvvm.Core 1.1 |
|||
import de.skycoder42.QtMvvm.Quick 1.1 |
|||
import de.skycoder42.QtMvvm.Sample 1.1 |
|||
|
|||
Rectangle { |
|||
property ChildViewModel viewModel: null |
|||
|
|||
color: "red" |
|||
|
|||
property bool dummyClosed: false |
|||
|
|||
function closeAction() { |
|||
if(dummyClosed) |
|||
return false; |
|||
else { |
|||
dummyClosed = true; |
|||
label.text = qsTr("I was closed!") |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
Label { |
|||
id: label |
|||
anchors.centerIn: parent |
|||
text: qsTr("I am the child!") |
|||
} |
|||
} |
@ -0,0 +1,71 @@ |
|||
import QtQuick 2.10 |
|||
import QtQuick.Controls 2.3 |
|||
import QtQuick.Layouts 1.3 |
|||
import de.skycoder42.QtMvvm.Core 1.1 |
|||
import de.skycoder42.QtMvvm.Quick 1.1 |
|||
import de.skycoder42.QtMvvm.Sample 1.1 |
|||
|
|||
Page { |
|||
id: containerView |
|||
property ContainerViewModel viewModel: null |
|||
|
|||
function presentItem(item) { |
|||
return viewPlaceholder.presentItem(item); |
|||
} |
|||
|
|||
function closeAction() { |
|||
return viewPlaceholder.closeAction(); |
|||
} |
|||
|
|||
header: ContrastToolBar { |
|||
ToolBarLabel { |
|||
text: qsTr("View Container") |
|||
anchors.fill: parent |
|||
} |
|||
} |
|||
|
|||
PresenterProgress {} |
|||
|
|||
Pane { |
|||
anchors.fill: parent |
|||
|
|||
ColumnLayout { |
|||
anchors.fill: parent |
|||
|
|||
ViewPlaceholder { |
|||
id: viewPlaceholder |
|||
|
|||
viewModelType: containerView.viewModel.vmType |
|||
parentViewModel: containerView.viewModel |
|||
autoPresent: false |
|||
|
|||
Layout.fillWidth: true |
|||
Layout.fillHeight: true |
|||
|
|||
BusyIndicator { |
|||
anchors.centerIn: parent |
|||
anchors.verticalCenterOffset: -(hookButton.height/2) |
|||
running: !viewPlaceholder.loadedView |
|||
} |
|||
|
|||
Button { |
|||
id: hookButton |
|||
Layout.fillWidth: true |
|||
text: qsTr("Load from QML") |
|||
onClicked: viewPlaceholder.presentView(); |
|||
enabled: !viewPlaceholder.loadedView |
|||
|
|||
anchors.left: parent.left |
|||
anchors.right: parent.right |
|||
anchors.bottom: parent.bottom |
|||
} |
|||
} |
|||
|
|||
Button { |
|||
Layout.fillWidth: true |
|||
text: viewPlaceholder.loadedView ? qsTr("Discard child") : qsTr("Show Child") |
|||
onClicked: viewPlaceholder.loadedView ? viewPlaceholder.discardView() : viewModel.loadChild(); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,201 @@ |
|||
#include "qqmlviewplaceholder.h" |
|||
#include <QtCore/QMetaMethod> |
|||
#include <QtQml/QQmlInfo> |
|||
#include <QtQml/QQmlProperty> |
|||
#include <QtMvvmCore/CoreApp> |
|||
using namespace QtMvvm; |
|||
|
|||
QQmlViewPlaceholder::QQmlViewPlaceholder(QQuickItem *parent) : |
|||
QQuickItem{parent} |
|||
{ |
|||
// auto presenting
|
|||
connect(this, &QQmlViewPlaceholder::viewModelTypeChanged, |
|||
this, &QQmlViewPlaceholder::presentIfReady); |
|||
connect(this, &QQmlViewPlaceholder::autoPresentChanged, |
|||
this, &QQmlViewPlaceholder::presentIfReady); |
|||
|
|||
// size changes
|
|||
connect(this, &QQmlViewPlaceholder::widthChanged, |
|||
this, &QQmlViewPlaceholder::resizeView); |
|||
connect(this, &QQmlViewPlaceholder::heightChanged, |
|||
this, &QQmlViewPlaceholder::resizeView); |
|||
connect(this, &QQmlViewPlaceholder::autoResizeViewChanged, |
|||
this, &QQmlViewPlaceholder::resizeView); |
|||
} |
|||
|
|||
bool QQmlViewPlaceholder::presentItem(const QVariant &item) |
|||
{ |
|||
// check if the parameter is valid
|
|||
auto quickItem = item.value<QQuickItem*>(); |
|||
if(!quickItem) { |
|||
qmlWarning(this) << "presentItem called with invalid item of type " << item.typeName(); |
|||
return false; |
|||
} |
|||
|
|||
// handle already existing view case
|
|||
if(_loadedView) { |
|||
if(_replaceViews) { // quick discard without reenableing all children
|
|||
_loadedView->setVisible(false); |
|||
_loadedView->deleteLater(); |
|||
_loadedView = nullptr; |
|||
} else { |
|||
qmlWarning(this) << R"(A view has already been presented. Discard it first via "discardView" or set "replaceViews" to true)"; |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
// add
|
|||
_loadedView = quickItem; |
|||
quickItem->setParent(this); |
|||
quickItem->setParentItem(this); |
|||
resizeView(); |
|||
quickItem->setVisible(true); |
|||
|
|||
// hide all children
|
|||
for(auto child : childItems()) { |
|||
if(child != _loadedView) |
|||
child->setVisible(false); |
|||
} |
|||
|
|||
emit loadedViewChanged(_loadedView); |
|||
return true; |
|||
} |
|||
|
|||
bool QQmlViewPlaceholder::closeAction() |
|||
{ |
|||
if(!_loadedView) |
|||
return false; |
|||
|
|||
// call close on the view
|
|||
auto vMetaObject = _loadedView->metaObject(); |
|||
auto cMethodIndex = vMetaObject->indexOfMethod("closeAction()"); |
|||
if(cMethodIndex != -1) { |
|||
auto cMethod = vMetaObject->method(cMethodIndex); |
|||
QVariant ok = false; |
|||
cMethod.invoke(_loadedView, Q_RETURN_ARG(QVariant, ok)); |
|||
if(ok.toBool()) |
|||
return true; |
|||
} |
|||
|
|||
// close the view itself (if wished)
|
|||
if(_closeViewOnAction) { |
|||
discardView(); |
|||
return true; |
|||
} |
|||
|
|||
// otherwise: nothing closed
|
|||
return false; |
|||
} |
|||
|
|||
void QQmlViewPlaceholder::setParentViewModel(ViewModel *parentViewModel) |
|||
{ |
|||
// first: clear the auto-connected viewmodel, if required
|
|||
if(_clearParentVmCon && _parentVmCon) |
|||
disconnect(_parentVmCon); |
|||
|
|||
// then: set property as usual
|
|||
if(_parentViewModel == parentViewModel) |
|||
return; |
|||
|
|||
_parentViewModel = parentViewModel; |
|||
emit parentViewModelChanged(_parentViewModel); |
|||
|
|||
// check the vm parent for a presenter method
|
|||
auto view = qobject_cast<QQuickItem*>(_parentViewModel->parent()); |
|||
if(view) { |
|||
if(view->metaObject()->indexOfMethod("presentItem(QVariant)") == -1) |
|||
qmlWarning(this) << R"(Parent item of "parentViewModel" does not have a "presentItem" method. Check the ViewPlaceholder documentation!)"; |
|||
} else |
|||
qmlWarning(this) << R"(Parent item of "parentViewModel" is not an Item)"; |
|||
|
|||
presentIfReady(); |
|||
} |
|||
|
|||
QQuickItem *QQmlViewPlaceholder::loadedView() const |
|||
{ |
|||
return _loadedView; |
|||
} |
|||
|
|||
void QQmlViewPlaceholder::componentComplete() |
|||
{ |
|||
// auto-set the vm if not already set
|
|||
if(!_parentViewModel) |
|||
getParentViewModel(); |
|||
|
|||
// last step: call base implementation, then present
|
|||
QQuickItem::componentComplete(); |
|||
_isReady = true; |
|||
presentIfReady(); |
|||
} |
|||
|
|||
void QQmlViewPlaceholder::presentView() |
|||
{ |
|||
CoreApp::show(qUtf8Printable(_viewModelType), _showParams, _parentViewModel); |
|||
} |
|||
|
|||
void QQmlViewPlaceholder::discardView() |
|||
{ |
|||
// hide view
|
|||
_loadedView->setVisible(false); |
|||
|
|||
// show all children again
|
|||
for(auto child : childItems()) { |
|||
if(child != _loadedView) |
|||
child->setVisible(true); |
|||
} |
|||
|
|||
// now delete it
|
|||
_loadedView->deleteLater(); |
|||
_loadedView = nullptr; |
|||
emit loadedViewChanged(nullptr); |
|||
} |
|||
|
|||
void QQmlViewPlaceholder::parentItemVmChanged(ViewModel *viewModel) |
|||
{ |
|||
// set vm without clearing the connection
|
|||
_clearParentVmCon = false; |
|||
setParentViewModel(viewModel); |
|||
_clearParentVmCon = true; |
|||
} |
|||
|
|||
void QQmlViewPlaceholder::presentIfReady() |
|||
{ |
|||
if(_isReady && |
|||
_autoPresent && |
|||
!_loadedView && |
|||
_parentViewModel && |
|||
!_viewModelType.isEmpty()) |
|||
presentView(); |
|||
} |
|||
|
|||
void QQmlViewPlaceholder::resizeView() |
|||
{ |
|||
if(_loadedView && _autoResizeView) { |
|||
_loadedView->setWidth(width()); |
|||
_loadedView->setHeight(height()); |
|||
} |
|||
} |
|||
|
|||
void QQmlViewPlaceholder::getParentViewModel() |
|||
{ |
|||
auto pItem = parentItem(); |
|||
if(!pItem) |
|||
return; |
|||
|
|||
QQmlProperty vmProp{pItem, QStringLiteral("viewModel")}; |
|||
if(!vmProp.isValid()) |
|||
return; |
|||
|
|||
// set the vm from the property
|
|||
auto vm = vmProp.read().value<ViewModel*>(); |
|||
if(vm) |
|||
setParentViewModel(vm); // no warning here - might be ok for lazy presented vms
|
|||
|
|||
// connect to further changes, via helper slot
|
|||
auto cSlotIndex = metaObject()->indexOfSlot("parentItemVmChanged(QtMvvm::ViewModel*)"); |
|||
Q_ASSERT(cSlotIndex != -1); |
|||
if(_parentVmCon) |
|||
disconnect(_parentVmCon); |
|||
_parentVmCon = connect(pItem, vmProp.property().notifySignal(), |
|||
this, metaObject()->method(cSlotIndex)); |
|||
} |
@ -0,0 +1,77 @@ |
|||
#ifndef QTMVVM_QQMLVIEWPLACEHOLDER_H |
|||
#define QTMVVM_QQMLVIEWPLACEHOLDER_H |
|||
|
|||
#include <QtQuick/QQuickItem> |
|||
|
|||
#include <QtMvvmCore/ViewModel> |
|||
|
|||
namespace QtMvvm { |
|||
|
|||
class QQmlViewPlaceholder : public QQuickItem |
|||
{ |
|||
Q_OBJECT |
|||
|
|||
Q_PROPERTY(QString viewModelType MEMBER _viewModelType NOTIFY viewModelTypeChanged) |
|||
Q_PROPERTY(QVariantHash showParams MEMBER _showParams NOTIFY showParamsChanged) |
|||
Q_PROPERTY(QtMvvm::ViewModel* parentViewModel MEMBER _parentViewModel WRITE setParentViewModel NOTIFY parentViewModelChanged) |
|||
Q_PROPERTY(bool autoPresent MEMBER _autoPresent NOTIFY autoPresentChanged) |
|||
Q_PROPERTY(bool autoResizeView MEMBER _autoResizeView NOTIFY autoResizeViewChanged) |
|||
Q_PROPERTY(bool replaceViews MEMBER _replaceViews NOTIFY replaceViewsChanged) |
|||
Q_PROPERTY(bool closeViewOnAction MEMBER _closeViewOnAction NOTIFY closeViewOnActionChanged) |
|||
|
|||
Q_PROPERTY(QQuickItem* loadedView READ loadedView NOTIFY loadedViewChanged) |
|||
|
|||
public: |
|||
explicit QQmlViewPlaceholder(QQuickItem *parent = nullptr); |
|||
|
|||
Q_INVOKABLE bool presentItem(const QVariant &item); |
|||
Q_INVOKABLE bool closeAction(); |
|||
|
|||
void setParentViewModel(ViewModel *parentViewModel); |
|||
QQuickItem* loadedView() const; |
|||
|
|||
void componentComplete() override; |
|||
|
|||
public Q_SLOTS: |
|||
void presentView(); |
|||
void discardView(); |
|||
|
|||
Q_SIGNALS: |
|||
void viewModelTypeChanged(const QString &viewModelType); |
|||
void showParamsChanged(const QVariantHash &showParams); |
|||
void parentViewModelChanged(QtMvvm::ViewModel* parentViewModel); |
|||
void autoPresentChanged(bool autoPresent); |
|||
void autoResizeViewChanged(bool autoResizeView); |
|||
void replaceViewsChanged(bool replaceViews); |
|||
void closeViewOnActionChanged(bool closeViewOnAction); |
|||
void loadedViewChanged(QQuickItem* loadedView); |
|||
|
|||
private Q_SLOTS: |
|||
void parentItemVmChanged(QtMvvm::ViewModel *viewModel); |
|||
void presentIfReady(); |
|||
|
|||
void resizeView(); |
|||
|
|||
private: |
|||
QString _viewModelType; |
|||
QVariantHash _showParams; |
|||
ViewModel *_parentViewModel = nullptr; |
|||
bool _autoPresent = true; |
|||
bool _autoResizeView = true; |
|||
bool _replaceViews = false; |
|||
bool _closeViewOnAction = false; |
|||
|
|||
QPointer<QQuickItem> _loadedView = nullptr; |
|||
|
|||
bool _isReady = false; |
|||
bool _clearParentVmCon = true; |
|||
QMetaObject::Connection _parentVmCon; |
|||
|
|||
void getParentViewModel(); |
|||
void connectSizeChanges(); |
|||
void disconnectSizeChanges(); |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif // QTMVVM_QQMLVIEWPLACEHOLDER_H
|
Loading…
Reference in new issue