From e1f33f2e4689a59de2814ffdea7ea246bbfe0283 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Fri, 26 Feb 2010 17:04:38 +1000 Subject: Change Connection syntax as per QT-2822. --- demos/declarative/flickr/flickr-desktop.qml | 6 +- demos/declarative/flickr/flickr-mobile.qml | 6 +- demos/declarative/flickr/mobile/GridDelegate.qml | 6 +- doc/src/declarative/elements.qdoc | 2 +- doc/src/declarative/extending.qdoc | 5 +- examples/declarative/connections/connections.qml | 4 +- src/declarative/QmlChanges.txt | 5 + src/declarative/util/qdeclarativeconnection.cpp | 250 +++++++++------------ src/declarative/util/qdeclarativeconnection_p.h | 43 ++-- src/declarative/util/qdeclarativeutilmodule.cpp | 3 +- .../data/test-connection.qml | 2 +- .../data/test-connection2.qml | 2 +- .../data/test-connection3.qml | 2 +- .../qdeclarativeconnection/data/trimming.qml | 2 +- .../tst_qdeclarativeconnection.cpp | 12 +- 15 files changed, 157 insertions(+), 193 deletions(-) diff --git a/demos/declarative/flickr/flickr-desktop.qml b/demos/declarative/flickr/flickr-desktop.qml index 3a86347..99216cb 100644 --- a/demos/declarative/flickr/flickr-desktop.qml +++ b/demos/declarative/flickr/flickr-desktop.qml @@ -21,9 +21,9 @@ Item { angle: wrapper.PathView.angle ? wrapper.PathView.angle : 0 } - Connection { - sender: imageDetails; signal: "closed()" - script: { + Connections { + target: imageDetails + onClosed: { if (wrapper.state == 'Details') { wrapper.state = ''; imageDetails.photoUrl = ""; diff --git a/demos/declarative/flickr/flickr-mobile.qml b/demos/declarative/flickr/flickr-mobile.qml index 77ccd08..21e4c49 100644 --- a/demos/declarative/flickr/flickr-mobile.qml +++ b/demos/declarative/flickr/flickr-mobile.qml @@ -53,9 +53,9 @@ Item { onButton2Clicked: if (screen.inListView == true) screen.inListView = false; else screen.inListView = true } - Connection { - sender: imageDetails; signal: "closed()" - script: { + Connections { + target: imageDetails + onClosed: { if (background.state == "DetailedView") { background.state = ''; imageDetails.photoUrl = ""; diff --git a/demos/declarative/flickr/mobile/GridDelegate.qml b/demos/declarative/flickr/mobile/GridDelegate.qml index 5722f10..7634573 100644 --- a/demos/declarative/flickr/mobile/GridDelegate.qml +++ b/demos/declarative/flickr/mobile/GridDelegate.qml @@ -33,9 +33,9 @@ Image { source: "images/gloss.png" } } - Connection { - sender: toolBar; signal: "button2Clicked()" - script: if (scaleMe.state == 'Details' ) scaleMe.state = 'Show'; + Connections { + target: toolBar + onButton2Clicked: if (scaleMe.state == 'Details' ) scaleMe.state = 'Show' } states: [ diff --git a/doc/src/declarative/elements.qdoc b/doc/src/declarative/elements.qdoc index da96b8e..1fd4dad 100644 --- a/doc/src/declarative/elements.qdoc +++ b/doc/src/declarative/elements.qdoc @@ -98,7 +98,7 @@ The following table lists the QML elements provided by the Qt Declarative module \o \list \o \l Script -\o \l Connection +\o \l Connections \o \l Component \o \l Timer \o \l QtObject diff --git a/doc/src/declarative/extending.qdoc b/doc/src/declarative/extending.qdoc index d823bf6..5aaa7bd 100644 --- a/doc/src/declarative/extending.qdoc +++ b/doc/src/declarative/extending.qdoc @@ -421,9 +421,8 @@ C++ signature: \snippet examples/declarative/extending/signal/birthdayparty.h 0 In classes with multiple signals with the same name, only the final signal -is accessible as a signal property. Although QML provides an element, -\l Connection, for accessing the other signals it is less elegant. For the best -QML API, class developers should avoid overloading signal names. +is accessible as a signal property. Note that signals with the same name +but different parameters cannot be distinguished. Signal parameters become accessible by name to the assigned script. An unnamed parameter cannot be accessed, so care should be taken to name all the diff --git a/examples/declarative/connections/connections.qml b/examples/declarative/connections/connections.qml index c140017..4692343 100644 --- a/examples/declarative/connections/connections.qml +++ b/examples/declarative/connections/connections.qml @@ -22,6 +22,6 @@ Rectangle { anchors { right: parent.right; bottom: parent.bottom; rightMargin: 10; bottomMargin: 10 } } - Connection { sender: leftButton; signal: "clicked()"; script: window.angle -= 90 } - Connection { sender: rightButton; signal: "clicked()"; script: window.angle += 90 } + Connections { target: leftButton; onClicked: window.angle -= 90 } + Connections { target: rightButton; onClicked: window.angle += 90 } } diff --git a/src/declarative/QmlChanges.txt b/src/declarative/QmlChanges.txt index 25e0088..34e4834 100644 --- a/src/declarative/QmlChanges.txt +++ b/src/declarative/QmlChanges.txt @@ -7,6 +7,11 @@ Flickable: renamed viewportX -> contentX Flickable: renamed viewportY -> contentY Removed Flickable.reportedVelocitySmoothing Renamed MouseRegion -> MouseArea +Connection: syntax and rename: + Connection { sender: a; signal: foo(); script: xxx } + Connection { sender: a; signal: bar(); script: yyy } + becomes: + Connections { target: a; onFoo: xxx; onBar: yyy } QmlView ------- diff --git a/src/declarative/util/qdeclarativeconnection.cpp b/src/declarative/util/qdeclarativeconnection.cpp index e9ae74b..a180509 100644 --- a/src/declarative/util/qdeclarativeconnection.cpp +++ b/src/declarative/util/qdeclarativeconnection.cpp @@ -42,8 +42,10 @@ #include "qdeclarativeconnection_p.h" #include +#include #include #include +#include #include #include @@ -52,236 +54,192 @@ QT_BEGIN_NAMESPACE -class QDeclarativeConnectionPrivate : public QObjectPrivate +class QDeclarativeConnectionsPrivate : public QObjectPrivate { public: - QDeclarativeConnectionPrivate() : boundsignal(0), signalSender(0), scriptset(false), componentcomplete(false) {} + QDeclarativeConnectionsPrivate() : target(0), componentcomplete(false) {} + + QList boundsignals; + QObject *target; - QDeclarativeBoundSignal *boundsignal; - QObject *signalSender; - QDeclarativeScriptString script; - bool scriptset; - QString signal; bool componentcomplete; + + QByteArray data; }; /*! - \qmlclass Connection QDeclarativeConnection + \qmlclass Connections QDeclarativeConnections \since 4.7 - \brief A Connection object describes generalized connections to signals. + \brief A Connections object describes generalized connections to signals. When connecting to signals in QML, the usual way is to create an "on" handler that reacts when a signal is received, like this: \qml MouseArea { - onClicked: { foo(x+123,y+456) } + onClicked: { foo(...) } } \endqml However, in some cases, it is not possible to connect to a signal in this - way. For example, JavaScript-in-HTML style signal properties do not allow: + way, such as: \list - \i connecting to signals with the same name but different parameters - \i conformance checking that parameters are correctly named \i multiple connections to the same signal \i connections outside the scope of the signal sender - \i signals in classes with coincidentally-named on properties + \i connections to targets not defined in QML \endlist - When any of these are needed, the Connection object can be used instead. + When any of these are needed, the Connections object can be used instead. - For example, the above code can be changed to use a Connection object, + For example, the above code can be changed to use a Connections object, like this: \qml MouseArea { - Connection { - signal: "clicked(x,y)" - script: { foo(x+123,y+456) } + Connections { + onClicked: foo(...) } } \endqml - More generally, the Connection object can be a child of some other object than + More generally, the Connections object can be a child of some other object than the sender of the signal: \qml MouseArea { - id: mr + id: area } ... - Connection { - sender: mr - signal: "clicked(x,y)" - script: { foo(x+123,y+456) } + Connections { + target: area + onClicked: foo(...) } \endqml */ /*! \internal - \class QDeclarativeConnection - \brief The QDeclarativeConnection class describes generalized connections to signals. + \class QDeclarativeConnections + \brief The QDeclarativeConnections class describes generalized connections to signals. */ -QDeclarativeConnection::QDeclarativeConnection(QObject *parent) : - QObject(*(new QDeclarativeConnectionPrivate), parent) +QDeclarativeConnections::QDeclarativeConnections(QObject *parent) : + QObject(*(new QDeclarativeConnectionsPrivate), parent) { } -QDeclarativeConnection::~QDeclarativeConnection() +QDeclarativeConnections::~QDeclarativeConnections() { - Q_D(QDeclarativeConnection); - delete d->boundsignal; } /*! - \qmlproperty Object Connection::sender + \qmlproperty Object Connections::target This property holds the object that sends the signal. - By default, the sender is assumed to be the parent of the Connection. + By default, the target is assumed to be the parent of the Connections. */ -QObject *QDeclarativeConnection::signalSender() const +QObject *QDeclarativeConnections::target() const { - Q_D(const QDeclarativeConnection); - return d->signalSender ? d->signalSender : parent(); + Q_D(const QDeclarativeConnections); + return d->target ? d->target : parent(); } -void QDeclarativeConnection::setSignalSender(QObject *obj) +void QDeclarativeConnections::setTarget(QObject *obj) { - Q_D(QDeclarativeConnection); - if (d->signalSender == obj) + Q_D(QDeclarativeConnections); + if (d->target == obj) return; - disconnectIfValid(); - d->signalSender = obj; - connectIfValid(); + foreach (QDeclarativeBoundSignal *s, d->boundsignals) + delete s; + d->boundsignals.clear(); + d->target = obj; + connectSignals(); + emit targetChanged(); } -void QDeclarativeConnection::connectIfValid() + +QByteArray +QDeclarativeConnectionsParser::compile(const QList &props) { - Q_D(QDeclarativeConnection); - if (!d->componentcomplete) - return; - // boundsignal must not exist - if ((d->signalSender || parent()) && !d->signal.isEmpty() && d->scriptset) { - // create - // XXX scope? - int sigIdx = -1; - int lparen = d->signal.indexOf(QLatin1Char('(')); - QList sigparams; - if (lparen >= 0 && d->signal.length() > lparen+2) { - QStringList l = d->signal.mid(lparen+1,d->signal.length()-lparen-2).split(QLatin1Char(',')); - foreach (const QString &s, l) { - sigparams.append(s.trimmed().toUtf8()); - } + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + for(int ii = 0; ii < props.count(); ++ii) + { + QString propName = QString::fromUtf8(props.at(ii).name()); + if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { + error(props.at(ii), QDeclarativeConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return QByteArray(); } - QString signalname = d->signal.left(lparen); - QObject *sender = d->signalSender ? d->signalSender : parent(); - const QMetaObject *mo = sender->metaObject(); - int methods = mo->methodCount(); - for (int ii = 0; ii < methods; ++ii) { - QMetaMethod method = mo->method(ii); - QString methodName = QString::fromUtf8(method.signature()); - int idx = methodName.indexOf(QLatin1Char('(')); - methodName = methodName.left(idx); - if (methodName == signalname && (lparen<0 || method.parameterNames() == sigparams)) { - sigIdx = ii; - break; + + QList values = props.at(ii).assignedValues(); + + for (int i = 0; i < values.count(); ++i) { + const QVariant &value = values.at(i); + + if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QDeclarativeConnections::tr("Connections: nested objects not allowed")); + return QByteArray(); + } else if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QDeclarativeConnections::tr("Connections: syntax error")); + return QByteArray(); + } else { + QDeclarativeParser::Variant v = qvariant_cast(value); + if (v.isScript()) { + ds << propName; + ds << v.asScript(); + } else { + error(props.at(ii), QDeclarativeConnections::tr("Connections: script expected")); + return QByteArray(); + } } } - if (sigIdx < 0) { - // Cannot usefully warn, since could be in middle of - // changing sender and signal. - // XXX need state change transactions to do better - return; - } - - d->boundsignal = new QDeclarativeBoundSignal(qmlContext(this), d->script.script(), sender, mo->method(sigIdx), this); } -} -void QDeclarativeConnection::disconnectIfValid() -{ - Q_D(QDeclarativeConnection); - if (!d->componentcomplete) - return; - if ((d->signalSender || parent()) && !d->signal.isEmpty() && d->scriptset) { - // boundsignal must exist - // destroy - delete d->boundsignal; - d->boundsignal = 0; - } + return rv; } -void QDeclarativeConnection::componentComplete() +void QDeclarativeConnectionsParser::setCustomData(QObject *object, + const QByteArray &data) { - Q_D(QDeclarativeConnection); - d->componentcomplete=true; - connectIfValid(); + QDeclarativeConnectionsPrivate *p = + static_cast(QObjectPrivate::get(object)); + p->data = data; } -/*! - \qmlproperty script Connection::script - This property holds the JavaScript executed whenever the signal is sent. -*/ -QDeclarativeScriptString QDeclarativeConnection::script() const +void QDeclarativeConnections::connectSignals() { - Q_D(const QDeclarativeConnection); - return d->script; -} + Q_D(QDeclarativeConnections); + if (!d->componentcomplete) + return; -void QDeclarativeConnection::setScript(const QDeclarativeScriptString& script) -{ - Q_D(QDeclarativeConnection); - if ((d->signalSender || parent()) && !d->signal.isEmpty()) { - if (!d->scriptset) { - // mustn't exist - create - d->scriptset = true; - d->script = script; - connectIfValid(); + QDataStream ds(d->data); + while (!ds.atEnd()) { + QString propName; + ds >> propName; + QString script; + ds >> script; + QDeclarativeProperty prop(target(), propName); + if (!prop.isValid()) { + qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName); + } else if (prop.type() & QDeclarativeProperty::SignalProperty) { + QDeclarativeBoundSignal *signal = + new QDeclarativeBoundSignal(target(), prop.method(), this); + signal->setExpression(new QDeclarativeExpression(qmlContext(this), script, 0)); + d->boundsignals += signal; } else { - // must exist - update - d->script = script; - d->boundsignal->expression()->setExpression(script.script()); + qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName); } - } else { - d->scriptset = true; - d->script = script; } } -/*! - \qmlproperty string Connection::signal - This property holds the signal from the sender to which the script is attached. - - The signal's formal parameter names must be given in parentheses: - - \qml -Connection { - signal: "clicked(x,y)" - script: { ... } -} - \endqml -*/ -QString QDeclarativeConnection::signal() const -{ - Q_D(const QDeclarativeConnection); - return d->signal; -} - -void QDeclarativeConnection::setSignal(const QString& sig) +void QDeclarativeConnections::componentComplete() { - Q_D(QDeclarativeConnection); - if (d->signal == sig) - return; - disconnectIfValid(); - d->signal = sig; - connectIfValid(); + Q_D(QDeclarativeConnections); + d->componentcomplete=true; + connectSignals(); } - - QT_END_NAMESPACE diff --git a/src/declarative/util/qdeclarativeconnection_p.h b/src/declarative/util/qdeclarativeconnection_p.h index ae2efe9..3eacf12 100644 --- a/src/declarative/util/qdeclarativeconnection_p.h +++ b/src/declarative/util/qdeclarativeconnection_p.h @@ -39,11 +39,12 @@ ** ****************************************************************************/ -#ifndef QDECLARATIVECONNECTION_H -#define QDECLARATIVECONNECTION_H +#ifndef QDECLARATIVECONNECTIONS_H +#define QDECLARATIVECONNECTIONS_H #include #include +#include #include #include @@ -56,37 +57,41 @@ QT_MODULE(Declarative) class QDeclarativeBoundSignal; class QDeclarativeContext; -class QDeclarativeConnectionPrivate; -class Q_DECLARATIVE_EXPORT QDeclarativeConnection : public QObject, public QDeclarativeParserStatus +class QDeclarativeConnectionsPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeConnections : public QObject, public QDeclarativeParserStatus { Q_OBJECT - Q_DECLARE_PRIVATE(QDeclarativeConnection) + Q_DECLARE_PRIVATE(QDeclarativeConnections) Q_INTERFACES(QDeclarativeParserStatus) - Q_PROPERTY(QObject *sender READ signalSender WRITE setSignalSender) - Q_PROPERTY(QDeclarativeScriptString script READ script WRITE setScript) - Q_PROPERTY(QString signal READ signal WRITE setSignal) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) public: - QDeclarativeConnection(QObject *parent=0); - ~QDeclarativeConnection(); + QDeclarativeConnections(QObject *parent=0); + ~QDeclarativeConnections(); - QObject *signalSender() const; - void setSignalSender(QObject *); - QDeclarativeScriptString script() const; - void setScript(const QDeclarativeScriptString&); - QString signal() const; - void setSignal(const QString&); + QObject *target() const; + void setTarget(QObject *); + +Q_SIGNALS: + void targetChanged(); private: - void disconnectIfValid(); - void connectIfValid(); + void connectSignals(); void componentComplete(); }; +class QDeclarativeConnectionsParser : public QDeclarativeCustomParser +{ +public: + virtual QByteArray compile(const QList &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + QT_END_NAMESPACE -QML_DECLARE_TYPE(QDeclarativeConnection) +QML_DECLARE_TYPE(QDeclarativeConnections) QT_END_HEADER diff --git a/src/declarative/util/qdeclarativeutilmodule.cpp b/src/declarative/util/qdeclarativeutilmodule.cpp index 8d3d682..3d34e33 100644 --- a/src/declarative/util/qdeclarativeutilmodule.cpp +++ b/src/declarative/util/qdeclarativeutilmodule.cpp @@ -80,7 +80,7 @@ void QDeclarativeUtilModule::defineModule() QML_REGISTER_TYPE(Qt,4,6,Behavior,QDeclarativeBehavior); QML_REGISTER_TYPE(Qt,4,6,Binding,QDeclarativeBind); QML_REGISTER_TYPE(Qt,4,6,ColorAnimation,QDeclarativeColorAnimation); - QML_REGISTER_TYPE(Qt,4,6,Connection,QDeclarativeConnection); + QML_REGISTER_TYPE(Qt,4,6,Connections,QDeclarativeConnections); QML_REGISTER_TYPE(Qt,4,6,DateTimeFormatter,QDeclarativeDateTimeFormatter); QML_REGISTER_TYPE(Qt,4,6,EaseFollow,QDeclarativeEaseFollow);; QML_REGISTER_TYPE(Qt,4,6,FontLoader,QDeclarativeFontLoader); @@ -118,4 +118,5 @@ void QDeclarativeUtilModule::defineModule() QML_REGISTER_CUSTOM_TYPE(Qt, 4,6, ListModel, QDeclarativeListModel, QDeclarativeListModelParser); QML_REGISTER_CUSTOM_TYPE(Qt, 4,6, PropertyChanges, QDeclarativePropertyChanges, QDeclarativePropertyChangesParser); + QML_REGISTER_CUSTOM_TYPE(Qt, 4,6, Connections, QDeclarativeConnections, QDeclarativeConnectionsParser); } diff --git a/tests/auto/declarative/qdeclarativeconnection/data/test-connection.qml b/tests/auto/declarative/qdeclarativeconnection/data/test-connection.qml index 9534621..81ab599 100644 --- a/tests/auto/declarative/qdeclarativeconnection/data/test-connection.qml +++ b/tests/auto/declarative/qdeclarativeconnection/data/test-connection.qml @@ -6,5 +6,5 @@ Item { property bool tested: false signal testMe - Connection { sender: screen; signal: "widthChanged()"; script: screen.tested = true } + Connections { target: screen; onWidthChanged: screen.tested = true } } diff --git a/tests/auto/declarative/qdeclarativeconnection/data/test-connection2.qml b/tests/auto/declarative/qdeclarativeconnection/data/test-connection2.qml index 65fe23a..22e9422 100644 --- a/tests/auto/declarative/qdeclarativeconnection/data/test-connection2.qml +++ b/tests/auto/declarative/qdeclarativeconnection/data/test-connection2.qml @@ -1,3 +1,3 @@ import Qt 4.6 -Connection { id: connection; sender: connection; signal: "widthChanged()"; script: 1 == 1 } +Connections { id: connection; target: connection; onTargetChanged: 1 == 1 } diff --git a/tests/auto/declarative/qdeclarativeconnection/data/test-connection3.qml b/tests/auto/declarative/qdeclarativeconnection/data/test-connection3.qml index 32133f9..6e396c0 100644 --- a/tests/auto/declarative/qdeclarativeconnection/data/test-connection3.qml +++ b/tests/auto/declarative/qdeclarativeconnection/data/test-connection3.qml @@ -1,3 +1,3 @@ import Qt 4.6 -Connection {} +Connections {} diff --git a/tests/auto/declarative/qdeclarativeconnection/data/trimming.qml b/tests/auto/declarative/qdeclarativeconnection/data/trimming.qml index c27dc46..736d5e8 100644 --- a/tests/auto/declarative/qdeclarativeconnection/data/trimming.qml +++ b/tests/auto/declarative/qdeclarativeconnection/data/trimming.qml @@ -6,5 +6,5 @@ Item { property string tested signal testMe(int param1, string param2) - Connection { sender: screen; signal: "testMe(param1, param2)"; script: screen.tested = param2 + param1 } + Connections { target: screen; onTestMe: screen.tested = param2 + param1 } } diff --git a/tests/auto/declarative/qdeclarativeconnection/tst_qdeclarativeconnection.cpp b/tests/auto/declarative/qdeclarativeconnection/tst_qdeclarativeconnection.cpp index adf343f..f10217c 100644 --- a/tests/auto/declarative/qdeclarativeconnection/tst_qdeclarativeconnection.cpp +++ b/tests/auto/declarative/qdeclarativeconnection/tst_qdeclarativeconnection.cpp @@ -71,12 +71,10 @@ void tst_qdeclarativeconnection::defaultValues() { QDeclarativeEngine engine; QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/test-connection3.qml")); - QDeclarativeConnection *item = qobject_cast(c.create()); + QDeclarativeConnections *item = qobject_cast(c.create()); QVERIFY(item != 0); - QVERIFY(item->signalSender() == 0); - QCOMPARE(item->script().script(), QString()); - QCOMPARE(item->signal(), QString()); + QVERIFY(item->target() == 0); delete item; } @@ -85,14 +83,12 @@ void tst_qdeclarativeconnection::properties() { QDeclarativeEngine engine; QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/test-connection2.qml")); - QDeclarativeConnection *item = qobject_cast(c.create()); + QDeclarativeConnections *item = qobject_cast(c.create()); QVERIFY(item != 0); QVERIFY(item != 0); - QVERIFY(item->signalSender() == item); - QCOMPARE(item->script().script(), QString("1 == 1")); - QCOMPARE(item->signal(), QString("widthChanged()")); + QVERIFY(item->target() == item); delete item; } -- cgit v0.12