diff options
author | Thomas McGuire <thomas.mcguire@kdab.com> | 2012-07-17 17:35:53 +0200 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-08-22 12:25:33 +0200 |
commit | 26ea8e01e9ee2a8c8c09266147b94c9ac92d09f9 (patch) | |
tree | c058873b0082b18a09dde27f7ee124285e696807 /src/qml | |
parent | 2cc57f1e33cc4d739b1b76c605e6241fa0f134a8 (diff) |
Make connectNotify() work with QML
Call connectNotify() and disconnectNotify() in QQmlNotifierEndPoint,
which works for QML signal handlers and for QML bindings.
Task-number: QTBUG-11284
Change-Id: Ic9a08ee6687e5c7e606f315c8fb30eec1493cd83
Reviewed-by: Kent Hansen <kent.hansen@nokia.com>
Diffstat (limited to 'src/qml')
-rw-r--r-- | src/qml/qml/qqmlabstractbinding_p.h | 30 | ||||
-rw-r--r-- | src/qml/qml/qqmldata_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine.cpp | 32 | ||||
-rw-r--r-- | src/qml/qml/qqmljavascriptexpression_p.h | 3 | ||||
-rw-r--r-- | src/qml/qml/qqmlnotifier.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlnotifier_p.h | 7 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4bindings.cpp | 75 | ||||
-rw-r--r-- | src/qml/qml/v4/qv4bindings_p.h | 10 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8bindings.cpp | 5 | ||||
-rw-r--r-- | src/qml/qml/v8/qv8bindings_p.h | 2 |
10 files changed, 122 insertions, 45 deletions
diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index ae92077add..1ce0a23149 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -63,8 +63,24 @@ QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding { public: + enum DestroyMode { + + // The binding should disconnect itself upon destroy + DisconnectBinding, + + // The binding doesn't need to disconnect itself, but it can if it wants to. + // + // This is used in QQmlData::destroyed() - at the point at which the bindings are + // destroyed, the notifiers are already disconnected, so no need to disconnect each + // binding again. + // + // Bindings can use this flag to speed up destruction, especially for v4 bindings + // disconnecting a single binding might be slow. + KeepBindingConnected + }; + struct VTable { - void (*destroy)(QQmlAbstractBinding *); + void (*destroy)(QQmlAbstractBinding *, DestroyMode destroyMode); QString (*expression)(const QQmlAbstractBinding *); int (*propertyIndex)(const QQmlAbstractBinding *); QObject *(*object)(const QQmlAbstractBinding *); @@ -82,7 +98,9 @@ public: // Bindings are free to implement their own memory management, so the delete operator is // not necessarily safe. The default implementation clears the binding, removes it from // the object and calls delete. - void destroy() { vtable()->destroy(this); } + void destroy(DestroyMode destroyMode = DisconnectBinding) + { vtable()->destroy(this, destroyMode); } + QString expression() const { return vtable()->expression(this); } // Should return the encoded property index for the binding. Should return this value @@ -108,7 +126,7 @@ public: // Default implementation for some VTable functions template<typename T> - static void default_destroy(QQmlAbstractBinding *); + static void default_destroy(QQmlAbstractBinding *, DestroyMode); static QString default_expression(const QQmlAbstractBinding *); static void default_retargetBinding(QQmlAbstractBinding *, QObject *, int); @@ -182,8 +200,12 @@ QQmlAbstractBinding::BindingType QQmlAbstractBinding::bindingType() const } template<typename T> -void QQmlAbstractBinding::default_destroy(QQmlAbstractBinding *This) +void QQmlAbstractBinding::default_destroy(QQmlAbstractBinding *This, DestroyMode mode) { + // Assume the binding disconnects itself in the destructor, which for example QQmlBinding + // does in the destructor of its base class, QQmlJavaScriptExpression + Q_UNUSED(mode); + This->removeFromObject(); This->clear(); delete static_cast<T *>(This); diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 92c1a0cc68..5f5dafa1f9 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -145,6 +145,7 @@ public: void addNotify(int index, QQmlNotifierEndpoint *); int endpointCount(int index); bool signalHasEndpoint(int index); + void disconnectNotifiers(); // The context that created the C++ object QQmlContextData *context; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 90a71f997a..6ebd6b7666 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -483,6 +483,11 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) // Mark this object as in the process of deletion to // prevent it resolving in bindings QQmlData::markAsDeleted(o); + + // Disconnect the notifiers now - during object destruction this would be too late, since + // the disconnect call wouldn't be able to call disconnectNotify(), as it isn't possible to + // get the metaobject anymore. + d->disconnectNotifiers(); } } @@ -1330,6 +1335,21 @@ bool QQmlData::signalHasEndpoint(int index) return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64))); } +void QQmlData::disconnectNotifiers() +{ + if (notifyList) { + while (notifyList->todo) + notifyList->todo->disconnect(); + for (int ii = 0; ii < notifyList->notifiesSize; ++ii) { + while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii]) + ep->disconnect(); + } + free(notifyList->notifies); + free(notifyList); + notifyList = 0; + } +} + QHash<int, QObject *> *QQmlData::attachedProperties() const { if (!extendedData) extendedData = new QQmlDataExtended; @@ -1410,17 +1430,7 @@ void QQmlData::destroyed(QObject *object) guard->objectDestroyed(object); } - if (notifyList) { - while (notifyList->todo) - notifyList->todo->disconnect(); - for (int ii = 0; ii < notifyList->notifiesSize; ++ii) { - while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii]) - ep->disconnect(); - } - free(notifyList->notifies); - free(notifyList); - notifyList = 0; - } + disconnectNotifiers(); if (extendedData) delete extendedData; diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index dc9b4b63c6..09da661186 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -141,6 +141,7 @@ public: inline bool hasDelayedError() const; QQmlError error(QQmlEngine *) const; void clearError(); + void clearGuards(); QQmlDelayedError *delayedError(); static void exceptionToError(v8::Handle<v8::Message>, QQmlError &); @@ -185,8 +186,6 @@ private: // activeGuards:flag2 - useSharedContext QBiPointer<QObject, DeleteWatcher> m_scopeObject; QForwardFieldList<Guard, &Guard::next> activeGuards; - - void clearGuards(); }; QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e) diff --git a/src/qml/qml/qqmlnotifier.cpp b/src/qml/qml/qqmlnotifier.cpp index 2e451ee761..0d63012353 100644 --- a/src/qml/qml/qqmlnotifier.cpp +++ b/src/qml/qml/qqmlnotifier.cpp @@ -118,6 +118,8 @@ void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal, QQmlEngine QQmlPropertyPrivate::flushSignal(source, sourceSignal); QQmlData *ddata = QQmlData::get(source, true); ddata->addNotify(sourceSignal, this); + QObjectPrivate * const priv = QObjectPrivate::get(source); + priv->connectNotify(QMetaObjectPrivate::signal(source->metaObject(), sourceSignal)); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlnotifier_p.h b/src/qml/qml/qqmlnotifier_p.h index 7f5606fd68..2e5288291c 100644 --- a/src/qml/qml/qqmlnotifier_p.h +++ b/src/qml/qml/qqmlnotifier_p.h @@ -44,6 +44,8 @@ #include "qqmldata_p.h" #include "qqmlguard_p.h" +#include <QtCore/qmetaobject.h> +#include <private/qmetaobject_p.h> QT_BEGIN_NAMESPACE @@ -187,6 +189,11 @@ void QQmlNotifierEndpoint::connect(QQmlNotifier *notifier) void QQmlNotifierEndpoint::disconnect() { + if (sourceSignal != -1) { + QObject * const obj = senderAsObject(); + QObjectPrivate * const priv = QObjectPrivate::get(obj); + priv->disconnectNotify(QMetaObjectPrivate::signal(obj->metaObject(), sourceSignal)); + } if (next) next->prev = prev; if (prev) *prev = next; if (isNotifying()) *((intptr_t *)(senderPtr & ~0x1)) = 0; diff --git a/src/qml/qml/v4/qv4bindings.cpp b/src/qml/qml/v4/qv4bindings.cpp index 150a11d011..0088741170 100644 --- a/src/qml/qml/v4/qv4bindings.cpp +++ b/src/qml/qml/v4/qv4bindings.cpp @@ -382,10 +382,13 @@ void QV4Bindings::Binding::update(QQmlAbstractBinding *_This, QQmlPropertyPrivat This->parent->run(This, flags); } -void QV4Bindings::Binding::destroy(QQmlAbstractBinding *_This) +void QV4Bindings::Binding::destroy(QQmlAbstractBinding *_This, QQmlAbstractBinding::DestroyMode mode) { QV4Bindings::Binding *This = static_cast<QV4Bindings::Binding *>(_This); + if (mode == QQmlAbstractBinding::DisconnectBinding) + This->disconnect(); + This->setEnabledFlag(false); This->removeFromObject(); This->clear(); @@ -417,8 +420,30 @@ void QV4Bindings::Binding::retargetBinding(QQmlAbstractBinding *_This, QObject * This->target.value().targetProperty = i; } +void QV4Bindings::Binding::disconnect() +{ + // We iterate over the signal table to find all subscriptions associated with this binding. + // This is slow, but disconnect() is not called in the common case, only in special cases + // like when the binding is overwritten. + QV4Program * const program = parent->program; + for (quint16 subIndex = 0; subIndex < program->subscriptions; subIndex++) { + QV4Program::BindingReferenceList * const list = program->signalTable(subIndex); + for (quint32 bindingIndex = 0; bindingIndex < list->count; ++bindingIndex) { + QV4Program::BindingReference * const bindingRef = list->bindings + bindingIndex; + Binding * const binding = parent->bindings + bindingRef->binding; + if (binding == this) { + Subscription * const sub = parent->subscriptions + subIndex; + if (sub->active) { + sub->active = false; + sub->disconnect(); + } + } + } + } +} + QV4Bindings::Subscription::Subscription() -: bindings(0), method(-1) + : bindings(0), method(-1), active(false) { setCallback(QQmlNotifierEndpoint::QV4BindingsSubscription); } @@ -526,22 +551,18 @@ void QV4Bindings::run(Binding *binding, QQmlPropertyPrivate::WriteFlags flags) } } - -void QV4Bindings::unsubscribe(int subIndex) +void QV4Bindings::subscribeId(QQmlContextData *p, int idIndex, int subIndex) { Subscription *sub = (subscriptions + subIndex); sub->disconnect(); -} - -void QV4Bindings::subscribeId(QQmlContextData *p, int idIndex, int subIndex) -{ - unsubscribe(subIndex); if (p->idValues[idIndex]) { - Subscription *sub = (subscriptions + subIndex); sub->bindings = this; sub->method = subIndex; sub->connect(&p->idValues[idIndex].bindings); + sub->active = true; + } else { + sub->active = false; } } @@ -552,10 +573,13 @@ void QV4Bindings::subscribe(QObject *o, int notifyIndex, int subIndex, QQmlEngin return; sub->bindings = this; sub->method = subIndex; - if (o) + if (o) { sub->connect(o, notifyIndex, e); - else + sub->active = true; + } else { sub->disconnect(); + sub->active = false; + } } static bool testCompareVariants(const QVariant &qtscriptRaw, const QVariant &v4) @@ -942,14 +966,6 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } else { INVALIDATION_CHECK(invalidated, object, instr->fetchAndSubscribe.property.coreIndex); - int subIdx = instr->fetchAndSubscribe.subscription; - Subscription *sub = 0; - if (subIdx != -1) { - sub = (subscriptions + subIdx); - sub->bindings = this; - sub->method = subIdx; - } - const Register::Type valueType = (Register::Type)instr->fetchAndSubscribe.valueType; reg.init(valueType); if (instr->fetchAndSubscribe.valueType >= FirstCleanupType) @@ -967,9 +983,22 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, if (accessors->notifier) { QQmlNotifier *notifier = 0; accessors->notifier(object, instr->fetchAndSubscribe.property.accessorData, ¬ifier); - if (notifier) sub->connect(notifier); - } else if (instr->fetchAndSubscribe.property.notifyIndex != -1) { - sub->connect(object, instr->fetchAndSubscribe.property.notifyIndex, context->engine); + if (notifier) { + int subIdx = instr->fetchAndSubscribe.subscription; + Subscription *sub = 0; + if (subIdx != -1) { + sub = (subscriptions + subIdx); + sub->bindings = this; + sub->method = subIdx; + } + sub->connect(notifier); + } + } else { + const int notifyIndex = instr->fetchAndSubscribe.property.notifyIndex; + if (notifyIndex != -1) { + const int subIdx = instr->fetchAndSubscribe.subscription; + subscribe(object, notifyIndex, subIdx, context->engine); + } } } } diff --git a/src/qml/qml/v4/qv4bindings_p.h b/src/qml/qml/v4/qv4bindings_p.h index 0553e575a5..04fca8529f 100644 --- a/src/qml/qml/v4/qv4bindings_p.h +++ b/src/qml/qml/v4/qv4bindings_p.h @@ -84,13 +84,15 @@ public: : QQmlAbstractBinding(V4), target(0), scope(0), instruction(0), executedBlocks(0), parent(0) {} // Inherited from QQmlAbstractBinding - static void destroy(QQmlAbstractBinding *); + static void destroy(QQmlAbstractBinding *, QQmlAbstractBinding::DestroyMode mode); static int propertyIndex(const QQmlAbstractBinding *); static QObject *object(const QQmlAbstractBinding *); static void setEnabled(QQmlAbstractBinding *, bool, QQmlPropertyPrivate::WriteFlags); static void update(QQmlAbstractBinding *, QQmlPropertyPrivate::WriteFlags); static void retargetBinding(QQmlAbstractBinding *, QObject *, int); + void disconnect(); + struct Retarget { QObject *target; int targetProperty; @@ -121,7 +123,10 @@ private: public: inline Subscription(); QV4Bindings *bindings; - int method; + int method:31; + + // Subscriptions are not shared between bindings (anymore), so this can be a simple bool flag + bool active:1; }; friend void QV4BindingsSubscription_callback(QQmlNotifierEndpoint *e, void **); @@ -144,7 +149,6 @@ private: ); - inline void unsubscribe(int subIndex); inline void subscribeId(QQmlContextData *p, int idIndex, int subIndex); inline void subscribe(QObject *o, int notifyIndex, int subIndex, QQmlEngine *); diff --git a/src/qml/qml/v8/qv8bindings.cpp b/src/qml/qml/v8/qv8bindings.cpp index dd57d57ee7..a15831a39c 100644 --- a/src/qml/qml/v8/qv8bindings.cpp +++ b/src/qml/qml/v8/qv8bindings.cpp @@ -214,10 +214,13 @@ void QV8Bindings::Binding::expressionChanged(QQmlJavaScriptExpression *e) This->update(QQmlPropertyPrivate::DontRemoveBinding); } -void QV8Bindings::Binding::destroy(QQmlAbstractBinding *_This) +void QV8Bindings::Binding::destroy(QQmlAbstractBinding *_This, QQmlAbstractBinding::DestroyMode mode) { QV8Bindings::Binding *This = static_cast<QV8Bindings::Binding *>(_This); + if (mode == QQmlAbstractBinding::DisconnectBinding) + This->clearGuards(); + This->setEnabledFlag(false); This->setDestroyedFlag(true); This->removeFromObject(); diff --git a/src/qml/qml/v8/qv8bindings_p.h b/src/qml/qml/v8/qv8bindings_p.h index ca5c028824..9fbeaeb0eb 100644 --- a/src/qml/qml/v8/qv8bindings_p.h +++ b/src/qml/qml/v8/qv8bindings_p.h @@ -94,7 +94,7 @@ public: static void expressionChanged(QQmlJavaScriptExpression *); // "Inherited" from QQmlAbstractBinding - static void destroy(QQmlAbstractBinding *); + static void destroy(QQmlAbstractBinding *, QQmlAbstractBinding::DestroyMode mode); static int propertyIndex(const QQmlAbstractBinding *); static QObject *object(const QQmlAbstractBinding *); static void setEnabled(QQmlAbstractBinding *, bool, QQmlPropertyPrivate::WriteFlags); |