diff options
author | Kari Oikarinen <kari.oikarinen@qt.io> | 2019-09-04 12:01:00 +0300 |
---|---|---|
committer | Kari Oikarinen <kari.oikarinen@qt.io> | 2019-09-04 12:01:00 +0300 |
commit | 402b26efc6903741655bcc42bc777f73c6fbc5ab (patch) | |
tree | f65d033a3cbf1e840d651bb5ce7ca7cfc4b0d990 | |
parent | 37189eee9cbdef8d973b04ed363bfb55480fd0c8 (diff) | |
parent | 9ec7a871082800d1af189236f99b67e4d564a966 (diff) |
Merge dev into 5.14 (delayed final downmerge)
This replicates the effects of the fast-forward merge that should have
been pushed on 2019-08-27 as the final down-merge from dev to 5.14.
Task-number: QTBUG-78019
Change-Id: Iccd04fa361959ba0711f41bf20ff3d064065ed7e
-rw-r--r-- | src/imports/remoteobjects/plugin.cpp | 80 | ||||
-rw-r--r-- | src/imports/remoteobjects/remoteobjects.pro | 2 | ||||
-rw-r--r-- | src/remoteobjects/qremoteobjectpendingcall.h | 30 | ||||
-rw-r--r-- | tests/auto/modelview/modeltest.cpp | 4 | ||||
-rw-r--r-- | tests/auto/modelview/tst_modelview.cpp | 2 | ||||
-rw-r--r-- | tests/auto/qml/usertypes/data/watcher.qml | 37 | ||||
-rw-r--r-- | tests/auto/qml/usertypes/tst_usertypes.cpp | 43 | ||||
-rw-r--r-- | tests/auto/qml/usertypes/usertypes.rep | 7 | ||||
-rw-r--r-- | tools/repc/repcodegenerator.cpp | 29 | ||||
-rw-r--r-- | tools/repc/repcodegenerator.h | 1 |
10 files changed, 228 insertions, 7 deletions
diff --git a/src/imports/remoteobjects/plugin.cpp b/src/imports/remoteobjects/plugin.cpp index 0bec1a9..4032a63 100644 --- a/src/imports/remoteobjects/plugin.cpp +++ b/src/imports/remoteobjects/plugin.cpp @@ -39,11 +39,90 @@ #include <QtRemoteObjects/qremoteobjectnode.h> #include <QtRemoteObjects/qremoteobjectsettingsstore.h> +#include <QtRemoteObjects/qremoteobjectpendingcall.h> +#include <QTimer> #include <QQmlExtensionPlugin> +#include <QJSValue> +#include <QtQml/private/qjsvalue_p.h> +#include <QtQml/qqmlengine.h> +#include <qqmlinfo.h> #include <qqml.h> QT_BEGIN_NAMESPACE +struct QtQmlRemoteObjectsResponse { + QJSValue promise; + QTimer *timer; +}; + +class QtQmlRemoteObjects : public QObject +{ + Q_OBJECT +public: + ~QtQmlRemoteObjects() { + auto i = m_callbacks.begin(); + while (i != m_callbacks.end()) { + delete i.key(); + delete i.value().timer; + i = m_callbacks.erase(i); + } + } + + Q_INVOKABLE QJSValue watch(const QRemoteObjectPendingCall &reply, int timeout = 30000) { + if (m_accessiblePromise.isUndefined()) + m_accessiblePromise = qmlEngine(this)->evaluate("(function() { var obj = {}; obj.promise = new Promise(function(resolve, reject) { obj.resolve = resolve; obj.reject = reject; }); return obj; })"); + + QRemoteObjectPendingCallWatcher *watcher = new QRemoteObjectPendingCallWatcher(reply); + + QJSValue promise = m_accessiblePromise.call(); + QtQmlRemoteObjectsResponse response; + response.promise = promise; + response.timer = new QTimer(); + response.timer->setSingleShot(true); + m_callbacks.insert(watcher, response); + + // handle timeout + connect(response.timer, &QTimer::timeout, [this, watcher]() { + auto i = m_callbacks.find(watcher); + if (i == m_callbacks.end()) { + qmlWarning(this) << "could not find callback for watcher."; + return; + } + + QJSValue v(QLatin1String("timeout")); + i.value().promise.property("reject").call(QJSValueList() << v); + + delete i.key(); + delete i.value().timer; + m_callbacks.erase(i); + }); + + // handle success + connect(watcher, &QRemoteObjectPendingCallWatcher::finished, [this](QRemoteObjectPendingCallWatcher *self) { + auto i = m_callbacks.find(self); + if (i == m_callbacks.end()) { + qmlWarning(this) << "could not find callback for watcher."; + return; + } + + QJSValue v; + QJSValuePrivate::setVariant(&v, self->returnValue()); + i.value().promise.property("resolve").call(QJSValueList() << v); + + delete i.key(); + delete i.value().timer; + m_callbacks.erase(i); + }); + + response.timer->start(timeout); + return promise.property("promise"); + } + +private: + QHash<QRemoteObjectPendingCallWatcher*,QtQmlRemoteObjectsResponse> m_callbacks; + QJSValue m_accessiblePromise; +}; + class QtRemoteObjectsPlugin : public QQmlExtensionPlugin { Q_OBJECT @@ -58,6 +137,7 @@ public: qmlRegisterType<QRemoteObjectNode>(uri, 5, 12, "Node"); qmlRegisterType<QRemoteObjectSettingsStore>(uri, 5, 12, "SettingsStore"); + qmlRegisterSingletonType<QtQmlRemoteObjects>(uri, 5, 14, "QtRemoteObjects", [](QQmlEngine *, QJSEngine*){return new QtQmlRemoteObjects();}); qmlProtectModule(uri, 5); } }; diff --git a/src/imports/remoteobjects/remoteobjects.pro b/src/imports/remoteobjects/remoteobjects.pro index ed21fa6..91de192 100644 --- a/src/imports/remoteobjects/remoteobjects.pro +++ b/src/imports/remoteobjects/remoteobjects.pro @@ -3,7 +3,7 @@ TARGET = qtremoteobjects TARGETPATH = QtRemoteObjects IMPORT_VERSION = 5.$$QT_MINOR_VERSION -QT += qml remoteobjects +QT += qml qml-private remoteobjects SOURCES = \ $$PWD/plugin.cpp \ diff --git a/src/remoteobjects/qremoteobjectpendingcall.h b/src/remoteobjects/qremoteobjectpendingcall.h index e895842..086e5d8 100644 --- a/src/remoteobjects/qremoteobjectpendingcall.h +++ b/src/remoteobjects/qremoteobjectpendingcall.h @@ -130,6 +130,36 @@ public: }; +// NOTE: manual expansion of Q_DECLARE_METATYPE_TEMPLATE_1ARG, minus the IsSequentialContainer +template <typename T> +struct QMetaTypeId< QRemoteObjectPendingReply<T> > +{ + enum { + Defined = QMetaTypeId2<T>::Defined + }; + static int qt_metatype_id() + { + static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); + if (const int id = metatype_id.load()) + return id; + const char *tName = QMetaType::typeName(qMetaTypeId<T>()); + Q_ASSERT(tName); + const int tNameLen = int(qstrlen(tName)); + QByteArray typeName; + typeName.reserve(int(sizeof("QRemoteObjectPendingReply")) + 1 + tNameLen + 1 + 1); + typeName.append("QRemoteObjectPendingReply", int(sizeof("QRemoteObjectPendingReply")) - 1) + .append('<').append(tName, tNameLen); + if (typeName.endsWith('>')) + typeName.append(' '); + typeName.append('>'); + const int newId = qRegisterNormalizedMetaType< QRemoteObjectPendingReply<T> >( + typeName, + reinterpret_cast< QRemoteObjectPendingReply<T> *>(quintptr(-1))); + metatype_id.storeRelease(newId); + return newId; + } +}; + QT_END_NAMESPACE #endif diff --git a/tests/auto/modelview/modeltest.cpp b/tests/auto/modelview/modeltest.cpp index 9abf878..859d883 100644 --- a/tests/auto/modelview/modeltest.cpp +++ b/tests/auto/modelview/modeltest.cpp @@ -443,12 +443,12 @@ void ModelTest::data() } // General Purpose roles that should return a QColor - QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundColorRole ); + QVariant colorVariant = model->data ( model->index ( 0, 0 ), Qt::BackgroundRole ); if ( colorVariant.isValid() ) { QVERIFY( colorVariant.canConvert<QColor>() ); } - colorVariant = model->data ( model->index ( 0, 0 ), Qt::TextColorRole ); + colorVariant = model->data ( model->index ( 0, 0 ), Qt::ForegroundRole ); if ( colorVariant.isValid() ) { QVERIFY( colorVariant.canConvert<QColor>() ); } diff --git a/tests/auto/modelview/tst_modelview.cpp b/tests/auto/modelview/tst_modelview.cpp index da42763..78e75ca 100644 --- a/tests/auto/modelview/tst_modelview.cpp +++ b/tests/auto/modelview/tst_modelview.cpp @@ -733,7 +733,7 @@ void TestModelView::testFlags() for (int i = 10; i < 20; ++i) { QStandardItem* firstItem = m_sourceModel.item(i, 0); QStandardItem* secondItem = m_sourceModel.item(i, 1); - firstItem->setFlags(firstItem->flags() | Qt::ItemIsEnabled | Qt::ItemIsTristate); + firstItem->setFlags(firstItem->flags() | Qt::ItemIsEnabled | Qt::ItemIsAutoTristate); secondItem->setFlags(firstItem->flags() | Qt::ItemIsEnabled); } bool signalsReceived = false; diff --git a/tests/auto/qml/usertypes/data/watcher.qml b/tests/auto/qml/usertypes/data/watcher.qml new file mode 100644 index 0000000..b49d824 --- /dev/null +++ b/tests/auto/qml/usertypes/data/watcher.qml @@ -0,0 +1,37 @@ +import QtQuick 2.0 +import QtRemoteObjects 5.14 +import usertypes 1.0 + +TypeWithReplyReplica { + property variant result + property bool hasError + + node: Node { + registryUrl: "local:testWatcher" + } + onStateChanged: function(state) { + if (state != TypeWithReplyReplica.Valid) + return; + QtRemoteObjects.watch(uppercase("hello"), 1000) + .then(function(value) { hasError = false; result = value }, + function(error) { hasError = true }) + } + + function callSlowFunction() { + result = 0 + hasError = false + + QtRemoteObjects.watch(slowFunction(), 300) + .then(function(value) { hasError = false; result = value }, + function(error) { hasError = true }) + } + + function callComplexFunction() { + result = null + hasError = false + + QtRemoteObjects.watch(complexReturnType(), 300) + .then(function(value) { hasError = false; result = value }, + function(error) { hasError = true }) + } +} diff --git a/tests/auto/qml/usertypes/tst_usertypes.cpp b/tests/auto/qml/usertypes/tst_usertypes.cpp index 7c554a9..07bed59 100644 --- a/tests/auto/qml/usertypes/tst_usertypes.cpp +++ b/tests/auto/qml/usertypes/tst_usertypes.cpp @@ -32,6 +32,21 @@ #include <qqmlcomponent.h> #include "rep_usertypes_merged.h" +class TypeWithReply : public TypeWithReplySimpleSource +{ +public: + QString uppercase(const QString & input) override { + return input.toUpper(); + } + QMap<QString, QString> complexReturnType() override { + return QMap<QString, QString>{{"one","1"}}; + } + int slowFunction() override { + QTest::qWait(1000); + return 15; + } +}; + class tst_usertypes : public QObject { Q_OBJECT @@ -46,6 +61,7 @@ private Q_SLOTS: void subObjectInQml(); void complexInQml_data(); void complexInQml(); + void watcherInQml(); }; tst_usertypes::tst_usertypes() @@ -192,6 +208,33 @@ void tst_usertypes::complexInQml() } } +void tst_usertypes::watcherInQml() +{ + qmlRegisterType<TypeWithReplyReplica>("usertypes", 1, 0, "TypeWithReplyReplica"); + + QRemoteObjectRegistryHost host(QUrl("local:testWatcher")); + TypeWithReply source; + host.enableRemoting(&source); + + QQmlEngine e; + QQmlComponent c(&e, SRCDIR "data/watcher.qml"); + QObject *obj = c.create(); + QVERIFY(obj); + + QTRY_COMPARE_WITH_TIMEOUT(obj->property("result").value<QString>(), QString::fromLatin1("HELLO"), 300); + QCOMPARE(obj->property("hasError").value<bool>(), false); + + QMetaObject::invokeMethod(obj, "callSlowFunction"); + QTRY_COMPARE_WITH_TIMEOUT(obj->property("hasError").value<bool>(), true, 1000); + QVERIFY(obj->property("result").value<int>() != 10); + + QMetaObject::invokeMethod(obj, "callComplexFunction"); + QTRY_VERIFY_WITH_TIMEOUT(!obj->property("result").isNull(), 300); + auto map = obj->property("result").value<QMap<QString,QString>>(); + QCOMPARE(map.value("one"), QString::fromLatin1("1")); + QCOMPARE(obj->property("hasError").value<bool>(), false); +} + QTEST_MAIN(tst_usertypes) #include "tst_usertypes.moc" diff --git a/tests/auto/qml/usertypes/usertypes.rep b/tests/auto/qml/usertypes/usertypes.rep index dff361d..a82d1d9 100644 --- a/tests/auto/qml/usertypes/usertypes.rep +++ b/tests/auto/qml/usertypes/usertypes.rep @@ -23,3 +23,10 @@ class ComplexType CLASS clock(SimpleClock) PROP(int after = 42) } + +class TypeWithReply +{ + SLOT(QString uppercase(const QString &input)) + SLOT(QMap<QString, QString> complexReturnType()) + SLOT(int slowFunction()) +}; diff --git a/tools/repc/repcodegenerator.cpp b/tools/repc/repcodegenerator.cpp index fdb27d7..001103c 100644 --- a/tools/repc/repcodegenerator.cpp +++ b/tools/repc/repcodegenerator.cpp @@ -205,6 +205,7 @@ void RepCodeGenerator::generate(const AST &ast, Mode mode, QString fileName) for (const ASTClass &astClass : ast.classes) { QSet<QString> classMetaTypes; + QSet<QString> pendingMetaTypes; for (const ASTProperty &property : astClass.properties) { if (property.isPointer) continue; @@ -212,6 +213,7 @@ void RepCodeGenerator::generate(const AST &ast, Mode mode, QString fileName) } const auto extractClassMetaTypes = [&](const ASTFunction &function) { classMetaTypes << function.returnType; + pendingMetaTypes << function.returnType; for (const ASTDeclaration &decl : function.params) { classMetaTypes << decl.type; } @@ -220,15 +222,19 @@ void RepCodeGenerator::generate(const AST &ast, Mode mode, QString fileName) extractClassMetaTypes(function); for (const ASTFunction &function : astClass.slotsList) extractClassMetaTypes(function); - QString classMetaTypeRegistrationCode = metaTypeRegistrationCode + generateMetaTypeRegistration(classMetaTypes); + + const QString classMetaTypeRegistrationCode = metaTypeRegistrationCode + + generateMetaTypeRegistration(classMetaTypes); + const QString replicaMetaTypeRegistrationCode = classMetaTypeRegistrationCode + + generateMetaTypeRegistrationForPending(pendingMetaTypes); if (mode == MERGED) { - generateClass(REPLICA, stream, astClass, classMetaTypeRegistrationCode); + generateClass(REPLICA, stream, astClass, replicaMetaTypeRegistrationCode); generateClass(SOURCE, stream, astClass, classMetaTypeRegistrationCode); generateClass(SIMPLE_SOURCE, stream, astClass, classMetaTypeRegistrationCode); generateSourceAPI(stream, astClass); } else { - generateClass(mode, stream, astClass, classMetaTypeRegistrationCode); + generateClass(mode, stream, astClass, mode == REPLICA ? replicaMetaTypeRegistrationCode : classMetaTypeRegistrationCode); if (mode == SOURCE) { generateClass(SIMPLE_SOURCE, stream, astClass, classMetaTypeRegistrationCode); generateSourceAPI(stream, astClass); @@ -564,6 +570,23 @@ QString RepCodeGenerator::generateMetaTypeRegistration(const QSet<QString> &meta return out; } +QString RepCodeGenerator::generateMetaTypeRegistrationForPending(const QSet<QString> &metaTypes) +{ + QString out; + if (!metaTypes.isEmpty()) + out += QLatin1String(" qRegisterMetaType<QRemoteObjectPendingCall>();\n"); + const QString qRegisterMetaType = QStringLiteral(" qRegisterMetaType<QRemoteObjectPendingReply<%1>>();\n"); + const QString qRegisterConverterConditional = QStringLiteral(" if (!QMetaType::hasRegisteredConverterFunction<QRemoteObjectPendingReply<%1>, QRemoteObjectPendingCall>())\n"); + const QString qRegisterConverter = QStringLiteral(" QMetaType::registerConverter<QRemoteObjectPendingReply<%1>, QRemoteObjectPendingCall>();\n"); + for (const QString &metaType : metaTypes) { + out += qRegisterMetaType.arg(metaType); + out += qRegisterConverterConditional.arg(metaType); + out += qRegisterConverter.arg(metaType); + } + return out; +} + + QString RepCodeGenerator::generateMetaTypeRegistrationForEnums(const QVector<QString> &enumUses) { QString out; diff --git a/tools/repc/repcodegenerator.h b/tools/repc/repcodegenerator.h index d7ec4c8..be3353d 100644 --- a/tools/repc/repcodegenerator.h +++ b/tools/repc/repcodegenerator.h @@ -63,6 +63,7 @@ public: private: void generateHeader(Mode mode, QTextStream &out, const AST &ast); QString generateMetaTypeRegistration(const QSet<QString> &metaTypes); + QString generateMetaTypeRegistrationForPending(const QSet<QString> &metaTypes); QString generateMetaTypeRegistrationForEnums(const QVector<QString> &enums); void generateStreamOperatorsForEnums(QTextStream &out, const QVector<QString> &enums); |