From 5085d5027f168db01e60e2161d44d9445f9c7beb Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Tue, 22 Sep 2009 22:18:56 +1000 Subject: Add plumbing for more advanced signal property control --- src/declarative/qml/qmlboundsignal.cpp | 129 ++++++++++++++++++++++++++++---- src/declarative/qml/qmlboundsignal_p.h | 55 +++++--------- src/declarative/qml/qmlenginedebug.cpp | 6 +- src/declarative/qml/qmlmetaproperty.cpp | 66 ++++++++++++++-- src/declarative/qml/qmlmetaproperty.h | 4 + src/declarative/qml/qmlvme.cpp | 6 +- src/declarative/util/qmlconnection.cpp | 7 +- 7 files changed, 200 insertions(+), 73 deletions(-) diff --git a/src/declarative/qml/qmlboundsignal.cpp b/src/declarative/qml/qmlboundsignal.cpp index c43097e..08c3387 100644 --- a/src/declarative/qml/qmlboundsignal.cpp +++ b/src/declarative/qml/qmlboundsignal.cpp @@ -52,40 +52,135 @@ QT_BEGIN_NAMESPACE -int QmlBoundSignal::evaluateIdx = -1; -QmlBoundSignal::QmlBoundSignal(QmlContext *ctxt, const QString &val, QObject *me, int idx, QObject *parent) -: QmlExpression(ctxt, val, me), _idx(idx) +class QmlBoundSignalParameters : public QObject +{ +Q_OBJECT +public: + QmlBoundSignalParameters(const QMetaMethod &, QObject * = 0); + ~QmlBoundSignalParameters(); + + void setValues(void **); + void clearValues(); + +private: + friend class MetaObject; + int metaCall(QMetaObject::Call, int _id, void **); + struct MetaObject : public QAbstractDynamicMetaObject { + MetaObject(QmlBoundSignalParameters *b) + : parent(b) {} + + int metaCall(QMetaObject::Call c, int id, void **a) { + return parent->metaCall(c, id, a); + } + QmlBoundSignalParameters *parent; + }; + + int *types; + void **values; + QMetaObject *myMetaObject; +}; + +static int evaluateIdx = -1; + +QmlAbstractBoundSignal::QmlAbstractBoundSignal(QObject *parent) +: QObject(parent) +{ +} + +QmlAbstractBoundSignal::~QmlAbstractBoundSignal() +{ +} + +QmlBoundSignal::QmlBoundSignal(QObject *scope, const QMetaMethod &signal, + QObject *parent) +: m_expression(0), m_idx(signal.methodIndex()), m_params(0) { // A cached evaluation of the QmlExpression::value() slot index. // // This is thread safe. Although it may be updated by two threads, they // will both set it to the same value - so the worst thing that can happen // is that they both do the work to figure it out. Boo hoo. - if (evaluateIdx == -1) - evaluateIdx = QmlExpression::staticMetaObject.indexOfMethod("value()"); + if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); - setTrackChange(false); QFx_setParent_noEvent(this, parent); - QMetaObject::connect(me, _idx, this, evaluateIdx); + QMetaObject::connect(scope, m_idx, this, evaluateIdx); + + if (!signal.parameterTypes().isEmpty()) + m_params = new QmlBoundSignalParameters(signal, this); } -QmlBoundSignalProxy::QmlBoundSignalProxy(QmlContext *ctxt, const QString &val, QObject *me, int idx, QObject *parent) -: QmlBoundSignal(ctxt, val, me, idx, parent) +QmlBoundSignal::QmlBoundSignal(QmlContext *ctxt, const QString &val, + QObject *scope, const QMetaMethod &signal, + QObject *parent) +: m_expression(0), m_idx(signal.methodIndex()), m_params(0) { - QMetaMethod signal = me->metaObject()->method(idx); + // A cached evaluation of the QmlExpression::value() slot index. + // + // This is thread safe. Although it may be updated by two threads, they + // will both set it to the same value - so the worst thing that can happen + // is that they both do the work to figure it out. Boo hoo. + if (evaluateIdx == -1) evaluateIdx = metaObject()->methodCount(); - params = new QmlBoundSignalParameters(signal, this); + QFx_setParent_noEvent(this, parent); + QMetaObject::connect(scope, m_idx, this, evaluateIdx); + + m_expression = new QmlExpression(ctxt, val, scope); + m_expression->setTrackChange(false); + + if (!signal.parameterTypes().isEmpty()) + m_params = new QmlBoundSignalParameters(signal, this); +} + +QmlBoundSignal::~QmlBoundSignal() +{ + delete m_expression; + m_expression = 0; +} + +int QmlBoundSignal::index() const +{ + return m_idx; } -int QmlBoundSignalProxy::qt_metacall(QMetaObject::Call c, int id, void **a) +/*! + Returns the signal expression. +*/ +QmlExpression *QmlBoundSignal::expression() const +{ + return m_expression; +} + +/*! + Sets the signal expression to \a e. Returns the current signal expression, + or null if there is no signal expression. + + The QmlBoundSignal instance takes ownership of \a e. The caller is + assumes ownership of the returned QmlExpression. +*/ +QmlExpression *QmlBoundSignal::setExpression(QmlExpression *e) +{ + QmlExpression *rv = m_expression; + m_expression = e; + if (m_expression) m_expression->setTrackChange(false); + return rv; +} + +QmlBoundSignal *QmlBoundSignal::cast(QObject *o) +{ + QmlAbstractBoundSignal *s = qobject_cast(o); + return static_cast(s); +} + +int QmlBoundSignal::qt_metacall(QMetaObject::Call c, int id, void **a) { if (c == QMetaObject::InvokeMetaMethod && id == evaluateIdx) { - params->setValues(a); - QmlExpressionPrivate::get(this)->value(params); - params->clearValues(); + if (m_params) m_params->setValues(a); + if (m_expression) + QmlExpressionPrivate::get(m_expression)->value(m_params); + if (m_params) m_params->clearValues(); return -1; } else { - return QmlBoundSignal::qt_metacall(c, id, a); + return QObject::qt_metacall(c, id, a); } } @@ -157,3 +252,5 @@ int QmlBoundSignalParameters::metaCall(QMetaObject::Call c, int id, void **a) } QT_END_NAMESPACE + +#include "qmlboundsignal.moc" diff --git a/src/declarative/qml/qmlboundsignal_p.h b/src/declarative/qml/qmlboundsignal_p.h index de8f91d..51c7155 100644 --- a/src/declarative/qml/qmlboundsignal_p.h +++ b/src/declarative/qml/qmlboundsignal_p.h @@ -58,56 +58,37 @@ QT_BEGIN_NAMESPACE -class QmlBoundSignal : public QmlExpression +class QmlAbstractBoundSignal : public QObject { -Q_OBJECT + Q_OBJECT public: - QmlBoundSignal(QmlContext *, const QString &, QObject *me, int idx, QObject *parent); - - int index() const { return _idx; } -protected: - static int evaluateIdx; -private: - int _idx; + QmlAbstractBoundSignal(QObject *parent = 0); + virtual ~QmlAbstractBoundSignal() = 0; }; -class QmlBoundSignalParameters : public QObject +class QmlBoundSignalParameters; +class QmlBoundSignal : public QmlAbstractBoundSignal { -Q_OBJECT public: - QmlBoundSignalParameters(const QMetaMethod &, QObject * = 0); - ~QmlBoundSignalParameters(); - - void setValues(void **); - void clearValues(); - -private: - friend class MetaObject; - int metaCall(QMetaObject::Call, int _id, void **); - struct MetaObject : public QAbstractDynamicMetaObject { - MetaObject(QmlBoundSignalParameters *b) - : parent(b) {} + QmlBoundSignal(QObject *scope, const QMetaMethod &signal, QObject *parent); + QmlBoundSignal(QmlContext *ctxt, const QString &val, QObject *scope, + const QMetaMethod &signal, QObject *parent); + virtual ~QmlBoundSignal(); - int metaCall(QMetaObject::Call c, int id, void **a) { - return parent->metaCall(c, id, a); - } - QmlBoundSignalParameters *parent; - }; + int index() const; - int *types; - void **values; - QMetaObject *myMetaObject; -}; + QmlExpression *expression() const; + QmlExpression *setExpression(QmlExpression *); -class QmlBoundSignalProxy : public QmlBoundSignal -{ -public: - QmlBoundSignalProxy(QmlContext *, const QString &, QObject *me, int idx, QObject *parent); + static QmlBoundSignal *cast(QObject *); protected: virtual int qt_metacall(QMetaObject::Call c, int id, void **a); + private: - QmlBoundSignalParameters *params; + QmlExpression *m_expression; + int m_idx; + QmlBoundSignalParameters *m_params; }; QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlenginedebug.cpp b/src/declarative/qml/qmlenginedebug.cpp index 7f9e530..a53ab2a 100644 --- a/src/declarative/qml/qmlenginedebug.cpp +++ b/src/declarative/qml/qmlenginedebug.cpp @@ -104,11 +104,7 @@ QmlEngineDebugServer::propertyData(QObject *obj, int propIdx) if (prop.type() < QVariant::UserType) { rv.type = QmlObjectProperty::Basic; - if (qobject_cast(obj) && prop.name() != QByteArray("objectName")) - // these "properties" only have meaning during signal emission - rv.value = tr("(signal parameter)"); - else - rv.value = prop.read(obj); + rv.value = prop.read(obj); } else if (QmlMetaType::isObject(prop.userType())) { rv.type = QmlObjectProperty::Object; } else if (QmlMetaType::isList(prop.userType()) || diff --git a/src/declarative/qml/qmlmetaproperty.cpp b/src/declarative/qml/qmlmetaproperty.cpp index 792b098..8f76240 100644 --- a/src/declarative/qml/qmlmetaproperty.cpp +++ b/src/declarative/qml/qmlmetaproperty.cpp @@ -547,6 +547,9 @@ QmlAbstractBinding *QmlMetaProperty::binding() const \a newBinding will be enabled, and the returned binding (if any) will be disabled. + + Ownership of \a newBinding transfers to QML. Ownership of the return value + is assumed by the caller. */ QmlAbstractBinding * QmlMetaProperty::setBinding(QmlAbstractBinding *newBinding) const @@ -577,6 +580,59 @@ QmlMetaProperty::setBinding(QmlAbstractBinding *newBinding) const return 0; } +/*! + Returns the expression associated with this signal property, or 0 if no + signal expression exists. +*/ +QmlExpression *QmlMetaProperty::signalExpression() const +{ + if (!(type() & SignalProperty)) + return 0; + + const QObjectList &children = d->object->children(); + + for (int ii = 0; ii < children.count(); ++ii) { + QObject *child = children.at(ii); + + QmlBoundSignal *signal = QmlBoundSignal::cast(child); + if (signal && signal->index() == coreIndex()) + return signal->expression(); + } + + return 0; +} + +/*! + Set the signal expression associated with this signal property to \a expr. + Returns the existing signal expression (if any), otherwise 0. + + Ownership of \a expr transfers to QML. Ownership of the return value is + assumed by the caller. +*/ +QmlExpression *QmlMetaProperty::setSignalExpression(QmlExpression *expr) const +{ + if (!(type() & SignalProperty)) + return 0; + + const QObjectList &children = d->object->children(); + + for (int ii = 0; ii < children.count(); ++ii) { + QObject *child = children.at(ii); + + QmlBoundSignal *signal = QmlBoundSignal::cast(child); + if (signal && signal->index() == coreIndex()) + return signal->setExpression(expr); + } + + if (expr) { + QmlBoundSignal *signal = new QmlBoundSignal(d->object, d->signal, + d->object); + return signal->setExpression(expr); + } else { + return 0; + } +} + void QmlMetaPropertyPrivate::findSignalInt(QObject *obj, const QString &name) { const QMetaObject *mo = obj->metaObject(); @@ -617,9 +673,9 @@ QVariant QmlMetaProperty::read() const const QObjectList &children = object()->children(); for (int ii = 0; ii < children.count(); ++ii) { - QmlBoundSignal *sig = qobject_cast(children.at(ii)); + QmlBoundSignal *sig = QmlBoundSignal::cast(children.at(ii)); if (sig && sig->index() == d->coreIdx) - return sig->expression(); + return sig->expression()->expression(); } } else if (type() & Property) { if (type() & Attached) { @@ -653,13 +709,13 @@ void QmlMetaPropertyPrivate::writeSignalProperty(const QVariant &value) const QObjectList &children = object->children(); for (int ii = 0; ii < children.count(); ++ii) { - QmlBoundSignal *sig = qobject_cast(children.at(ii)); + QmlBoundSignal *sig = QmlBoundSignal::cast(children.at(ii)); if (sig && sig->index() == coreIdx) { if (expr.isEmpty()) { sig->disconnect(); sig->deleteLater(); } else { - sig->setExpression(expr); + sig->expression()->setExpression(expr); } return; } @@ -667,7 +723,7 @@ void QmlMetaPropertyPrivate::writeSignalProperty(const QVariant &value) if (!expr.isEmpty()) { // XXX scope - (void *)new QmlBoundSignal(qmlContext(object), expr, object, coreIdx, object); + (void *)new QmlBoundSignal(qmlContext(object), expr, object, signal, object); } } diff --git a/src/declarative/qml/qmlmetaproperty.h b/src/declarative/qml/qmlmetaproperty.h index 7b9ff47..62e93c4 100644 --- a/src/declarative/qml/qmlmetaproperty.h +++ b/src/declarative/qml/qmlmetaproperty.h @@ -53,6 +53,7 @@ QT_MODULE(Declarative) class QObject; class QmlAbstractBinding; +class QmlExpression; class QStringList; class QVariant; struct QMetaObject; @@ -125,6 +126,9 @@ public: QmlAbstractBinding *binding() const; QmlAbstractBinding *setBinding(QmlAbstractBinding *) const; + QmlExpression *signalExpression() const; + QmlExpression *setSignalExpression(QmlExpression *) const; + static QmlMetaProperty createProperty(QObject *, const QString &); int coreIndex() const; diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index ca88455..d377cc3 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -537,11 +537,7 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, QmlCompiledData QMetaMethod signal = target->metaObject()->method(instr.storeSignal.signalIndex); - if (signal.parameterTypes().isEmpty()) { - (void *)new QmlBoundSignal(ctxt, primitives.at(instr.storeSignal.value), target, instr.storeSignal.signalIndex, target); - } else { - (void *)new QmlBoundSignalProxy(ctxt, primitives.at(instr.storeSignal.value), target, instr.storeSignal.signalIndex, target); - } + (void *)new QmlBoundSignal(ctxt, primitives.at(instr.storeSignal.value), target, signal, target); } break; diff --git a/src/declarative/util/qmlconnection.cpp b/src/declarative/util/qmlconnection.cpp index 6dca2d1..c77d0f6 100644 --- a/src/declarative/util/qmlconnection.cpp +++ b/src/declarative/util/qmlconnection.cpp @@ -187,10 +187,7 @@ void QmlConnection::connectIfValid() return; } - if (sigparams.isEmpty()) - d->boundsignal = new QmlBoundSignal(qmlContext(this), d->script, sender, sigIdx, this); - else - d->boundsignal = new QmlBoundSignalProxy(new QmlContext(qmlContext(this),this), d->script, sender, sigIdx, this); + d->boundsignal = new QmlBoundSignal(qmlContext(this), d->script, sender, mo->method(sigIdx), this); } } @@ -238,7 +235,7 @@ void QmlConnection::setScript(const QString& script) } else { // must exist - update d->script = script; - d->boundsignal->setExpression(script); + d->boundsignal->expression()->setExpression(script); } } else { d->script = script; -- cgit v0.12