From 4922be635aeaf25343892a73b30ec0d99d467e37 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 14 Oct 2009 15:12:55 +1000 Subject: Support read-only iteration of list properties Eventually this should be extended to support modifying list properties. --- src/declarative/qml/qml.pri | 6 +- src/declarative/qml/qmlengine.cpp | 4 + src/declarative/qml/qmlengine_p.h | 2 + src/declarative/qml/qmllistscriptclass.cpp | 141 +++++++++++++++++++++ src/declarative/qml/qmllistscriptclass_p.h | 85 +++++++++++++ src/declarative/qml/qmlobjectscriptclass.cpp | 12 +- .../qmlecmascript/data/listProperties.qml | 44 +++++++ tests/auto/declarative/qmlecmascript/testtypes.h | 13 +- .../qmlecmascript/tst_qmlecmascript.cpp | 20 +++ 9 files changed, 320 insertions(+), 7 deletions(-) create mode 100644 src/declarative/qml/qmllistscriptclass.cpp create mode 100644 src/declarative/qml/qmllistscriptclass_p.h create mode 100644 tests/auto/declarative/qmlecmascript/data/listProperties.qml diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index 2e62a3b..58a18a2 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -44,7 +44,8 @@ SOURCES += qml/qmlparser.cpp \ qml/qmlcontextscriptclass.cpp \ qml/qmlglobalscriptclass.cpp \ qml/qmlvaluetypescriptclass.cpp \ - qml/qmltypenamescriptclass.cpp + qml/qmltypenamescriptclass.cpp \ + qml/qmllistscriptclass.cpp HEADERS += qml/qmlparser_p.h \ qml/qmlinstruction_p.h \ @@ -105,7 +106,8 @@ HEADERS += qml/qmlparser_p.h \ qml/qmlcontextscriptclass_p.h \ qml/qmlglobalscriptclass_p.h \ qml/qmlvaluetypescriptclass_p.h \ - qml/qmltypenamescriptclass_p.h + qml/qmltypenamescriptclass_p.h \ + qml/qmllistscriptclass_p.h # for qtscript debugger contains(QT_CONFIG, scripttools):QT += scripttools diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index 7e95428..4dbc336 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -86,6 +86,7 @@ #include #include #include +#include #ifdef Q_OS_WIN // for %APPDATA% #include "qt_windows.h" @@ -173,6 +174,8 @@ QmlEnginePrivate::~QmlEnginePrivate() valueTypeClass = 0; delete typeNameClass; typeNameClass = 0; + delete listClass; + listClass = 0; delete networkAccessManager; networkAccessManager = 0; delete nodeListClass; @@ -218,6 +221,7 @@ void QmlEnginePrivate::init() objectClass = new QmlObjectScriptClass(q); valueTypeClass = new QmlValueTypeScriptClass(q); typeNameClass = new QmlTypeNameScriptClass(q); + listClass = new QmlListScriptClass(q); rootContext = new QmlContext(q,true); #ifdef QT_SCRIPTTOOLS_LIB if (qmlDebugger()){ diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 4c90a80..a85ac55 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -96,6 +96,7 @@ class QScriptDeclarativeClass; class QmlTypeNameScriptClass; class QmlTypeNameCache; class QmlComponentAttached; +class QmlListScriptClass; class QmlEnginePrivate : public QObjectPrivate { @@ -129,6 +130,7 @@ public: QmlObjectScriptClass *objectClass; QmlValueTypeScriptClass *valueTypeClass; QmlTypeNameScriptClass *typeNameClass; + QmlListScriptClass *listClass; // Global script class QScriptClass *globalClass; // Used by DOM Core 3 API diff --git a/src/declarative/qml/qmllistscriptclass.cpp b/src/declarative/qml/qmllistscriptclass.cpp new file mode 100644 index 0000000..09bde8c --- /dev/null +++ b/src/declarative/qml/qmllistscriptclass.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmllistscriptclass_p.h" +#include + +QT_BEGIN_NAMESPACE + +struct ListData : public QScriptDeclarativeClass::Object { + QGuard object; + int propertyIdx; + QmlListScriptClass::ListType type; +}; + +QmlListScriptClass::QmlListScriptClass(QmlEngine *e) +: QScriptDeclarativeClass(QmlEnginePrivate::getScriptEngine(e)), engine(e) +{ + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + m_lengthId = createPersistentIdentifier(QLatin1String("length")); +} + +QmlListScriptClass::~QmlListScriptClass() +{ +} + +QScriptValue QmlListScriptClass::newList(QObject *object, int propId, ListType type) +{ + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + + if (!object || propId == -1) + return scriptEngine->nullValue(); + + ListData *data = new ListData; + data->object = object; + data->propertyIdx = propId; + data->type = type; + + return newObject(scriptEngine, this, data); +} + +QScriptClass::QueryFlags +QmlListScriptClass::queryProperty(Object *object, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + if (name == m_lengthId.identifier) + return QScriptClass::HandlesReadAccess; + + bool ok = false; + quint32 idx = toArrayIndex(name, &ok); + + if (ok) { + lastIndex = idx; + return QScriptClass::HandlesReadAccess; + } else { + return 0; + } +} + +QScriptValue QmlListScriptClass::property(Object *obj, const Identifier &name) +{ + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); + QmlEnginePrivate *enginePriv = QmlEnginePrivate::get(engine); + + ListData *data = (ListData *)obj; + if (!data->object) + return scriptEngine->undefinedValue(); + + void *list = 0; + void *args[] = { &list, 0 }; + QMetaObject::metacall(data->object, QMetaObject::ReadProperty, + data->propertyIdx, args); + + if (!list) + return scriptEngine->undefinedValue(); + + if (data->type == QListPtr) { + const QList &qlist = *((QList*)list); + + if (name == m_lengthId.identifier) + return qlist.count(); + else if (lastIndex < qlist.count()) + return enginePriv->objectClass->newQObject(qlist.at(lastIndex)); + else + return scriptEngine->undefinedValue(); + + } else { + Q_ASSERT(data->type == QmlListPtr); + const QmlList &qmllist = *((QmlList*)list); + + int count = qmllist.count(); + + if (name == m_lengthId.identifier) + return count; + else if (lastIndex < count) + return enginePriv->objectClass->newQObject(qmllist.at(lastIndex)); + else + return scriptEngine->undefinedValue(); + } +} + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qmllistscriptclass_p.h b/src/declarative/qml/qmllistscriptclass_p.h new file mode 100644 index 0000000..7592098 --- /dev/null +++ b/src/declarative/qml/qmllistscriptclass_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLLISTSCRIPTCLASS_P_H +#define QMLLISTSCRIPTCLASS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QmlEngine; +class QmlListScriptClass : public QScriptDeclarativeClass +{ +public: + QmlListScriptClass(QmlEngine *); + ~QmlListScriptClass(); + + enum ListType { QListPtr, QmlListPtr }; + QScriptValue newList(QObject *, int, ListType); + +protected: + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + virtual QScriptValue property(Object *, const Identifier &); + +private: + PersistentIdentifier m_lengthId; + QmlEngine *engine; + + quint32 lastIndex; +}; + +QT_END_NAMESPACE + +#endif // QMLLISTSCRIPTCLASS_P_H + diff --git a/src/declarative/qml/qmlobjectscriptclass.cpp b/src/declarative/qml/qmlobjectscriptclass.cpp index 80f2786..122db51 100644 --- a/src/declarative/qml/qmlobjectscriptclass.cpp +++ b/src/declarative/qml/qmlobjectscriptclass.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -64,8 +65,7 @@ QmlObjectScriptClass::QmlObjectScriptClass(QmlEngine *bindEngine) : QScriptDeclarativeClass(QmlEnginePrivate::getScriptEngine(bindEngine)), lastData(0), engine(bindEngine) { - engine = bindEngine; - QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(bindEngine); + QScriptEngine *scriptEngine = QmlEnginePrivate::getScriptEngine(engine); m_destroy = scriptEngine->newFunction(destroy); m_destroyId = createPersistentIdentifier(QLatin1String("destroy")); @@ -214,7 +214,13 @@ QScriptValue QmlObjectScriptClass::property(QObject *obj, const Identifier &name return enginePriv->valueTypeClass->newObject(obj, lastData->coreIndex, valueType); } - if (lastData->flags & QmlPropertyCache::Data::IsQObjectDerived) { + if (lastData->flags & QmlPropertyCache::Data::IsQList) { + return enginePriv->listClass->newList(obj, lastData->coreIndex, + QmlListScriptClass::QListPtr); + } else if (lastData->flags & QmlPropertyCache::Data::IsQmlList) { + return enginePriv->listClass->newList(obj, lastData->coreIndex, + QmlListScriptClass::QmlListPtr); + } if (lastData->flags & QmlPropertyCache::Data::IsQObjectDerived) { QObject *rv = 0; void *args[] = { &rv, 0 }; QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); diff --git a/tests/auto/declarative/qmlecmascript/data/listProperties.qml b/tests/auto/declarative/qmlecmascript/data/listProperties.qml new file mode 100644 index 0000000..73a1d6f --- /dev/null +++ b/tests/auto/declarative/qmlecmascript/data/listProperties.qml @@ -0,0 +1,44 @@ +import Qt.test 1.0 +import Qt 4.6 + +MyQmlObject { + id: root + + objectListProperty: [ + Object { property int a: 10 }, + Object { property int a: 11 } + ] + + objectQmlListProperty: [ + Object { property int a: 10 }, + Object { property int a: 1 }, + Object { property int a: 39 } + ] + + Script { + function calcTest1() { + var rv = 0; + for (var ii = 0; ii < root.objectListProperty.length; ++ii) { + rv += root.objectListProperty[ii].a; + } + return rv; + } + + function calcTest2() { + var rv = 0; + for (var ii = 0; ii < root.objectQmlListProperty.length; ++ii) { + rv += root.objectQmlListProperty[ii].a; + } + return rv; + } + } + + property int test1: calcTest1(); + property int test2: root.objectListProperty.length + property int test3: calcTest2(); + property int test4: root.objectQmlListProperty.length + property bool test5: root.objectQmlListProperty[1] != undefined + property bool test6: root.objectQmlListProperty[100] == undefined + property bool test7: root.objectListProperty[1] != undefined + property bool test8: root.objectListProperty[100] == undefined +} diff --git a/tests/auto/declarative/qmlecmascript/testtypes.h b/tests/auto/declarative/qmlecmascript/testtypes.h index e6c2c20..ae3a954 100644 --- a/tests/auto/declarative/qmlecmascript/testtypes.h +++ b/tests/auto/declarative/qmlecmascript/testtypes.h @@ -6,6 +6,7 @@ #include #include #include +#include #include class MyQmlAttachedObject : public QObject @@ -23,11 +24,14 @@ class MyQmlObject : public QObject Q_OBJECT Q_ENUMS(MyEnum) Q_ENUMS(MyEnum2) - Q_PROPERTY(int deleteOnSet READ deleteOnSet WRITE setDeleteOnSet); + Q_PROPERTY(int deleteOnSet READ deleteOnSet WRITE setDeleteOnSet) Q_PROPERTY(bool trueProperty READ trueProperty CONSTANT) Q_PROPERTY(bool falseProperty READ falseProperty CONSTANT) Q_PROPERTY(QString stringProperty READ stringProperty WRITE setStringProperty NOTIFY stringChanged) - Q_PROPERTY(QObject *objectProperty READ objectProperty WRITE setObjectProperty NOTIFY objectChanged); + Q_PROPERTY(QObject *objectProperty READ objectProperty WRITE setObjectProperty NOTIFY objectChanged) + Q_PROPERTY(QmlList *objectQmlListProperty READ objectQmlListProperty CONSTANT) + Q_PROPERTY(QList *objectListProperty READ objectListProperty CONSTANT) + public: MyQmlObject(): m_methodCalled(false), m_methodIntCalled(false), m_object(0) {} @@ -54,6 +58,9 @@ public: emit objectChanged(); } + QmlList *objectQmlListProperty() { return &m_objectQmlList; } + QList *objectListProperty() { return &m_objectQList; } + bool methodCalled() const { return m_methodCalled; } bool methodIntCalled() const { return m_methodIntCalled; } @@ -84,6 +91,8 @@ private: QObject *m_object; QString m_string; + QmlConcreteList m_objectQmlList; + QList m_objectQList; }; QML_DECLARE_TYPE(MyQmlObject); diff --git a/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp b/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp index 7fb1703..f24882a 100644 --- a/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp +++ b/tests/auto/declarative/qmlecmascript/tst_qmlecmascript.cpp @@ -63,6 +63,7 @@ private slots: void extendedObjectPropertyLookup(); void scriptErrors(); void signalTriggeredBindings(); + void listProperties(); private: QmlEngine engine; @@ -787,6 +788,25 @@ void tst_qmlecmascript::signalTriggeredBindings() QCOMPARE(object->property("test2").toReal(), 400.); } +/* +Test that list properties can be iterated from ECMAScript +*/ +void tst_qmlecmascript::listProperties() +{ + QmlComponent component(&engine, TEST_FILE("listProperties.qml")); + MyQmlObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->property("test1").toInt(), 21); + QCOMPARE(object->property("test2").toInt(), 2); + QCOMPARE(object->property("test3").toInt(), 50); + QCOMPARE(object->property("test4").toInt(), 3); + QCOMPARE(object->property("test5").toBool(), true); + QCOMPARE(object->property("test6").toBool(), true); + QCOMPARE(object->property("test7").toBool(), true); + QCOMPARE(object->property("test8").toBool(), true); +} + QTEST_MAIN(tst_qmlecmascript) #include "tst_qmlecmascript.moc" -- cgit v0.12