diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index d4d2d527d44..495ce7c9483 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -4255,7 +4255,7 @@ void qDeleteInEventHandler(QObject *o) must not have an overloaded or templated operator(). */ -/** +/*! \internal Implementation of the template version of connect @@ -4280,12 +4280,13 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, const int *types, const QMetaObject *senderMetaObject) { - if (!sender || !signal || !slotObj || !senderMetaObject) { + if (!signal) { qWarning("QObject::connect: invalid null parameter"); if (slotObj) slotObj->destroyIfLastRef(); return QMetaObject::Connection(); } + int signal_index = -1; void *args[] = { &signal_index, signal }; for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) { @@ -4299,6 +4300,27 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa return QMetaObject::Connection(0); } signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject); + return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject); +} + +/*! + \internal + + Internal version of connect used by the template version of QObject::connect (called via connectImpl) and + also used by the QObjectPrivate::connect version used by QML. The signal_index is expected to be relative + to the number of signals. + */ +QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index, + const QObject *receiver, void **slot, + QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, + const int *types, const QMetaObject *senderMetaObject) +{ + if (!sender || !slotObj || !senderMetaObject) { + qWarning("QObject::connect: invalid null parameter"); + if (slotObj) + slotObj->destroyIfLastRef(); + return QMetaObject::Connection(); + } QObject *s = const_cast(sender); QObject *r = const_cast(receiver); @@ -4469,6 +4491,40 @@ bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, receiver, -1, slot); } +/*! + \internal + Used by QML to connect a signal by index to a slot implemented in JavaScript (wrapped in a custom QSlotOBjectBase subclass). + + The signal_index is an index relative to the number of methods. + */ +QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type) +{ + if (!sender) { + qWarning("QObject::connect: invalid null parameter"); + if (slotObj) + slotObj->destroyIfLastRef(); + return QMetaObject::Connection(); + } + const QMetaObject *senderMetaObject = sender->metaObject(); + signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index); + + return QObjectPrivate::connectImpl(sender, signal_index, sender, /*slot*/0, slotObj, type, /*types*/0, senderMetaObject); +} + +/*! + \internal + Used by QML to disconnect a signal by index that's connected to a slot implemented in JavaScript (wrapped in a custom QSlotObjectBase subclass) + In the QML case the slot is not a pointer to a pointer to the function to disconnect, but instead it is a pointer to an array of internal values + required for the disconnect. + */ +bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, void **slot) +{ + const QMetaObject *senderMetaObject = sender->metaObject(); + signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index); + + return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, sender, -1, slot); +} + /*! \class QMetaObject::Connection \inmodule QtCore Represents a handle to a signal-slot connection. diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index e849ec15995..3c43972ac94 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -207,6 +207,13 @@ public: template static inline bool disconnect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot); + + static QMetaObject::Connection connectImpl(const QObject *sender, int signal_index, + const QObject *receiver, void **slot, + QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, + const int *types, const QMetaObject *senderMetaObject); + static QMetaObject::Connection connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type); + static bool disconnect(const QObject *sender, int signal_index, void **slot); public: ExtraData *extraData; // extra data set by the user QThreadData *threadData; // id of the thread that owns the object diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index afbe1a5eced..c489344b10a 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -456,6 +456,7 @@ class Q_CORE_EXPORT QMetaObject::Connection { void *d_ptr; //QObjectPrivate::Connection* explicit Connection(void *data) : d_ptr(data) { } friend class QObject; + friend class QObjectPrivate; friend struct QMetaObject; public: ~Connection(); diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index d16369de025..9bf28430b0c 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -146,6 +146,7 @@ private slots: void connectFunctorOverloads(); void disconnectDoesNotLeakFunctor(); void connectBase(); + void qmlConnect(); }; struct QObjectCreatedOnShutdown @@ -5835,6 +5836,56 @@ void tst_QObject::connectBase() QCOMPARE( r1.count_slot3, 1 ); } +struct QmlReceiver : public QtPrivate::QSlotObjectBase +{ + int callCount; + void *magic; + + QmlReceiver() + : QtPrivate::QSlotObjectBase(&impl) + , callCount(0) + , magic(0) + {} + + static void impl(int which, QSlotObjectBase *this_, QObject *, void **metaArgs, bool *ret) + { + switch (which) { + case Destroy: delete static_cast(this_); return; + case Call: static_cast(this_)->callCount++; return; + case Compare: *ret = static_cast(this_)->magic == metaArgs[0]; return; + case NumOperations: break; + } + } +}; + +void tst_QObject::qmlConnect() +{ +#ifdef QT_BUILD_INTERNAL + SenderObject sender; + QmlReceiver *receiver = new QmlReceiver; + receiver->magic = receiver; + receiver->ref(); + + QVERIFY(QObjectPrivate::connect(&sender, sender.metaObject()->indexOfSignal("signal1()"), + receiver, Qt::AutoConnection)); + + QCOMPARE(receiver->callCount, 0); + sender.emitSignal1(); + QCOMPARE(receiver->callCount, 1); + + void *a[] = { + receiver + }; + QVERIFY(QObjectPrivate::disconnect(&sender, sender.metaObject()->indexOfSignal("signal1()"), reinterpret_cast(&a))); + + sender.emitSignal1(); + QCOMPARE(receiver->callCount, 1); + + receiver->destroyIfLastRef(); +#else + QSKIP("Needs QT_BUILD_INTERNAL"); +#endif +} QTEST_MAIN(tst_QObject) #include "tst_qobject.moc"