diff --git a/.qmake.conf b/.qmake.conf index b03d030..6cf6b00 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -5,4 +5,8 @@ CONFIG += warning_clean exceptions c++14 win32:cross_compile: CONFIG += winrt DEFINES += QT_DEPRECATED_WARNINGS QT_ASCII_CAST_WARNINGS -MODULE_VERSION = 1.0.0 +MODULE_VERSION_MAJOR = 1 +MODULE_VERSION_MINOR = 0 +MODULE_VERSION_PATCH = 0 +MODULE_VERSION_IMPORT = $${MODULE_VERSION_MAJOR}.$${MODULE_VERSION_MINOR} +MODULE_VERSION = $${MODULE_VERSION_MAJOR}.$${MODULE_VERSION_MINOR}.$${MODULE_VERSION_PATCH} diff --git a/examples/mvvmquick/SampleQuick/SampleQuick.pro b/examples/mvvmquick/SampleQuick/SampleQuick.pro index ad1be61..43522aa 100644 --- a/examples/mvvmquick/SampleQuick/SampleQuick.pro +++ b/examples/mvvmquick/SampleQuick/SampleQuick.pro @@ -40,14 +40,14 @@ else:unix: PRE_TARGETDEPS += $$OUT_PWD/../../mvvmcore/SampleCore/libSampleCore.a #hacky code to make it possible to use the example from within a shadowed build samples_in_build { # first, create a fake qml imports dir - FAKEPATH = qml/de/skycoder42/qtmvvm/quick - ORIGPATH = ../../../../../../../../qml/de/skycoder42/qtmvvm/quick + FAKEPATH = qml/de/skycoder42/QtMvvm/Quick + ORIGPATH = ../../../../../../../../qml/de/skycoder42/QtMvvm/Quick QMLDEPPATH = $$PWD/../../../src/imports/mvvmquick system($$QMAKE_MKDIR $$shell_quote($$shell_path($$FAKEPATH))) # next, symlink all "compiled" files (whole dir for core, as it has no qml files build_symlink_target.target = create_qml_build_symlinks - build_symlink_target.commands += $$QMAKE_SYMBOLIC_LINK $$shell_path(../../../../../../../qml/de/skycoder42/qtmvvm/core) $$shell_path(qml/de/skycoder42/qtmvvm/core) \ + build_symlink_target.commands += $$QMAKE_SYMBOLIC_LINK $$shell_path(../../../../../../../qml/de/skycoder42/QtMvvm/Core) $$shell_path(qml/de/skycoder42/QtMvvm/Core) \ $$escape_expand(\n\t)$$QMAKE_SYMBOLIC_LINK $$shell_path($$ORIGPATH/libdeclarative_mvvmquick.so) $$shell_path($$FAKEPATH/libdeclarative_mvvmquick.so) \ $$escape_expand(\n\t)$$QMAKE_SYMBOLIC_LINK $$shell_path($$ORIGPATH/plugins.qmltypes) $$shell_path($$FAKEPATH/plugins.qmltypes) \ $$escape_expand(\n\t)$$QMAKE_SYMBOLIC_LINK $$shell_path($$ORIGPATH/qmldir) $$shell_path($$FAKEPATH/qmldir) diff --git a/examples/mvvmquick/SampleQuick/main.qml b/examples/mvvmquick/SampleQuick/main.qml index c8a3907..b3e4ff0 100644 --- a/examples/mvvmquick/SampleQuick/main.qml +++ b/examples/mvvmquick/SampleQuick/main.qml @@ -1,6 +1,6 @@ import QtQuick 2.10 -import de.skycoder42.qtmvvm.core 1.0 -import de.skycoder42.qtmvvm.quick 1.0 +import de.skycoder42.QtMvvm.Core 1.0 +import de.skycoder42.QtMvvm.Quick 1.0 QtMvvmApp { title: qsTr("QtMvvm Quick Sample") diff --git a/src/imports/mvvmcore/mvvmcore.pro b/src/imports/mvvmcore/mvvmcore.pro index 5749c46..ac4f2f9 100644 --- a/src/imports/mvvmcore/mvvmcore.pro +++ b/src/imports/mvvmcore/mvvmcore.pro @@ -1,18 +1,20 @@ QT += core qml quick mvvmcore CXX_MODULE = mvvmcore -TARGETPATH = de/skycoder42/qtmvvm/core +TARGETPATH = de/skycoder42/QtMvvm/Core TARGET = declarative_mvvmcore -IMPORT_VERSION = 1.0 +IMPORT_VERSION = $$MODULE_VERSION_IMPORT +DEFINES += "VERSION_MAJOR=$$MODULE_VERSION_MAJOR" +DEFINES += "VERSION_MINOR=$$MODULE_VERSION_MINOR" HEADERS += \ qtmvvmcore_plugin.h \ qqmlmvvmbinding.h \ - qqmlmvvmmessage.h + qqmlmvvmmessage.h SOURCES += \ qtmvvmcore_plugin.cpp \ qqmlmvvmbinding.cpp \ - qqmlmvvmmessage.cpp + qqmlmvvmmessage.cpp OTHER_FILES += qmldir diff --git a/src/imports/mvvmcore/plugins.qmltypes b/src/imports/mvvmcore/plugins.qmltypes index aecdbb5..c6d8234 100644 --- a/src/imports/mvvmcore/plugins.qmltypes +++ b/src/imports/mvvmcore/plugins.qmltypes @@ -4,14 +4,14 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable de.skycoder42.qtmvvm.core 1.0' +// 'qmlplugindump -nonrelocatable de.skycoder42.QtMvvm.Core 1.0' Module { dependencies: ["QtQuick 2.8"] Component { name: "QtMvvm::QQmlMvvmBinding" prototype: "QObject" - exports: ["de.skycoder42.qtmvvm.core/Binding 1.0"] + exports: ["de.skycoder42.QtMvvm.Core/Binding 1.0"] exportMetaObjectRevisions: [0] Enum { name: "BindingDirection" @@ -57,7 +57,7 @@ Module { Component { name: "QtMvvm::QQmlMvvmMessage" prototype: "QObject" - exports: ["de.skycoder42.qtmvvm.core/Message 1.0"] + exports: ["de.skycoder42.QtMvvm.Core/Message 1.0"] isCreatable: false isSingleton: true exportMetaObjectRevisions: [0] diff --git a/src/imports/mvvmcore/qmldir b/src/imports/mvvmcore/qmldir index 2681fa5..0b1de1e 100644 --- a/src/imports/mvvmcore/qmldir +++ b/src/imports/mvvmcore/qmldir @@ -1,4 +1,4 @@ -module de.skycoder42.qtmvvm.core +module de.skycoder42.QtMvvm.Core plugin declarative_mvvmcore classname QtMvvmCoreDeclarativeModule typeinfo plugins.qmltypes diff --git a/src/imports/mvvmcore/qtmvvmcore_plugin.cpp b/src/imports/mvvmcore/qtmvvmcore_plugin.cpp index 2815d0a..0ba593d 100644 --- a/src/imports/mvvmcore/qtmvvmcore_plugin.cpp +++ b/src/imports/mvvmcore/qtmvvmcore_plugin.cpp @@ -16,8 +16,12 @@ QtMvvmCoreDeclarativeModule::QtMvvmCoreDeclarativeModule(QObject *parent) : void QtMvvmCoreDeclarativeModule::registerTypes(const char *uri) { - Q_ASSERT(qstrcmp(uri, "de.skycoder42.qtmvvm.core") == 0); + Q_ASSERT(qstrcmp(uri, "de.skycoder42.QtMvvm.Core") == 0); + //Version 1.0 qmlRegisterType(uri, 1, 0, "Binding"); qmlRegisterSingletonType(uri, 1, 0, "Message", createMessageSingleton); + + // Check to make shure no module update is forgotten + static_assert(VERSION_MAJOR == 1 && VERSION_MINOR == 0, "QML module version needs to be updated"); } diff --git a/src/imports/mvvmquick/PresenterProgress.qml b/src/imports/mvvmquick/PresenterProgress.qml index 4155843..caf9310 100644 --- a/src/imports/mvvmquick/PresenterProgress.qml +++ b/src/imports/mvvmquick/PresenterProgress.qml @@ -1,6 +1,6 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import de.skycoder42.qtmvvm.quick 1.0 +import de.skycoder42.QtMvvm.Quick 1.0 ProgressBar { visible: QuickPresenter.viewLoading diff --git a/src/imports/mvvmquick/PresentingStackView.qml b/src/imports/mvvmquick/PresentingStackView.qml index 524e6ea..7246807 100644 --- a/src/imports/mvvmquick/PresentingStackView.qml +++ b/src/imports/mvvmquick/PresentingStackView.qml @@ -1,74 +1,59 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import de.skycoder42.qtmvvm.quick 1.0 StackView { - id: mainStack - anchors.fill: parent + id: _presenterStack - readonly property int animDuration: 150 - readonly property int opDuration: 75 + property int animDuration: 150 + property int opDuration: 75 function presentItem(item) { - if(push(item)) { - QuickPresenter.qmlPresenter.opened(item); + if(push(item)) return true; - } else + else return false; } - function withdrawItem(item) { - if(currentItem === item) { - if(pop()) { - QuickPresenter.qmlPresenter.closed(item); - return true; - } - } - - return false; - } - function closeAction() { - if(typeof mainStack.currentItem.closeAction != "undefined") { - if(mainStack.currentItem.closeAction()) + if(typeof _presenterStack.currentItem.closeAction == "function") { + if(_presenterStack.currentItem.closeAction()) return true; } - if(mainStack.depth <= 1) + if(_presenterStack.depth <= 1) return false; else { - var item = mainStack.pop(); - if(item) { - QuickPresenter.qmlPresenter.closed(item); + if(_presenterStack.pop()) return true; - } else + else return false; } } + //TODO only for android? maybe move to second class? pushEnter: Transition { PropertyAnimation { property: "y" easing.type: Easing.InOutQuad from: height * 0.3 to: 0 - duration: mainStack.animDuration + duration: _presenterStack.animDuration } PropertyAnimation { property: "opacity" from: 0.0 to: 1.0 - duration: mainStack.opDuration + duration: _presenterStack.opDuration } } pushExit: Transition { PauseAnimation { - duration: mainStack.animDuration + duration: _presenterStack.animDuration } } popEnter: Transition { PauseAnimation { - duration: mainStack.animDuration + duration: _presenterStack.animDuration } } popExit: Transition { @@ -77,18 +62,18 @@ StackView { easing.type: Easing.InOutQuad from: 0 to: height * 0.3 - duration: mainStack.animDuration + duration: _presenterStack.animDuration } SequentialAnimation { PauseAnimation { - duration: mainStack.animDuration - mainStack.opDuration + duration: _presenterStack.animDuration - _presenterStack.opDuration } PropertyAnimation { id: pp1 property: "opacity" from: 1.0 to: 0.0 - duration: mainStack.opDuration + duration: _presenterStack.opDuration } } } diff --git a/src/imports/mvvmquick/QtMvvmApp.qml b/src/imports/mvvmquick/QtMvvmApp.qml index cfbbf57..450db54 100644 --- a/src/imports/mvvmquick/QtMvvmApp.qml +++ b/src/imports/mvvmquick/QtMvvmApp.qml @@ -1,6 +1,6 @@ import QtQuick 2.10 import QtQuick.Controls 2.3 -import de.skycoder42.qtmvvm.quick 1.0 +import de.skycoder42.QtMvvm.Quick 1.0 ApplicationWindow { id: _root @@ -12,8 +12,13 @@ ApplicationWindow { z: -10 //keep it low so its hidden after the first view was shown } + PresentingStackView { + id: mainStack + anchors.fill: parent + } + function presentItem(item) { - return true; + return mainStack.presentItem(item); } function presentPopup(item) { @@ -21,4 +26,20 @@ ApplicationWindow { } Component.onCompleted: QuickPresenter.qmlPresenter = _root + + onClosing: { + var closed = false;//messageBox.closeAction(); + +// if(!closed) { +// if(popups.length > 0) { +// popups[popups.length - 1].close(); +// closed = true; +// } +// } + + if(!closed) + closed = mainStack.closeAction(); + + close.accepted = !closed; + } } diff --git a/src/imports/mvvmquick/mvvmquick.pro b/src/imports/mvvmquick/mvvmquick.pro index 663e305..803a6fe 100644 --- a/src/imports/mvvmquick/mvvmquick.pro +++ b/src/imports/mvvmquick/mvvmquick.pro @@ -1,8 +1,10 @@ QT += core qml quick mvvmquick mvvmquick-private CXX_MODULE = mvvmquick -TARGETPATH = de/skycoder42/qtmvvm/quick +TARGETPATH = de/skycoder42/QtMvvm/Quick TARGET = declarative_mvvmquick -IMPORT_VERSION = 1.0 +IMPORT_VERSION = $$MODULE_VERSION_IMPORT +DEFINES += "VERSION_MAJOR=$$MODULE_VERSION_MAJOR" +DEFINES += "VERSION_MINOR=$$MODULE_VERSION_MINOR" HEADERS += \ qtmvvmquick_plugin.h \ diff --git a/src/imports/mvvmquick/plugins.qmltypes b/src/imports/mvvmquick/plugins.qmltypes index b5cd3a6..4b27dd1 100644 --- a/src/imports/mvvmquick/plugins.qmltypes +++ b/src/imports/mvvmquick/plugins.qmltypes @@ -4,14 +4,14 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable de.skycoder42.qtmvvm.quick 1.0' +// 'qmlplugindump -nonrelocatable de.skycoder42.QtMvvm.Quick 1.0' Module { dependencies: ["QtQuick 2.8"] Component { name: "QtMvvm::QQmlQuickPresenter" prototype: "QObject" - exports: ["de.skycoder42.qtmvvm.quick/QuickPresenter 1.0"] + exports: ["de.skycoder42.QtMvvm.Quick/QuickPresenter 1.0"] isCreatable: false isSingleton: true exportMetaObjectRevisions: [0] @@ -30,12 +30,5 @@ Module { name: "loadingProgressChanged" Parameter { name: "loadingProgress"; type: "double" } } - Method { - name: "present" - Parameter { name: "viewModel"; type: "QtMvvm::ViewModel"; isPointer: true } - Parameter { name: "params"; type: "QVariantHash" } - Parameter { name: "viewUrl"; type: "QUrl" } - Parameter { name: "parent"; type: "QPointer" } - } } } diff --git a/src/imports/mvvmquick/qmldir b/src/imports/mvvmquick/qmldir index 389d25d..2c6eb77 100644 --- a/src/imports/mvvmquick/qmldir +++ b/src/imports/mvvmquick/qmldir @@ -1,4 +1,4 @@ -module de.skycoder42.qtmvvm.quick +module de.skycoder42.QtMvvm.Quick plugin declarative_mvvmquick classname QtMvvmQuickDeclarativeModule typeinfo plugins.qmltypes diff --git a/src/imports/mvvmquick/qqmlquickpresenter.cpp b/src/imports/mvvmquick/qqmlquickpresenter.cpp index e177e28..17c9793 100644 --- a/src/imports/mvvmquick/qqmlquickpresenter.cpp +++ b/src/imports/mvvmquick/qqmlquickpresenter.cpp @@ -105,34 +105,18 @@ void QQmlQuickPresenter::addObject(QQmlComponent *component, ViewModel *viewMode component->completeCreate(); auto presented = false; + auto cPresenter = QuickPresenterPrivate::currentPresenter(); if(parent && parent->parent()) - presented = tryPresent(parent->parent(), item); + presented = cPresenter->presentToQml(parent->parent(), item); if(!presented) - presented = tryPresent(_qmlPresenter, item); + presented = cPresenter->presentToQml(_qmlPresenter, item); - if(presented) - QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership); - else { + if(presented) { + if(!item->parent()) + QQmlEngine::setObjectOwnership(item, QQmlEngine::JavaScriptOwnership); + } else { qmlWarning(this) << "Failed to present item for viewModel of type" << viewModel->metaObject()->className(); item->deleteLater(); } } - -bool QQmlQuickPresenter::tryPresent(QObject *qmlPresenter, QObject *viewObject) -{ - auto meta = qmlPresenter->metaObject(); - auto index = -1; - if(viewObject->inherits("QQuickItem")) - index = meta->indexOfMethod("presentItem(QVariant)"); - if(viewObject->inherits("QQuickPopup")) - index = meta->indexOfMethod("presentPopup(QVariant)"); - - QVariant presented = false; - if(index != -1) { - meta->method(index).invoke(qmlPresenter, Qt::DirectConnection, - Q_RETURN_ARG(QVariant, presented), - Q_ARG(QVariant, QVariant::fromValue(viewObject))); - } - return presented.toBool(); -} diff --git a/src/imports/mvvmquick/qqmlquickpresenter.h b/src/imports/mvvmquick/qqmlquickpresenter.h index 1dee18f..d271c37 100644 --- a/src/imports/mvvmquick/qqmlquickpresenter.h +++ b/src/imports/mvvmquick/qqmlquickpresenter.h @@ -29,15 +29,13 @@ public: bool isViewLoading() const; qreal loadingProgress() const; -public Q_SLOTS: - void present(QtMvvm::ViewModel *viewModel, const QVariantHash ¶ms, const QUrl &viewUrl, QPointer parent); - Q_SIGNALS: void qmlPresenterChanged(QObject* qmlPresenter); void viewLoadingChanged(bool viewLoading); void loadingProgressChanged(qreal loadingProgress); private Q_SLOTS: + void present(QtMvvm::ViewModel *viewModel, const QVariantHash ¶ms, const QUrl &viewUrl, QPointer parent); void statusChanged(QQmlComponent::Status status); private: @@ -50,7 +48,6 @@ private: QHash _loadCache; void addObject(QQmlComponent *component, ViewModel *viewModel, const QVariantHash ¶ms, QPointer parent); - bool tryPresent(QObject *qmlPresenter, QObject *viewObject); }; } diff --git a/src/imports/mvvmquick/qtmvvmquick_plugin.cpp b/src/imports/mvvmquick/qtmvvmquick_plugin.cpp index 5f44dbd..ed5bf1d 100644 --- a/src/imports/mvvmquick/qtmvvmquick_plugin.cpp +++ b/src/imports/mvvmquick/qtmvvmquick_plugin.cpp @@ -16,7 +16,11 @@ QtMvvmQuickDeclarativeModule::QtMvvmQuickDeclarativeModule(QObject *parent) : void QtMvvmQuickDeclarativeModule::registerTypes(const char *uri) { - Q_ASSERT(qstrcmp(uri, "de.skycoder42.qtmvvm.quick") == 0); + Q_ASSERT(qstrcmp(uri, "de.skycoder42.QtMvvm.Quick") == 0); + //Version 1.0 qmlRegisterSingletonType(uri, 1, 0, "QuickPresenter", createQuickPresenterQmlSingleton); + + // Check to make shure no module update is forgotten + static_assert(VERSION_MAJOR == 1 && VERSION_MINOR == 0, "QML module version needs to be updated"); } diff --git a/src/mvvmquick/quickpresenter.cpp b/src/mvvmquick/quickpresenter.cpp index cac2a0c..a5478c1 100644 --- a/src/mvvmquick/quickpresenter.cpp +++ b/src/mvvmquick/quickpresenter.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -48,6 +49,19 @@ void QuickPresenter::showDialog(const QtMvvm::MessageConfig &config, QtMvvm::Mes qDebug(Q_FUNC_INFO); } +bool QuickPresenter::presentToQml(QObject *qmlPresenter, QObject *viewObject) +{ + auto meta = qmlPresenter->metaObject(); + auto index = presentMethodIndex(meta, viewObject); + QVariant presented = false; + if(index != -1) { + meta->method(index).invoke(qmlPresenter, Qt::DirectConnection, + Q_RETURN_ARG(QVariant, presented), + Q_ARG(QVariant, QVariant::fromValue(viewObject))); + } + return presented.toBool(); +} + QUrl QuickPresenter::findViewUrl(const QMetaObject *viewModelType) { auto currentMeta = viewModelType; @@ -88,6 +102,36 @@ QUrl QuickPresenter::findViewUrl(const QMetaObject *viewModelType) return QUrl(); } +int QuickPresenter::presentMethodIndex(const QMetaObject *presenterMetaObject, QObject *viewObject) +{ + auto index = -1; + if(viewObject->inherits("QQuickPopup")) + index = presenterMetaObject->indexOfMethod("presentPopup(QVariant)"); + if(viewObject->inherits("QQuickItem")) { + if(nameOrClassContains(viewObject, QStringLiteral("Drawer"))) + index = presenterMetaObject->indexOfMethod("presentDrawerContent(QVariant)"); + if(index == -1 && nameOrClassContains(viewObject, QStringLiteral("Tab"))) + index = presenterMetaObject->indexOfMethod("presentTab(QVariant)"); + + if(index == -1) + index = presenterMetaObject->indexOfMethod("presentItem(QVariant)"); + } + return index; +} + +bool QuickPresenter::nameOrClassContains(const QObject *obj, const QString &contained, Qt::CaseSensitivity caseSensitive) const +{ + if(obj->objectName().contains(contained, caseSensitive)) + return true; + auto currentMeta = obj->metaObject(); + while(currentMeta) { + if(QString::fromUtf8(currentMeta->className()).contains(contained, caseSensitive)) + return true; + currentMeta = currentMeta->superClass(); + } + return false; +} + // ------------- Private Implementation ------------- QuickPresenterPrivate::QuickPresenterPrivate() : diff --git a/src/mvvmquick/quickpresenter.h b/src/mvvmquick/quickpresenter.h index 8be483f..ea986af 100644 --- a/src/mvvmquick/quickpresenter.h +++ b/src/mvvmquick/quickpresenter.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -33,8 +34,13 @@ public: void present(ViewModel *viewModel, const QVariantHash ¶ms, QPointer parent) override; void showDialog(const MessageConfig &config, MessageResult *result) override; + virtual bool presentToQml(QObject *qmlPresenter, QObject *viewObject); + protected: virtual QUrl findViewUrl(const QMetaObject *viewModelType); + virtual int presentMethodIndex(const QMetaObject *presenterMetaObject, QObject *viewObject); + + bool nameOrClassContains(const QObject *obj, const QString &contained, Qt::CaseSensitivity caseSensitive = Qt::CaseInsensitive) const; private: friend class QtMvvm::QuickPresenterPrivate;