From 8491b0476a221cf136b412bda532662a69063045 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Thu, 2 Jul 2009 17:45:23 +1000 Subject: Remove Content and ContentWrapper property aliases do a better job of this --- src/declarative/fx/fx.pri | 3 - src/declarative/fx/qfxcomponentinstance.cpp | 1 - src/declarative/fx/qfxcontentwrapper.cpp | 144 ---------------------------- src/declarative/fx/qfxcontentwrapper.h | 91 ------------------ src/declarative/fx/qfxcontentwrapper_p.h | 71 -------------- 5 files changed, 310 deletions(-) delete mode 100644 src/declarative/fx/qfxcontentwrapper.cpp delete mode 100644 src/declarative/fx/qfxcontentwrapper.h delete mode 100644 src/declarative/fx/qfxcontentwrapper_p.h diff --git a/src/declarative/fx/fx.pri b/src/declarative/fx/fx.pri index fd6e480..0c26356 100644 --- a/src/declarative/fx/fx.pri +++ b/src/declarative/fx/fx.pri @@ -4,8 +4,6 @@ HEADERS += \ fx/qfxblurfilter.h \ fx/qfxcomponentinstance.h \ fx/qfxcomponentinstance_p.h \ - fx/qfxcontentwrapper.h \ - fx/qfxcontentwrapper_p.h \ fx/qfxevents_p.h \ fx/qfxflickable.h \ fx/qfxflickable_p.h \ @@ -51,7 +49,6 @@ SOURCES += \ fx/qfxanchors.cpp \ fx/qfxblurfilter.cpp \ fx/qfxcomponentinstance.cpp \ - fx/qfxcontentwrapper.cpp \ fx/qfxevents.cpp \ fx/qfxflickable.cpp \ fx/qfxflipable.cpp \ diff --git a/src/declarative/fx/qfxcomponentinstance.cpp b/src/declarative/fx/qfxcomponentinstance.cpp index 7f592a9..6cf8e74 100644 --- a/src/declarative/fx/qfxcomponentinstance.cpp +++ b/src/declarative/fx/qfxcomponentinstance.cpp @@ -42,7 +42,6 @@ #include "qfxcomponentinstance.h" #include "qfxcomponentinstance_p.h" #include -#include #include diff --git a/src/declarative/fx/qfxcontentwrapper.cpp b/src/declarative/fx/qfxcontentwrapper.cpp deleted file mode 100644 index 482442b..0000000 --- a/src/declarative/fx/qfxcontentwrapper.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/**************************************************************************** -** -** 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 "qfxcontentwrapper.h" -#include "qfxcontentwrapper_p.h" - - -QT_BEGIN_NAMESPACE -QML_DEFINE_TYPE(QFxContentWrapper,ContentWrapper) - -/*! - \qmlclass ContentWrapper QFxContentWrapper - \ingroup group_utility - \brief ContentWrapper provides a component which contains content. - \inherits Item - - In some cases the content of a component is not defined by the component itself. - For example, the items placed in a group box need to be specified external to - group box component definition itself. - In cases like these \l Content can be used to specify at what location in the component - the content should be placed. It is used in conjuntion with the \e content property of - ContentWrapper: any items listed as content will be placed in the location - specified by Content. The component containing the Content must be of type - ContentWrapper. - - GroupBox component definition: - \quotefile doc/src/snippets/declarative/GroupBox.qml - - \bold Note that in the above component definition ContentWrapper's \e children - property is specified explicitly since \e content is the default property. - - Component use: - \table - \row \o \image content.png - \o \quotefile doc/src/snippets/declarative/content.qml - \endtable - - \sa Content -*/ - -QFxContentWrapper::QFxContentWrapper(QFxItem *parent) -: QFxItem(*(new QFxContentWrapperPrivate), parent) -{ -} - -QFxContentWrapper::QFxContentWrapper(QFxContentWrapperPrivate &dd, QFxItem *parent) - : QFxItem(dd, parent) -{ -} - -/*! - \qmlproperty list ContentWrapper::content - - Contains the list of elements to replace the \l Content - placeholder. - - \sa Content -*/ -QList *QFxContentWrapper::content() -{ - Q_D(QFxContentWrapper); - return &(d->_content); -} - -void QFxContentWrapper::componentComplete() -{ - QFxItem::componentComplete(); - if (content()->size() < 1) - return; - - QList nodes; - nodes.append(this); - QFxItem *target = findContent(nodes); - if (!target) - return; - target = target->itemParent(); - - QList myContent(*content()); - for (int ii = 0; ii < myContent.count(); ++ii) - myContent.at(ii)->setParent(target); -} - -QFxItem *QFxContentWrapper::findContent(QList &nodes) -{ - QSimpleCanvasItem *item = nodes.takeFirst(); - if (qobject_cast(item)) - return static_cast(item); - nodes << item->children(); - if (nodes.isEmpty()) - return 0; - return findContent(nodes); -} - -QML_DEFINE_TYPE(QFxContent,Content) - -/*! - \qmlclass Content QFxContent - \ingroup group_utility - \brief Content is used as a placeholder for the content of a component. - \inherits Item - - The Content element is used to place content within a component. - See \l ContentWrapper for usage. -*/ - -QT_END_NAMESPACE diff --git a/src/declarative/fx/qfxcontentwrapper.h b/src/declarative/fx/qfxcontentwrapper.h deleted file mode 100644 index 9a9a89c..0000000 --- a/src/declarative/fx/qfxcontentwrapper.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** 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 QFXCONTENTWRAPPER_H -#define QFXCONTENTWRAPPER_H - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -QT_MODULE(Declarative) - -class QFxContentWrapperPrivate; -class Q_DECLARATIVE_EXPORT QFxContentWrapper : public QFxItem -{ - Q_OBJECT - - Q_PROPERTY(QList* content READ content DESIGNABLE false) - Q_CLASSINFO("DefaultProperty", "content") -public: - QFxContentWrapper(QFxItem *parent=0); - - QList *content(); - -private: - void create(); - QFxItem *findContent(QList &nodes); - -protected: - void componentComplete(); - QFxContentWrapper(QFxContentWrapperPrivate &dd, QFxItem *parent); - -private: - Q_DECLARE_PRIVATE(QFxContentWrapper) -}; - -class Q_DECLARATIVE_EXPORT QFxContent : public QFxItem -{ - Q_OBJECT -public: - QFxContent(QFxItem *parent=0) : QFxItem(parent) {} -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QFxContentWrapper) -QML_DECLARE_TYPE(QFxContent) - -QT_END_HEADER - -#endif // QFXCONTENTWRAPPER_H diff --git a/src/declarative/fx/qfxcontentwrapper_p.h b/src/declarative/fx/qfxcontentwrapper_p.h deleted file mode 100644 index a75fa1e..0000000 --- a/src/declarative/fx/qfxcontentwrapper_p.h +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** 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 QFXCONTENTWRAPPER_P_H -#define QFXCONTENTWRAPPER_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 "qfxitem_p.h" -#include "qfxcontentwrapper.h" - - -QT_BEGIN_NAMESPACE -class QFxContentWrapperPrivate : public QFxItemPrivate -{ - Q_DECLARE_PUBLIC(QFxContentWrapper) -public: - QFxContentWrapperPrivate() { } - - QList _content; -}; - -QT_END_NAMESPACE -#endif // QFXCONTENTWRAPPER_P_H -- cgit v0.12 From f3499122c99ef31cd064559c32b3a7afda6ec5b0 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 2 Jul 2009 11:07:53 +0200 Subject: Small change to make move and replace methods overridable. --- src/declarative/qml/rewriter/rewriter.cpp | 9 +++++++-- src/declarative/qml/rewriter/rewriter_p.h | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/declarative/qml/rewriter/rewriter.cpp b/src/declarative/qml/rewriter/rewriter.cpp index 2ce927c..ed45f16 100644 --- a/src/declarative/qml/rewriter/rewriter.cpp +++ b/src/declarative/qml/rewriter/rewriter.cpp @@ -83,14 +83,19 @@ void Rewriter::moveTextBefore(const AST::SourceLocation &firstLoc, const AST::SourceLocation &lastLoc, const AST::SourceLocation &loc) { - textWriter.move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset); + move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset); } void Rewriter::moveTextAfter(const AST::SourceLocation &firstLoc, const AST::SourceLocation &lastLoc, const AST::SourceLocation &loc) { - textWriter.move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset + loc.length); + move(firstLoc.offset, lastLoc.offset + lastLoc.length - firstLoc.offset, loc.offset + loc.length); +} + +void Rewriter::move(int pos, int length, int to) +{ + textWriter.move(pos, length, to); } QT_END_NAMESPACE diff --git a/src/declarative/qml/rewriter/rewriter_p.h b/src/declarative/qml/rewriter/rewriter_p.h index fcb9ca5..44f3cce 100644 --- a/src/declarative/qml/rewriter/rewriter_p.h +++ b/src/declarative/qml/rewriter/rewriter_p.h @@ -121,7 +121,8 @@ public: // // low-level offset based API // - void replace(int offset, int length, const QString &text); + virtual void replace(int offset, int length, const QString &text); + virtual void move(int pos, int length, int to); void insertText(int offset, const QString &text); void removeText(int offset, int length); -- cgit v0.12 From bdaf37f74e69f74c8fec23739e4dad23121af415 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Thu, 2 Jul 2009 21:06:32 +1000 Subject: Support parameterized synthesized methods --- src/declarative/qml/qmlcompiler.cpp | 65 ++++++--- src/declarative/qml/qmlparser.cpp | 2 +- src/declarative/qml/qmlparser_p.h | 1 + src/declarative/qml/qmlscriptparser.cpp | 15 +- src/declarative/qml/qmlvme.cpp | 2 +- src/declarative/qml/qmlvmemetaobject.cpp | 243 ++++++++++++++++--------------- src/declarative/qml/qmlvmemetaobject_p.h | 59 ++++++-- 7 files changed, 221 insertions(+), 166 deletions(-) diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index aa2cf84..b04c932 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -63,6 +63,7 @@ #include #include #include "parser/qmljsast_p.h" +#include #include "qmlscriptparser_p.h" @@ -1406,6 +1407,8 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) obj->dynamicSlots.isEmpty()) return true; + QByteArray dynamicData(sizeof(QmlVMEMetaData), (char)0); + QMetaObjectBuilder builder; if (obj->metatype) builder.setClassName(QByteArray(obj->metatype->className()) + "QML"); @@ -1420,41 +1423,65 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) builder.addClassInfo("DefaultProperty", p.name); QByteArray type; + int propertyType; switch(p.type) { case Object::DynamicProperty::Alias: hasAlias = true; continue; break; case Object::DynamicProperty::Variant: + propertyType = -1; type = "QVariant"; break; case Object::DynamicProperty::Int: + propertyType = QVariant::Int; type = "int"; break; case Object::DynamicProperty::Bool: + propertyType = QVariant::Bool; type = "bool"; break; case Object::DynamicProperty::Real: + propertyType = QVariant::Double; type = "double"; break; case Object::DynamicProperty::String: + propertyType = QVariant::String; type = "QString"; break; case Object::DynamicProperty::Url: + propertyType = QVariant::Url; type = "QUrl"; break; case Object::DynamicProperty::Color: + propertyType = QVariant::Color; type = "QColor"; break; case Object::DynamicProperty::Date: + propertyType = QVariant::Date; type = "QDate"; break; } + ((QmlVMEMetaData *)dynamicData.data())->propertyCount++; + QmlVMEMetaData::PropertyData propertyData = { propertyType }; + dynamicData.append((char *)&propertyData, sizeof(propertyData)); + builder.addSignal(p.name + "Changed()"); builder.addProperty(p.name, type, ii); } + if (preAlias != -1) { + for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { + const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); + + if (p.type == Object::DynamicProperty::Alias) { + ((QmlVMEMetaData *)dynamicData.data())->aliasCount++; + compileAlias(builder, dynamicData, obj, p); + } + } + } + for (int ii = 0; ii < obj->dynamicSignals.count(); ++ii) { const Object::DynamicSignal &s = obj->dynamicSignals.at(ii); QByteArray sig(s.name + "("); @@ -1465,38 +1492,30 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) sig.append(")"); QMetaMethodBuilder b = builder.addSignal(sig); b.setParameterNames(s.parameterNames); + ((QmlVMEMetaData *)dynamicData.data())->signalCount++; } int slotStart = obj->dynamicSlots.isEmpty()?-1:output->primitives.count(); for (int ii = 0; ii < obj->dynamicSlots.count(); ++ii) { const Object::DynamicSlot &s = obj->dynamicSlots.at(ii); - builder.addSlot(s.name + "()"); + QByteArray sig(s.name + "("); + for (int jj = 0; jj < s.parameterNames.count(); ++jj) { + if (jj) sig.append(","); + sig.append("QVariant"); + } + sig.append(")"); + QMetaMethodBuilder b = builder.addSlot(sig); + b.setParameterNames(s.parameterNames); + + ((QmlVMEMetaData *)dynamicData.data())->methodCount++; + QmlVMEMetaData::MethodData methodData = { s.parameterNames.count() }; + dynamicData.append((char *)&methodData, sizeof(methodData)); if (preAlias == -1) output->primitives << s.body; } - QByteArray aliasData; - if (preAlias != -1) { - int dynProperties = 0; - QByteArray data; - int propCount = builder.propertyCount(); - int signalCount = builder.methodCount(); - for (int ii = 0; ii < obj->dynamicProperties.count(); ++ii) { - const Object::DynamicProperty &p = obj->dynamicProperties.at(ii); - - if (p.type == Object::DynamicProperty::Alias) { - dynProperties++; - compileAlias(builder, data, obj, p); - } - } - aliasData.append((const char *)&dynProperties, sizeof(int)); - aliasData.append((const char *)&propCount, sizeof(int)); - aliasData.append((const char *)&signalCount, sizeof(int)); - aliasData.append(data); - } - if (obj->metatype) builder.setSuperClass(obj->metatype); @@ -1506,7 +1525,7 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) if (preAlias != -1) { QmlInstruction &store = output->bytecode[preAlias]; - store.storeMeta.aliasData = output->indexForByteArray(aliasData); + store.storeMeta.aliasData = output->indexForByteArray(dynamicData); qFree(output->synthesizedMetaObjects.at(store.storeMeta.data)); output->synthesizedMetaObjects[store.storeMeta.data] = obj->extObjectData; @@ -1516,7 +1535,7 @@ bool QmlCompiler::compileDynamicMeta(QmlParser::Object *obj, int preAlias) store.type = QmlInstruction::StoreMetaObject; store.storeMeta.data = output->synthesizedMetaObjects.count() - 1; store.storeMeta.slotData = slotStart; - store.storeMeta.aliasData = -1; + store.storeMeta.aliasData = output->indexForByteArray(dynamicData); store.line = obj->location.start.line; output->bytecode << store; diff --git a/src/declarative/qml/qmlparser.cpp b/src/declarative/qml/qmlparser.cpp index 2e7eb69..8daab6a 100644 --- a/src/declarative/qml/qmlparser.cpp +++ b/src/declarative/qml/qmlparser.cpp @@ -133,7 +133,7 @@ QmlParser::Object::DynamicSlot::DynamicSlot() } QmlParser::Object::DynamicSlot::DynamicSlot(const DynamicSlot &o) -: name(o.name), body(o.body) +: name(o.name), body(o.body), parameterNames(o.parameterNames) { } diff --git a/src/declarative/qml/qmlparser_p.h b/src/declarative/qml/qmlparser_p.h index 803c73e..7550870 100644 --- a/src/declarative/qml/qmlparser_p.h +++ b/src/declarative/qml/qmlparser_p.h @@ -166,6 +166,7 @@ namespace QmlParser QByteArray name; QString body; + QList parameterNames; }; // The list of dynamic properties diff --git a/src/declarative/qml/qmlscriptparser.cpp b/src/declarative/qml/qmlscriptparser.cpp index 1c88018..82f6a60 100644 --- a/src/declarative/qml/qmlscriptparser.cpp +++ b/src/declarative/qml/qmlscriptparser.cpp @@ -723,20 +723,19 @@ bool ProcessAST::visit(AST::UiSourceElement *node) if (AST::FunctionDeclaration *funDecl = AST::cast(node->sourceElement)) { - if(funDecl->formals) { - QmlError error; - error.setDescription(QCoreApplication::translate("QmlParser","Slot declarations must be parameterless")); - error.setLine(funDecl->lparenToken.startLine); - error.setColumn(funDecl->lparenToken.startColumn); - _parser->_errors << error; - return false; + Object::DynamicSlot slot; + + AST::FormalParameterList *f = funDecl->formals; + while (f) { + slot.parameterNames << f->name->asString().toUtf8(); + f = f->finish(); } QString body = textAt(funDecl->lbraceToken, funDecl->rbraceToken); - Object::DynamicSlot slot; slot.name = funDecl->name->asString().toUtf8(); slot.body = body; obj->dynamicSlots << slot; + } else { QmlError error; error.setDescription(QCoreApplication::translate("QmlParser","QmlJS declaration outside Script element")); diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index 0d88e02..a11caeb 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -239,7 +239,7 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, QmlCompiledComp case QmlInstruction::StoreMetaObject: { QObject *target = stack.top(); - new QmlVMEMetaObject(target, synthesizedMetaObjects.at(instr.storeMeta.data), &comp->primitives, instr.storeMeta.slotData, (instr.storeMeta.aliasData != -1)?datas.at(instr.storeMeta.aliasData):QByteArray(), comp); + new QmlVMEMetaObject(target, synthesizedMetaObjects.at(instr.storeMeta.data), &comp->primitives, instr.storeMeta.slotData, (const QmlVMEMetaData *)datas.at(instr.storeMeta.aliasData).constData(), comp); } break; diff --git a/src/declarative/qml/qmlvmemetaobject.cpp b/src/declarative/qml/qmlvmemetaobject.cpp index dc06bc5..6d14689 100644 --- a/src/declarative/qml/qmlvmemetaobject.cpp +++ b/src/declarative/qml/qmlvmemetaobject.cpp @@ -55,10 +55,10 @@ QmlVMEMetaObject::QmlVMEMetaObject(QObject *obj, const QMetaObject *other, QList *strData, int slotData, - const QByteArray &alias, + const QmlVMEMetaData *meta, QmlRefCount *rc) -: object(obj), ref(rc), slotData(strData), slotDataIdx(slotData), parent(0), - aliasData(alias), aliases(0), aliasArray(0) +: object(obj), ref(rc), metaData(meta), slotData(strData), + slotDataIdx(slotData), parent(0) { if (ref) ref->addref(); @@ -71,44 +71,18 @@ QmlVMEMetaObject::QmlVMEMetaObject(QObject *obj, parent = static_cast(op->metaObject); op->metaObject = this; - if (!aliasData.isEmpty()) { - aliases = (Aliases *)aliasData.constData(); - aliasArray = (AliasArray *)(aliasData.constData() + 3 * sizeof(int)); - aConnected.resize(aliases->aliasCount); - } + propOffset = QAbstractDynamicMetaObject::propertyOffset(); + methodOffset = QAbstractDynamicMetaObject::methodOffset(); - baseProp = propertyOffset(); - baseSig = methodOffset(); - int propCount = propertyCount() - (aliases?aliases->aliasCount:0); - data = new QVariant[propCount - baseProp]; - vTypes.resize(propCount - baseProp); + data = new QVariant[metaData->propertyCount]; + aConnected.resize(metaData->aliasCount); // ### Optimize - for (int ii = baseProp; ii < propCount; ++ii) { - QMetaProperty prop = property(ii); - if ((int)prop.type() != -1) { - data[ii - baseProp] = QVariant((QVariant::Type)prop.userType()); - } else { - vTypes.setBit(ii - baseProp, true); - } + for (int ii = 0; ii < metaData->propertyCount; ++ii) { + int t = (metaData->propertyData() + ii)->propertyType; + if (t != -1) + data[ii] = QVariant((QVariant::Type)t); } - - baseSlot = -1; - slotCount = 0; - for (int ii = baseSig; ii < methodCount(); ++ii) { - QMetaMethod m = method(ii); - if (m.methodType() == QMetaMethod::Slot) { - if (baseSlot == -1) - baseSlot = ii; - } else { - if (baseSlot != -1) { - slotCount = ii - baseSlot; - break; - } - } - } - if(baseSlot != -1 && !slotCount) - slotCount = methodCount() - baseSlot; } QmlVMEMetaObject::~QmlVMEMetaObject() @@ -120,111 +94,146 @@ QmlVMEMetaObject::~QmlVMEMetaObject() delete [] data; } -int QmlVMEMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +int QmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) { + int id = _id; if(c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) { - if (id >= baseProp) { - int propId = id - baseProp; - bool needActivate = false; + if (id >= propOffset) { + id -= propOffset; + + if (id < metaData->propertyCount) { + int t = (metaData->propertyData() + id)->propertyType; + bool needActivate = false; + + if (t == -1) { + + if (c == QMetaObject::ReadProperty) { + *reinterpret_cast(a[0]) = data[id]; + } else if (c == QMetaObject::WriteProperty) { + needActivate = + (data[id] != *reinterpret_cast(a[0])); + data[id] = *reinterpret_cast(a[0]); + } + + } else { + + if (c == QMetaObject::ReadProperty) { + switch(t) { + case QVariant::Int: + *reinterpret_cast(a[0]) = data[id].toInt(); + break; + case QVariant::Bool: + *reinterpret_cast(a[0]) = data[id].toBool(); + break; + case QVariant::Double: + *reinterpret_cast(a[0]) = data[id].toDouble(); + break; + case QVariant::String: + *reinterpret_cast(a[0]) = data[id].toString(); + break; + case QVariant::Url: + *reinterpret_cast(a[0]) = data[id].toUrl(); + break; + case QVariant::Color: + *reinterpret_cast(a[0]) = data[id].value(); + break; + case QVariant::Date: + *reinterpret_cast(a[0]) = data[id].toDate(); + break; + default: + break; + } + + } else if (c == QMetaObject::WriteProperty) { + + QVariant value = QVariant((QVariant::Type)data[id].type(), a[0]); + needActivate = (data[id] != value); + data[id] = value; + } + + } + + if (c == QMetaObject::WriteProperty && needActivate) { + activate(object, methodOffset + id, 0); + } + + return -1; + } + + id -= metaData->propertyCount; + + if (id < metaData->aliasCount) { - if (aliases && propId >= aliases->propCount) { QmlContext *ctxt = qmlContext(object); if (!ctxt) return -1; - int aliasId = propId - aliases->propCount; - AliasArray *d = aliasArray + aliasId; + QmlVMEMetaData::AliasData *d = metaData->aliasData() + id; QmlContextPrivate *ctxtPriv = (QmlContextPrivate *)QObjectPrivate::get(ctxt); - QObject *target = *(QObject **)ctxtPriv->propertyValues[d->contextIdx].data(); + QObject *target = + *(QObject **)ctxtPriv->propertyValues[d->contextIdx].data(); if (!target) return -1; - if (c == QMetaObject::ReadProperty && - !aConnected.testBit(aliasId)) { - - int mySigIdx = baseSig + aliasId + aliases->signalOffset; - QMetaObject::connect(ctxt, d->contextIdx + ctxtPriv->notifyIndex, object, mySigIdx); + if (c == QMetaObject::ReadProperty && !aConnected.testBit(id)) { + int sigIdx = methodOffset + id + metaData->propertyCount; + QMetaObject::connect(ctxt, d->contextIdx + ctxtPriv->notifyIndex, object, sigIdx); - QMetaProperty prop = target->metaObject()->property(d->propIdx); + QMetaProperty prop = + target->metaObject()->property(d->propertyIdx); if (prop.hasNotifySignal()) QMetaObject::connect(target, prop.notifySignalIndex(), - object, mySigIdx); - aConnected.setBit(aliasId); - - } - return QMetaObject::metacall(target, c, d->propIdx, a); - - } else if (vTypes.testBit(propId)) { - if (c == QMetaObject::ReadProperty) { - *reinterpret_cast(a[0]) = data[propId]; - } else if (c == QMetaObject::WriteProperty) { - needActivate = - (data[propId] != *reinterpret_cast(a[0])); - data[propId] = *reinterpret_cast(a[0]); + object, sigIdx); + aConnected.setBit(id); } - } else { - if (c == QMetaObject::ReadProperty) { - switch(data[propId].type()) { - case QVariant::Int: - *reinterpret_cast(a[0]) = data[propId].toInt(); - break; - case QVariant::Bool: - *reinterpret_cast(a[0]) = data[propId].toBool(); - break; - case QVariant::Double: - *reinterpret_cast(a[0]) = data[propId].toDouble(); - break; - case QVariant::String: - *reinterpret_cast(a[0]) = data[propId].toString(); - break; - case QVariant::Url: - *reinterpret_cast(a[0]) = data[propId].toUrl(); - break; - case QVariant::Color: - *reinterpret_cast(a[0]) = data[propId].value(); - break; - case QVariant::Date: - *reinterpret_cast(a[0]) = data[propId].toDate(); - break; - default: - qFatal("Unknown type"); - break; - } - } else if (c == QMetaObject::WriteProperty) { + return QMetaObject::metacall(target, c, d->propertyIdx, a); - QVariant value = QVariant((QVariant::Type)data[propId].type(), a[0]); - needActivate = (data[propId] != value); - data[propId] = value; - } } + return -1; - if (c == QMetaObject::WriteProperty && needActivate) { - activate(object, baseSig + propId, 0); - } + } - return id; - } } else if(c == QMetaObject::InvokeMetaMethod) { - if (id >= baseSig && (aliases && id >= baseSig + aliases->signalOffset)) { - QMetaObject::activate(object, id, a); - return id; - } else if (id >= baseSig && (baseSlot == -1 || id < baseSlot)) { - QMetaObject::activate(object, id, a); - return id; - } else if (id >= baseSlot && id < (baseSlot + slotCount)) { - int idx = id - baseSlot + slotDataIdx; - QmlContext *ctxt = qmlContext(object); - QmlExpression expr(ctxt, slotData->at(idx), object); - expr.setTrackChange(false); - expr.value(); - return id; + + if (id >= methodOffset) { + + id -= methodOffset; + int plainSignals = metaData->signalCount + metaData->propertyCount + + metaData->aliasCount; + if (id < plainSignals) { + QMetaObject::activate(object, _id, a); + return -1; + } + + id -= plainSignals; + + if (id < metaData->methodCount) { + QString code = slotData->at(id + slotDataIdx); + QmlContext *ctxt = qmlContext(object); + + if (0 == (metaData->methodData() + id)->parameterCount) { + QmlExpression expr(ctxt, code, object); + expr.setTrackChange(false); + expr.value(); + } else { + QmlContext newCtxt(ctxt); + QMetaMethod m = method(_id); + QList names = m.parameterNames(); + for (int ii = 0; ii < names.count(); ++ii) + newCtxt.setContextProperty(names.at(ii), *(QVariant *)a[ii + 1]); + QmlExpression expr(&newCtxt, code, object); + expr.setTrackChange(false); + expr.value(); + } + } + return -1; } } if (parent) - return parent->metaCall(c, id, a); + return parent->metaCall(c, _id, a); else - return object->qt_metacall(c, id, a); + return object->qt_metacall(c, _id, a); } QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlvmemetaobject_p.h b/src/declarative/qml/qmlvmemetaobject_p.h index 45fb33d..6421c3f 100644 --- a/src/declarative/qml/qmlvmemetaobject_p.h +++ b/src/declarative/qml/qmlvmemetaobject_p.h @@ -59,11 +59,45 @@ #include QT_BEGIN_NAMESPACE + +struct QmlVMEMetaData +{ + short propertyCount; + short aliasCount; + short signalCount; + short methodCount; + + struct AliasData { + int contextIdx; + int propertyIdx; + }; + + struct PropertyData { + int propertyType; + }; + + struct MethodData { + int parameterCount; + }; + + PropertyData *propertyData() const { + return (PropertyData *)(((const char *)this) + sizeof(QmlVMEMetaData)); + } + + AliasData *aliasData() const { + return (AliasData *)(propertyData() + propertyCount); + } + + MethodData *methodData() const { + return (MethodData *)(aliasData() + propertyCount); + } +}; + class QmlRefCount; class QmlVMEMetaObject : public QAbstractDynamicMetaObject { public: - QmlVMEMetaObject(QObject *, const QMetaObject *, QList *, int slotData, const QByteArray &aliasData, QmlRefCount * = 0); + QmlVMEMetaObject(QObject *, const QMetaObject *, QList *, int slotData, const QmlVMEMetaData *data, QmlRefCount * = 0); ~QmlVMEMetaObject(); protected: @@ -72,26 +106,19 @@ protected: private: QObject *object; QmlRefCount *ref; - int baseProp; - int baseSig; - int baseSlot; - int slotCount; + + const QmlVMEMetaData *metaData; + int propOffset; + int methodOffset; + QVariant *data; - QBitArray vTypes; QBitArray aConnected; + QList *slotData; int slotDataIdx; + QAbstractDynamicMetaObject *parent; - QByteArray aliasData; - struct Aliases { - int aliasCount; - int propCount; - int signalOffset; - } *aliases; - struct AliasArray { - int contextIdx; - int propIdx; - } *aliasArray; + }; QT_END_NAMESPACE -- cgit v0.12 From 67a9e4fe6e35bf517bd2f99540cf88a8e567b0ca Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 3 Jul 2009 11:21:30 +1000 Subject: Move QmlExpression into its own file --- src/declarative/debugger/qmldebugger.cpp | 1 + src/declarative/qml/qml.pri | 2 + src/declarative/qml/qmlcontext.cpp | 4 +- src/declarative/qml/qmlcontext.h | 1 + src/declarative/qml/qmlengine.cpp | 523 -------------------------- src/declarative/qml/qmlengine.h | 1 + src/declarative/qml/qmlengine_p.h | 71 ---- src/declarative/qml/qmlexpression.cpp | 604 +++++++++++++++++++++++++++++++ src/declarative/qml/qmlexpression.h | 2 +- src/declarative/qml/qmlexpression_p.h | 138 +++++++ 10 files changed, 750 insertions(+), 597 deletions(-) create mode 100644 src/declarative/qml/qmlexpression.cpp create mode 100644 src/declarative/qml/qmlexpression_p.h diff --git a/src/declarative/debugger/qmldebugger.cpp b/src/declarative/debugger/qmldebugger.cpp index 9122d87..78c4090 100644 --- a/src/declarative/debugger/qmldebugger.cpp +++ b/src/declarative/debugger/qmldebugger.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index efe4d3f..fa09935 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -2,6 +2,7 @@ SOURCES += qml/qmlparser.cpp \ qml/qmlinstruction.cpp \ qml/qmlvmemetaobject.cpp \ qml/qmlengine.cpp \ + qml/qmlexpression.cpp \ qml/qmlbindablevalue.cpp \ qml/qmlmetaproperty.cpp \ qml/qmlcomponent.cpp \ @@ -45,6 +46,7 @@ HEADERS += qml/qmlparser_p.h \ qml/qmlvme_p.h \ qml/qmlcompiler_p.h \ qml/qmlengine_p.h \ + qml/qmlexpression_p.h \ qml/qmlprivate.h \ qml/qmldom.h \ qml/qmldom_p.h \ diff --git a/src/declarative/qml/qmlcontext.cpp b/src/declarative/qml/qmlcontext.cpp index e5016f2..b590596 100644 --- a/src/declarative/qml/qmlcontext.cpp +++ b/src/declarative/qml/qmlcontext.cpp @@ -41,12 +41,12 @@ #include #include +#include #include #include #include #include - -#include +#include // 6-bits #define MAXIMUM_DEFAULT_OBJECTS 63 diff --git a/src/declarative/qml/qmlcontext.h b/src/declarative/qml/qmlcontext.h index ce5fe52..f5858cb 100644 --- a/src/declarative/qml/qmlcontext.h +++ b/src/declarative/qml/qmlcontext.h @@ -89,6 +89,7 @@ private: friend class QmlEngine; friend class QmlEnginePrivate; friend class QmlExpression; + friend class QmlExpressionPrivate; friend class QmlBasicScript; friend class QmlContextScriptClass; friend class QmlObjectScriptClass; diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index ad3f4b6..f26e389 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -925,457 +925,6 @@ QScriptValue QmlEngine::createQMLObject(QScriptContext *ctxt, QScriptEngine *eng return engine->nullValue(); } -QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b) -: q(b), ctxt(0), sseData(0), proxy(0), me(0), trackChange(false), line(-1), id(0), log(0) -{ -} - -QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, void *expr, QmlRefCount *rc) -: q(b), ctxt(0), sse((const char *)expr, rc), sseData(0), proxy(0), me(0), trackChange(true), line(-1), id(0), log(0) -{ -} - -QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, const QString &expr) -: q(b), ctxt(0), expression(expr), sseData(0), proxy(0), me(0), trackChange(true), line(-1), id(0), log(0) -{ -} - -QmlExpressionPrivate::~QmlExpressionPrivate() -{ - sse.deleteScriptState(sseData); - sseData = 0; - delete proxy; - delete log; -} - -/*! - Create an invalid QmlExpression. - - As the expression will not have an associated QmlContext, this will be a - null expression object and its value will always be an invalid QVariant. - */ -QmlExpression::QmlExpression() -: d(new QmlExpressionPrivate(this)) -{ -} - -/*! \internal */ -QmlExpression::QmlExpression(QmlContext *ctxt, void *expr, - QmlRefCount *rc, QObject *me) -: d(new QmlExpressionPrivate(this, expr, rc)) -{ - d->ctxt = ctxt; - if(ctxt && ctxt->engine()) - d->id = ctxt->engine()->d_func()->getUniqueId(); - if(ctxt) - ctxt->d_func()->childExpressions.insert(this); - d->me = me; -} - -/*! - Create a QmlExpression object. - - The \a expression ECMAScript will be executed in the \a ctxt QmlContext. - If specified, the \a scope object's properties will also be in scope during - the expression's execution. -*/ -QmlExpression::QmlExpression(QmlContext *ctxt, const QString &expression, - QObject *scope) -: d(new QmlExpressionPrivate(this, expression)) -{ - d->ctxt = ctxt; - if(ctxt && ctxt->engine()) - d->id = ctxt->engine()->d_func()->getUniqueId(); - if(ctxt) - ctxt->d_func()->childExpressions.insert(this); - d->me = scope; -} - -/*! - Destroy the QmlExpression instance. -*/ -QmlExpression::~QmlExpression() -{ - if (d->ctxt) - d->ctxt->d_func()->childExpressions.remove(this); - delete d; d = 0; -} - -/*! - Returns the QmlEngine this expression is associated with, or 0 if there - is no association or the QmlEngine has been destroyed. -*/ -QmlEngine *QmlExpression::engine() const -{ - return d->ctxt?d->ctxt->engine():0; -} - -/*! - Returns the QmlContext this expression is associated with, or 0 if there - is no association or the QmlContext has been destroyed. -*/ -QmlContext *QmlExpression::context() const -{ - return d->ctxt; -} - -/*! - Returns the expression string. -*/ -QString QmlExpression::expression() const -{ - if (d->sse.isValid()) - return QLatin1String(d->sse.expression()); - else - return d->expression; -} - -/*! - Clear the expression. -*/ -void QmlExpression::clearExpression() -{ - setExpression(QString()); -} - -/*! - Set the expression to \a expression. -*/ -void QmlExpression::setExpression(const QString &expression) -{ - if (d->sseData) { - d->sse.deleteScriptState(d->sseData); - d->sseData = 0; - } - - delete d->proxy; d->proxy = 0; - - d->expression = expression; - - d->sse.clear(); -} - -/*! - Called by QmlExpression each time the expression value changes from the - last time it was evaluated. The expression must have been evaluated at - least once (by calling QmlExpression::value()) before this callback will - be made. - - The default implementation does nothing. -*/ -void QmlExpression::valueChanged() -{ -} - -void BindExpressionProxy::changed() -{ - e->valueChanged(); -} - -/*! - Returns the value of the expression, or an invalid QVariant if the - expression is invalid or has an error. -*/ -QVariant QmlExpression::value() -{ - QVariant rv; - if (!d->ctxt || !engine() || (!d->sse.isValid() && d->expression.isEmpty())) - return rv; - -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxPerfTimer perf; -#endif - - QmlBasicScript::CacheState cacheState = QmlBasicScript::Reset; - - QmlEnginePrivate *ep = engine()->d_func(); - QmlExpression *lastCurrentExpression = ep->currentExpression; - ep->currentExpression = this; - if (d->sse.isValid()) { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxPerfTimer perfsse; -#endif - - context()->d_func()->defaultObjects.insert(context()->d_func()->highPriorityCount, d->me); - - if (!d->sseData) - d->sseData = d->sse.newScriptState(); - rv = d->sse.run(context(), d->sseData, &cacheState); - - context()->d_func()->defaultObjects.removeAt(context()->d_func()->highPriorityCount); - } else { -#ifdef Q_ENABLE_PERFORMANCE_LOG - QFxPerfTimer perfqt; -#endif - context()->d_func()->defaultObjects.insert(context()->d_func()->highPriorityCount, d->me); - - QScriptEngine *scriptEngine = engine()->scriptEngine(); - QScriptValueList oldScopeChain = scriptEngine->currentContext()->scopeChain(); - for (int i = 0; i < oldScopeChain.size(); ++i) { - scriptEngine->currentContext()->popScope(); - } - for (int i = context()->d_func()->scopeChain.size() - 1; i > -1; --i) { - scriptEngine->currentContext()->pushScope(context()->d_func()->scopeChain.at(i)); - } - QScriptValue svalue = scriptEngine->evaluate(expression(), d->fileName.toString(), d->line); - if (scriptEngine->hasUncaughtException()) { - if (scriptEngine->uncaughtException().isError()){ - QScriptValue exception = scriptEngine->uncaughtException(); - if (!exception.property(QLatin1String("fileName")).toString().isEmpty()){ - qWarning() << exception.property(QLatin1String("fileName")).toString() - << scriptEngine->uncaughtExceptionLineNumber() - << exception.toString(); - - } else { - qWarning() << exception.toString(); - } - } - } - - context()->d_func()->defaultObjects.removeAt(context()->d_func()->highPriorityCount); - if (svalue.isArray()) { - int length = svalue.property(QLatin1String("length")).toInt32(); - if (length && svalue.property(0).isObject()) { - QList list; - for (int ii = 0; ii < length; ++ii) { - QScriptValue arrayItem = svalue.property(ii); - QObject *d = qvariant_cast(arrayItem.data().toVariant()); - if (d) { - list << d; - } else { - list << 0; - } - } - rv = QVariant::fromValue(list); - } - } else if (svalue.isObject() && - !svalue.isNumber() && - !svalue.isString() && - !svalue.isDate() && - !svalue.isError() && - !svalue.isFunction() && - !svalue.isNull() && - !svalue.isQMetaObject() && - !svalue.isQObject() && - !svalue.isRegExp()) { - QScriptValue objValue = svalue.data(); - if (objValue.isValid()) { - QVariant var = objValue.toVariant(); - if (var.userType() >= (int)QVariant::UserType && - QmlMetaType::isObject(var.userType())) - rv = var; - } - } - if (rv.isNull()) - rv = svalue.toVariant(); - - for (int i = 0; i < context()->d_func()->scopeChain.size(); ++i) { - scriptEngine->currentContext()->popScope(); - } - for (int i = oldScopeChain.size() - 1; i > -1; --i) { - scriptEngine->currentContext()->pushScope(oldScopeChain.at(i)); - } - } - ep->currentExpression = lastCurrentExpression; - - if (cacheState != QmlBasicScript::NoChange) { - if (cacheState != QmlBasicScript::Incremental && d->proxy) { - delete d->proxy; - d->proxy = 0; - } - - if (trackChange() && ep->capturedProperties.count()) { - if (!d->proxy) - d->proxy = new BindExpressionProxy(this); - - static int changedIndex = -1; - if (changedIndex == -1) - changedIndex = BindExpressionProxy::staticMetaObject.indexOfSlot("changed()"); - - if(qmlDebugger()) { - QmlExpressionLog log; - log.setTime(engine()->d_func()->getUniqueId()); - log.setExpression(expression()); - log.setResult(rv); - - for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) { - const QmlEnginePrivate::CapturedProperty &prop = - ep->capturedProperties.at(ii); - - if (prop.notifyIndex != -1) { - QMetaObject::connect(prop.object, prop.notifyIndex, - d->proxy, changedIndex); - } else { - // ### FIXME - //QString warn = QLatin1String("Expression depends on property without a NOTIFY signal: [") + QLatin1String(prop.object->metaObject()->className()) + QLatin1String("].") + prop.name; - //log.addWarning(warn); - } - } - d->addLog(log); - - } else { - for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) { - const QmlEnginePrivate::CapturedProperty &prop = - ep->capturedProperties.at(ii); - - if (prop.notifyIndex != -1) - QMetaObject::connect(prop.object, prop.notifyIndex, - d->proxy, changedIndex); - } - } - } else { - QmlExpressionLog log; - log.setTime(engine()->d_func()->getUniqueId()); - log.setExpression(expression()); - log.setResult(rv); - d->addLog(log); - } - - } else { - if(qmlDebugger()) { - QmlExpressionLog log; - log.setTime(engine()->d_func()->getUniqueId()); - log.setExpression(expression()); - log.setResult(rv); - d->addLog(log); - } - } - - ep->capturedProperties.clear(); - - return rv; -} - -/*! - Returns true if the expression results in a constant value. - QmlExpression::value() must have been invoked at least once before the - return from this method is valid. - */ -bool QmlExpression::isConstant() const -{ - return d->proxy == 0; -} - -/*! - Returns true if the changes are tracked in the expression's value. -*/ -bool QmlExpression::trackChange() const -{ - return d->trackChange; -} - -/*! - Set whether changes are tracked in the expression's value to \a trackChange. - - If true, the QmlExpression will monitor properties involved in the - expression's evaluation, and call QmlExpression::valueChanged() if they have - changed. This allows an application to ensure that any value associated - with the result of the expression remains up to date. - - If false, the QmlExpression will not montitor properties involved in the - expression's evaluation, and QmlExpression::valueChanged() will never be - called. This is more efficient if an application wants a "one off" - evaluation of the expression. - - By default, trackChange is true. -*/ -void QmlExpression::setTrackChange(bool trackChange) -{ - d->trackChange = trackChange; -} - -/*! - Set the location of this expression to \a line of \a fileName. This information - is used by the script engine. -*/ -void QmlExpression::setSourceLocation(const QUrl &fileName, int line) -{ - d->fileName = fileName; - d->line = line; -} - -/*! - Returns the expression's scope object, if provided, otherwise 0. - - In addition to data provided by the expression's QmlContext, the scope - object's properties are also in scope during the expression's evaluation. -*/ -QObject *QmlExpression::scopeObject() const -{ - return d->me; -} - -/*! - \internal -*/ -quint32 QmlExpression::id() const -{ - return d->id; -} - -/*! - \class QmlExpression - \brief The QmlExpression class evaluates ECMAScript in a QML context. -*/ - -/*! - \class QmlExpressionObject - \brief The QmlExpressionObject class extends QmlExpression with signals and slots. - - To remain as lightweight as possible, QmlExpression does not inherit QObject - and consequently cannot use signals or slots. For the cases where this is - more convenient in an application, QmlExpressionObject can be used instead. - - QmlExpressionObject behaves identically to QmlExpression, except that the - QmlExpressionObject::value() method is a slot, and the - QmlExpressionObject::valueChanged() callback is a signal. -*/ -/*! - Create a QmlExpression with the specified \a parent. - - As the expression will not have an associated QmlContext, this will be a - null expression object and its value will always be an invalid QVariant. -*/ -QmlExpressionObject::QmlExpressionObject(QObject *parent) -: QObject(parent) -{ -} - -/*! - Create a QmlExpressionObject with the specified \a parent. - - The \a expression ECMAScript will be executed in the \a ctxt QmlContext. - If specified, the \a scope object's properties will also be in scope during - the expression's execution. -*/ -QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, const QString &expression, QObject *scope, QObject *parent) -: QObject(parent), QmlExpression(ctxt, expression, scope) -{ -} - -/*! \internal */ -QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, void *d, QmlRefCount *rc, QObject *me) -: QmlExpression(ctxt, d, rc, me) -{ -} - -/*! - Returns the value of the expression, or an invalid QVariant if the - expression is invalid or has an error. -*/ -QVariant QmlExpressionObject::value() -{ - return QmlExpression::value(); -} - -/*! - \fn void QmlExpressionObject::valueChanged() - - Emitted each time the expression value changes from the last time it was - evaluated. The expression must have been evaluated at least once (by - calling QmlExpressionObject::value()) before this signal will be emitted. -*/ - QmlScriptClass::QmlScriptClass(QmlEngine *bindengine) : QScriptClass(bindengine->scriptEngine()), engine(bindengine) { @@ -1628,76 +1177,4 @@ void QmlObjectScriptClass::setProperty(QScriptValue &object, scriptEngine->currentContext()->setActivationObject(oldact); } -void QmlExpressionPrivate::addLog(const QmlExpressionLog &l) -{ - if (!log) - log = new QList(); - log->append(l); -} - -QmlExpressionLog::QmlExpressionLog() -{ -} - -QmlExpressionLog::QmlExpressionLog(const QmlExpressionLog &o) -: m_time(o.m_time), - m_expression(o.m_expression), - m_result(o.m_result), - m_warnings(o.m_warnings) -{ -} - -QmlExpressionLog::~QmlExpressionLog() -{ -} - -QmlExpressionLog &QmlExpressionLog::operator=(const QmlExpressionLog &o) -{ - m_time = o.m_time; - m_expression = o.m_expression; - m_result = o.m_result; - m_warnings = o.m_warnings; - return *this; -} - -void QmlExpressionLog::setTime(quint32 time) -{ - m_time = time; -} - -quint32 QmlExpressionLog::time() const -{ - return m_time; -} - -QString QmlExpressionLog::expression() const -{ - return m_expression; -} - -void QmlExpressionLog::setExpression(const QString &e) -{ - m_expression = e; -} - -QStringList QmlExpressionLog::warnings() const -{ - return m_warnings; -} - -void QmlExpressionLog::addWarning(const QString &w) -{ - m_warnings << w; -} - -QVariant QmlExpressionLog::result() const -{ - return m_result; -} - -void QmlExpressionLog::setResult(const QVariant &r) -{ - m_result = r; -} - QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlengine.h b/src/declarative/qml/qmlengine.h index f114379..42359e1 100644 --- a/src/declarative/qml/qmlengine.h +++ b/src/declarative/qml/qmlengine.h @@ -104,6 +104,7 @@ private: friend class QmlContext; friend class QmlContextPrivate; friend class QmlExpression; + friend class QmlExpressionPrivate; friend class QmlBasicScript; friend class QmlVME; friend class QmlComponent; diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 93ae704..e91c4ee 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -169,23 +169,6 @@ public: } }; - -class BindExpressionProxy : public QObject -{ -Q_OBJECT -public: - BindExpressionProxy(QmlExpression *be) - :e(be) - { - } - -private: - QmlExpression *e; - -private Q_SLOTS: - void changed(); -}; - class QmlScriptClass : public QScriptClass { public: @@ -247,60 +230,6 @@ public: const QScriptValue &value); }; -class QmlExpressionLog -{ -public: - QmlExpressionLog(); - QmlExpressionLog(const QmlExpressionLog &); - ~QmlExpressionLog(); - - QmlExpressionLog &operator=(const QmlExpressionLog &); - - void setTime(quint32); - quint32 time() const; - - QString expression() const; - void setExpression(const QString &); - - QStringList warnings() const; - void addWarning(const QString &); - - QVariant result() const; - void setResult(const QVariant &); - -private: - quint32 m_time; - QString m_expression; - QVariant m_result; - QStringList m_warnings; -}; - -class QmlExpressionPrivate -{ -public: - QmlExpressionPrivate(QmlExpression *); - QmlExpressionPrivate(QmlExpression *, const QString &expr); - QmlExpressionPrivate(QmlExpression *, void *expr, QmlRefCount *rc); - ~QmlExpressionPrivate(); - - QmlExpression *q; - QmlContext *ctxt; - QString expression; - QmlBasicScript sse; - void *sseData; - BindExpressionProxy *proxy; - QObject *me; - bool trackChange; - - QUrl fileName; - int line; - - quint32 id; - - void addLog(const QmlExpressionLog &); - QList *log; -}; - QT_END_NAMESPACE #endif // QMLENGINE_P_H diff --git a/src/declarative/qml/qmlexpression.cpp b/src/declarative/qml/qmlexpression.cpp new file mode 100644 index 0000000..1e7b149 --- /dev/null +++ b/src/declarative/qml/qmlexpression.cpp @@ -0,0 +1,604 @@ +/**************************************************************************** +** +** 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 "qmlexpression.h" +#include "qmlexpression_p.h" +#include "qmlengine_p.h" +#include "qmlcontext_p.h" +#include "QtCore/qdebug.h" + +Q_DECLARE_METATYPE(QList); + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlDebugger, QML_DEBUGGER) + +QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b) +: q(b), ctxt(0), sseData(0), proxy(0), me(0), trackChange(false), line(-1), id(0), log(0) +{ +} + +QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, void *expr, QmlRefCount *rc) +: q(b), ctxt(0), sse((const char *)expr, rc), sseData(0), proxy(0), me(0), trackChange(true), line(-1), id(0), log(0) +{ +} + +QmlExpressionPrivate::QmlExpressionPrivate(QmlExpression *b, const QString &expr) +: q(b), ctxt(0), expression(expr), sseData(0), proxy(0), me(0), trackChange(true), line(-1), id(0), log(0) +{ +} + +QmlExpressionPrivate::~QmlExpressionPrivate() +{ + sse.deleteScriptState(sseData); + sseData = 0; + delete proxy; + delete log; +} + +/*! + Create an invalid QmlExpression. + + As the expression will not have an associated QmlContext, this will be a + null expression object and its value will always be an invalid QVariant. + */ +QmlExpression::QmlExpression() +: d(new QmlExpressionPrivate(this)) +{ +} + +/*! \internal */ +QmlExpression::QmlExpression(QmlContext *ctxt, void *expr, + QmlRefCount *rc, QObject *me) +: d(new QmlExpressionPrivate(this, expr, rc)) +{ + d->ctxt = ctxt; + if(ctxt && ctxt->engine()) + d->id = ctxt->engine()->d_func()->getUniqueId(); + if(ctxt) + ctxt->d_func()->childExpressions.insert(this); + d->me = me; +} + +/*! + Create a QmlExpression object. + + The \a expression ECMAScript will be executed in the \a ctxt QmlContext. + If specified, the \a scope object's properties will also be in scope during + the expression's execution. +*/ +QmlExpression::QmlExpression(QmlContext *ctxt, const QString &expression, + QObject *scope) +: d(new QmlExpressionPrivate(this, expression)) +{ + d->ctxt = ctxt; + if(ctxt && ctxt->engine()) + d->id = ctxt->engine()->d_func()->getUniqueId(); + if(ctxt) + ctxt->d_func()->childExpressions.insert(this); + d->me = scope; +} + +/*! + Destroy the QmlExpression instance. +*/ +QmlExpression::~QmlExpression() +{ + if (d->ctxt) + d->ctxt->d_func()->childExpressions.remove(this); + delete d; d = 0; +} + +/*! + Returns the QmlEngine this expression is associated with, or 0 if there + is no association or the QmlEngine has been destroyed. +*/ +QmlEngine *QmlExpression::engine() const +{ + return d->ctxt?d->ctxt->engine():0; +} + +/*! + Returns the QmlContext this expression is associated with, or 0 if there + is no association or the QmlContext has been destroyed. +*/ +QmlContext *QmlExpression::context() const +{ + return d->ctxt; +} + +/*! + Returns the expression string. +*/ +QString QmlExpression::expression() const +{ + if (d->sse.isValid()) + return QLatin1String(d->sse.expression()); + else + return d->expression; +} + +/*! + Clear the expression. +*/ +void QmlExpression::clearExpression() +{ + setExpression(QString()); +} + +/*! + Set the expression to \a expression. +*/ +void QmlExpression::setExpression(const QString &expression) +{ + if (d->sseData) { + d->sse.deleteScriptState(d->sseData); + d->sseData = 0; + } + + delete d->proxy; d->proxy = 0; + + d->expression = expression; + + d->sse.clear(); +} + +/*! + Called by QmlExpression each time the expression value changes from the + last time it was evaluated. The expression must have been evaluated at + least once (by calling QmlExpression::value()) before this callback will + be made. + + The default implementation does nothing. +*/ +void QmlExpression::valueChanged() +{ +} + +QVariant QmlExpressionPrivate::evalSSE(QmlBasicScript::CacheState &cacheState) +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer perfsse; +#endif + + QmlContextPrivate *ctxtPriv = ctxt->d_func(); + if (me) + ctxtPriv->defaultObjects.insert(ctxtPriv->highPriorityCount , me); + + if (!sseData) + sseData = sse.newScriptState(); + QVariant rv = sse.run(ctxt, sseData, &cacheState); + + if (me) + ctxtPriv->defaultObjects.removeAt(ctxtPriv->highPriorityCount); + + return rv; +} + +QVariant QmlExpressionPrivate::evalQtScript() +{ +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer perfqt; +#endif + + QmlContextPrivate *ctxtPriv = ctxt->d_func(); + QmlEngine *engine = ctxt->engine(); + + if (me) + ctxtPriv->defaultObjects.insert(ctxtPriv->highPriorityCount, me); + + QScriptEngine *scriptEngine = engine->scriptEngine(); + QScriptValueList oldScopeChain = + scriptEngine->currentContext()->scopeChain(); + + for (int i = 0; i < oldScopeChain.size(); ++i) + scriptEngine->currentContext()->popScope(); + for (int i = ctxtPriv->scopeChain.size() - 1; i > -1; --i) + scriptEngine->currentContext()->pushScope(ctxtPriv->scopeChain.at(i)); + + QScriptValue svalue = + scriptEngine->evaluate(expression, fileName.toString(), line); + + if (scriptEngine->hasUncaughtException()) { + if (scriptEngine->uncaughtException().isError()){ + QScriptValue exception = scriptEngine->uncaughtException(); + QLatin1String fileNameProp("fileName"); + if (!exception.property(fileNameProp).toString().isEmpty()){ + qWarning() << exception.property(fileNameProp).toString() + << scriptEngine->uncaughtExceptionLineNumber() + << exception.toString(); + } else { + qWarning() << exception.toString(); + } + } + } + + if (me) + ctxtPriv->defaultObjects.removeAt(ctxtPriv->highPriorityCount); + + QVariant rv; + + if (svalue.isArray()) { + int length = svalue.property(QLatin1String("length")).toInt32(); + if (length && svalue.property(0).isObject()) { + QList list; + for (int ii = 0; ii < length; ++ii) { + QScriptValue arrayItem = svalue.property(ii); + QObject *d = + qvariant_cast(arrayItem.data().toVariant()); + if (d) { + list << d; + } else { + list << 0; + } + } + rv = QVariant::fromValue(list); + } + } else if (svalue.isObject() && + !svalue.isNumber() && + !svalue.isString() && + !svalue.isDate() && + !svalue.isError() && + !svalue.isFunction() && + !svalue.isNull() && + !svalue.isQMetaObject() && + !svalue.isQObject() && + !svalue.isRegExp()) { + QScriptValue objValue = svalue.data(); + if (objValue.isValid()) { + QVariant var = objValue.toVariant(); + if (var.userType() >= (int)QVariant::UserType && + QmlMetaType::isObject(var.userType())) + rv = var; + } + } + if (rv.isNull()) + rv = svalue.toVariant(); + + for (int i = 0; i < ctxtPriv->scopeChain.size(); ++i) + scriptEngine->currentContext()->popScope(); + for (int i = oldScopeChain.size() - 1; i > -1; --i) + scriptEngine->currentContext()->pushScope(oldScopeChain.at(i)); + + return rv; +} + +/*! + Returns the value of the expression, or an invalid QVariant if the + expression is invalid or has an error. +*/ +QVariant QmlExpression::value() +{ + QVariant rv; + if (!d->ctxt || !engine() || (!d->sse.isValid() && d->expression.isEmpty())) + return rv; + +#ifdef Q_ENABLE_PERFORMANCE_LOG + QFxPerfTimer perf; +#endif + + QmlBasicScript::CacheState cacheState = QmlBasicScript::Reset; + + QmlEnginePrivate *ep = engine()->d_func(); + QmlExpression *lastCurrentExpression = ep->currentExpression; + ep->currentExpression = this; + + if (d->sse.isValid()) { + rv = d->evalSSE(cacheState); + } else { + rv = d->evalQtScript(); + } + + ep->currentExpression = lastCurrentExpression; + + if (cacheState != QmlBasicScript::NoChange) { + if (cacheState != QmlBasicScript::Incremental && d->proxy) { + delete d->proxy; + d->proxy = 0; + } + + if (trackChange() && ep->capturedProperties.count()) { + if (!d->proxy) + d->proxy = new QmlExpressionBindProxy(this); + + static int changedIndex = -1; + if (changedIndex == -1) + changedIndex = QmlExpressionBindProxy::staticMetaObject.indexOfSlot("changed()"); + + if(qmlDebugger()) { + QmlExpressionLog log; + log.setTime(engine()->d_func()->getUniqueId()); + log.setExpression(expression()); + log.setResult(rv); + + for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) { + const QmlEnginePrivate::CapturedProperty &prop = + ep->capturedProperties.at(ii); + + if (prop.notifyIndex != -1) { + QMetaObject::connect(prop.object, prop.notifyIndex, + d->proxy, changedIndex); + } else { + // ### FIXME + //QString warn = QLatin1String("Expression depends on property without a NOTIFY signal: [") + QLatin1String(prop.object->metaObject()->className()) + QLatin1String("].") + prop.name; + //log.addWarning(warn); + } + } + d->addLog(log); + + } else { + for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) { + const QmlEnginePrivate::CapturedProperty &prop = + ep->capturedProperties.at(ii); + + if (prop.notifyIndex != -1) + QMetaObject::connect(prop.object, prop.notifyIndex, + d->proxy, changedIndex); + } + } + } else { + QmlExpressionLog log; + log.setTime(engine()->d_func()->getUniqueId()); + log.setExpression(expression()); + log.setResult(rv); + d->addLog(log); + } + + } else { + if(qmlDebugger()) { + QmlExpressionLog log; + log.setTime(engine()->d_func()->getUniqueId()); + log.setExpression(expression()); + log.setResult(rv); + d->addLog(log); + } + } + + ep->capturedProperties.clear(); + + return rv; +} + +/*! + Returns true if the expression results in a constant value. + QmlExpression::value() must have been invoked at least once before the + return from this method is valid. + */ +bool QmlExpression::isConstant() const +{ + return d->proxy == 0; +} + +/*! + Returns true if the changes are tracked in the expression's value. +*/ +bool QmlExpression::trackChange() const +{ + return d->trackChange; +} + +/*! + Set whether changes are tracked in the expression's value to \a trackChange. + + If true, the QmlExpression will monitor properties involved in the + expression's evaluation, and call QmlExpression::valueChanged() if they have + changed. This allows an application to ensure that any value associated + with the result of the expression remains up to date. + + If false, the QmlExpression will not montitor properties involved in the + expression's evaluation, and QmlExpression::valueChanged() will never be + called. This is more efficient if an application wants a "one off" + evaluation of the expression. + + By default, trackChange is true. +*/ +void QmlExpression::setTrackChange(bool trackChange) +{ + d->trackChange = trackChange; +} + +/*! + Set the location of this expression to \a line of \a fileName. This information + is used by the script engine. +*/ +void QmlExpression::setSourceLocation(const QUrl &fileName, int line) +{ + d->fileName = fileName; + d->line = line; +} + +/*! + Returns the expression's scope object, if provided, otherwise 0. + + In addition to data provided by the expression's QmlContext, the scope + object's properties are also in scope during the expression's evaluation. +*/ +QObject *QmlExpression::scopeObject() const +{ + return d->me; +} + +/*! + \internal +*/ +quint32 QmlExpression::id() const +{ + return d->id; +} + +/*! + \class QmlExpression + \brief The QmlExpression class evaluates ECMAScript in a QML context. +*/ + +/*! + \class QmlExpressionObject + \brief The QmlExpressionObject class extends QmlExpression with signals and slots. + + To remain as lightweight as possible, QmlExpression does not inherit QObject + and consequently cannot use signals or slots. For the cases where this is + more convenient in an application, QmlExpressionObject can be used instead. + + QmlExpressionObject behaves identically to QmlExpression, except that the + QmlExpressionObject::value() method is a slot, and the + QmlExpressionObject::valueChanged() callback is a signal. +*/ +/*! + Create a QmlExpression with the specified \a parent. + + As the expression will not have an associated QmlContext, this will be a + null expression object and its value will always be an invalid QVariant. +*/ +QmlExpressionObject::QmlExpressionObject(QObject *parent) +: QObject(parent) +{ +} + +/*! + Create a QmlExpressionObject with the specified \a parent. + + The \a expression ECMAScript will be executed in the \a ctxt QmlContext. + If specified, the \a scope object's properties will also be in scope during + the expression's execution. +*/ +QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, const QString &expression, QObject *scope, QObject *parent) +: QObject(parent), QmlExpression(ctxt, expression, scope) +{ +} + +/*! \internal */ +QmlExpressionObject::QmlExpressionObject(QmlContext *ctxt, void *d, QmlRefCount *rc, QObject *me) +: QmlExpression(ctxt, d, rc, me) +{ +} + +/*! + Returns the value of the expression, or an invalid QVariant if the + expression is invalid or has an error. +*/ +QVariant QmlExpressionObject::value() +{ + return QmlExpression::value(); +} + +/*! + \fn void QmlExpressionObject::valueChanged() + + Emitted each time the expression value changes from the last time it was + evaluated. The expression must have been evaluated at least once (by + calling QmlExpressionObject::value()) before this signal will be emitted. +*/ + +void QmlExpressionPrivate::addLog(const QmlExpressionLog &l) +{ + if (!log) + log = new QList(); + log->append(l); +} + +QmlExpressionLog::QmlExpressionLog() +{ +} + +QmlExpressionLog::QmlExpressionLog(const QmlExpressionLog &o) +: m_time(o.m_time), + m_expression(o.m_expression), + m_result(o.m_result), + m_warnings(o.m_warnings) +{ +} + +QmlExpressionLog::~QmlExpressionLog() +{ +} + +QmlExpressionLog &QmlExpressionLog::operator=(const QmlExpressionLog &o) +{ + m_time = o.m_time; + m_expression = o.m_expression; + m_result = o.m_result; + m_warnings = o.m_warnings; + return *this; +} + +void QmlExpressionLog::setTime(quint32 time) +{ + m_time = time; +} + +quint32 QmlExpressionLog::time() const +{ + return m_time; +} + +QString QmlExpressionLog::expression() const +{ + return m_expression; +} + +void QmlExpressionLog::setExpression(const QString &e) +{ + m_expression = e; +} + +QStringList QmlExpressionLog::warnings() const +{ + return m_warnings; +} + +void QmlExpressionLog::addWarning(const QString &w) +{ + m_warnings << w; +} + +QVariant QmlExpressionLog::result() const +{ + return m_result; +} + +void QmlExpressionLog::setResult(const QVariant &r) +{ + m_result = r; +} + + +QT_END_NAMESPACE + diff --git a/src/declarative/qml/qmlexpression.h b/src/declarative/qml/qmlexpression.h index e8cac7a..3d8f8df 100644 --- a/src/declarative/qml/qmlexpression.h +++ b/src/declarative/qml/qmlexpression.h @@ -86,7 +86,7 @@ protected: virtual void valueChanged(); private: - friend class BindExpressionProxy; + friend class QmlExpressionBindProxy; friend class QmlDebugger; friend class QmlContext; QmlExpressionPrivate *d; diff --git a/src/declarative/qml/qmlexpression_p.h b/src/declarative/qml/qmlexpression_p.h new file mode 100644 index 0000000..5883125 --- /dev/null +++ b/src/declarative/qml/qmlexpression_p.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** 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 QMLEXPRESSION_P_H +#define QMLEXPRESSION_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 "qmlbasicscript_p.h" +#include "qmlexpression.h" + +QT_BEGIN_NAMESPACE + +class QmlExpression; +class QString; +class QmlExpressionLog; +class QmlExpressionBindProxy; +class QmlExpressionPrivate +{ +public: + QmlExpressionPrivate(QmlExpression *); + QmlExpressionPrivate(QmlExpression *, const QString &expr); + QmlExpressionPrivate(QmlExpression *, void *expr, QmlRefCount *rc); + ~QmlExpressionPrivate(); + + QmlExpression *q; + QmlContext *ctxt; + QString expression; + QmlBasicScript sse; + void *sseData; + QmlExpressionBindProxy *proxy; + QObject *me; + bool trackChange; + + QUrl fileName; + int line; + + quint32 id; + + void addLog(const QmlExpressionLog &); + QList *log; + + QVariant evalSSE(QmlBasicScript::CacheState &cacheState); + QVariant evalQtScript(); +}; + +class QmlExpressionBindProxy : public QObject +{ +Q_OBJECT +public: + QmlExpressionBindProxy(QmlExpression *be) + :e(be) { } + +private: + QmlExpression *e; + +private Q_SLOTS: + void changed() { e->valueChanged(); } +}; + +class QmlExpressionLog +{ +public: + QmlExpressionLog(); + QmlExpressionLog(const QmlExpressionLog &); + ~QmlExpressionLog(); + + QmlExpressionLog &operator=(const QmlExpressionLog &); + + void setTime(quint32); + quint32 time() const; + + QString expression() const; + void setExpression(const QString &); + + QStringList warnings() const; + void addWarning(const QString &); + + QVariant result() const; + void setResult(const QVariant &); + +private: + quint32 m_time; + QString m_expression; + QVariant m_result; + QStringList m_warnings; +}; + +QT_END_NAMESPACE + +#endif // QMLEXPRESSION_P_H -- cgit v0.12 From 6dee715bdc2ff49292f3711446b3616731d681ad Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 3 Jul 2009 11:51:30 +1000 Subject: Add CONSTANT attribute to Q_PROPERTY() This will be used by the declarative module to determine if a property lacking a NOTIFY signal is truly constant, or just missing a NOTIFY signal. --- doc/src/properties.qdoc | 6 ++++++ doc/src/snippets/code/doc_src_properties.qdoc | 3 ++- src/corelib/kernel/qmetaobject.cpp | 17 ++++++++++++++++- src/corelib/kernel/qmetaobject.h | 1 + src/tools/moc/generator.cpp | 6 +++++- src/tools/moc/moc.cpp | 23 +++++++++++++++++++++++ src/tools/moc/moc.h | 3 ++- tests/auto/qmetaobject/tst_qmetaobject.cpp | 17 +++++++++++++++++ 8 files changed, 72 insertions(+), 4 deletions(-) diff --git a/doc/src/properties.qdoc b/doc/src/properties.qdoc index 5490dd0..cac5016 100644 --- a/doc/src/properties.qdoc +++ b/doc/src/properties.qdoc @@ -122,6 +122,12 @@ editable property for (checkable) buttons. Note that QItemDelegate gets and sets a widget's \c USER property. + \o The presence of the \c CONSTANT attibute indicates that the property + value is constant. For a given object instance, the READ method of a + constant property must return the same value every time it is called. This + constant value may be different for different instances of the object. A + constant property cannot have a WRTE method or a NOTIFY signal. + \endlist The \c READ, \c WRITE, and \c RESET functions can be inherited. diff --git a/doc/src/snippets/code/doc_src_properties.qdoc b/doc/src/snippets/code/doc_src_properties.qdoc index 377cc9c..64e5377 100644 --- a/doc/src/snippets/code/doc_src_properties.qdoc +++ b/doc/src/snippets/code/doc_src_properties.qdoc @@ -7,7 +7,8 @@ Q_PROPERTY(type name [DESIGNABLE bool] [SCRIPTABLE bool] [STORED bool] - [USER bool]) + [USER bool] + [CONSTANT]) //! [0] diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 690587c..ebfe05b 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -160,7 +160,8 @@ enum PropertyFlags { User = 0x00100000, ResolveUser = 0x00200000, Notify = 0x00400000, - Dynamic = 0x00800000 + Dynamic = 0x00800000, + Constant = 0x00000400 }; enum MethodFlags { @@ -2479,6 +2480,20 @@ bool QMetaProperty::isDynamic() const } /*! + Returns true if the property is constant; otherwise returns false. + + A property is constant if the \c{Q_PROPERTY()}'s \c CONSTANT attribute + is set. +*/ +bool QMetaProperty::isConstant() const +{ + if (!mobj) + return false; + int flags = mobj->d.data[handle + 2]; + return flags & Constant; +} + +/*! \obsolete Returns true if the property is editable for the given \a object; diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index 7e16a05..adc31d1 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -190,6 +190,7 @@ public: bool isEditable(const QObject *obj = 0) const; bool isUser(const QObject *obj = 0) const; bool isDynamic() const; + bool isConstant() const; bool isFlagType() const; bool isEnumType() const; diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 12da981..328717c 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -67,7 +67,8 @@ enum PropertyFlags { User = 0x00100000, ResolveUser = 0x00200000, Notify = 0x00400000, - Dynamic = 0x00800000 + Dynamic = 0x00800000, + Constant = 0x00000400 }; enum MethodFlags { AccessPrivate = 0x00, @@ -601,6 +602,9 @@ void Generator::generateProperties() if (p.notifyId != -1) flags |= Notify; + if (p.constant) + flags |= Constant; + fprintf(out, " %4d, %4d, ", strreg(p.name), strreg(p.type)); diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index bc971fc..7ad67c9 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -913,6 +913,12 @@ void Moc::parseProperty(ClassDef *def) propDef.name = lexem(); while (test(IDENTIFIER)) { QByteArray l = lexem(); + + if (l[0] == 'C' && l == "CONSTANT") { + propDef.constant = true; + continue; + } + QByteArray v, v2; if (test(LPAREN)) { v = lexemUntil(RPAREN); @@ -968,6 +974,23 @@ void Moc::parseProperty(ClassDef *def) msg += " has no READ accessor function. The property will be invalid."; warning(msg.constData()); } + if (propDef.constant && !propDef.write.isNull()) { + QByteArray msg; + msg += "Property declaration "; + msg += propDef.name; + msg += " is both WRITEable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } + if (propDef.constant && !propDef.notify.isNull()) { + QByteArray msg; + msg += "Property declaration "; + msg += propDef.name; + msg += " is both NOTIFYable and CONSTANT. CONSTANT will be ignored."; + propDef.constant = false; + warning(msg.constData()); + } + if(!propDef.notify.isEmpty()) def->notifyableProperties++; diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index 1c58204..f459032 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -115,9 +115,10 @@ struct FunctionDef struct PropertyDef { - PropertyDef():notifyId(-1), gspec(ValueSpec){} + PropertyDef():notifyId(-1), constant(false), gspec(ValueSpec){} QByteArray name, type, read, write, reset, designable, scriptable, editable, stored, user, notify; int notifyId; + bool constant; enum Specification { ValueSpec, ReferenceSpec, PointerSpec }; Specification gspec; bool stdCppSet() const { diff --git a/tests/auto/qmetaobject/tst_qmetaobject.cpp b/tests/auto/qmetaobject/tst_qmetaobject.cpp index dea0ffb..f4cff2b 100644 --- a/tests/auto/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/qmetaobject/tst_qmetaobject.cpp @@ -108,6 +108,7 @@ class tst_QMetaObject : public QObject Q_PROPERTY(int value6 READ value6 NOTIFY value6Changed) Q_PROPERTY(MyStruct value7 READ value7 WRITE setVal7 NOTIFY value7Changed) Q_PROPERTY(int value8 READ value8 NOTIFY value8Changed) + Q_PROPERTY(int value9 READ value9 CONSTANT) public: enum EnumType { EnumType1 }; @@ -137,6 +138,8 @@ public: int value8() const { return 1; } + int value9() const { return 1; } + QList value4; QVariantList value5; @@ -159,6 +162,7 @@ private slots: void customPropertyType(); void checkScope(); void propertyNotify(); + void propertyConstant(); void stdSet(); void classInfo(); @@ -785,6 +789,19 @@ void tst_QMetaObject::propertyNotify() QCOMPARE(signal.signature(), (const char *)0); } +void tst_QMetaObject::propertyConstant() +{ + const QMetaObject *mo = metaObject(); + + QMetaProperty prop = mo->property(mo->indexOfProperty("value8")); + QVERIFY(prop.isValid()); + QVERIFY(!prop.isConstant()); + + prop = mo->property(mo->indexOfProperty("value9")); + QVERIFY(prop.isValid()); + QVERIFY(prop.isConstant()); +} + class ClassInfoTestObjectA : public QObject { Q_OBJECT -- cgit v0.12 From e514c8c498bee83af9e2c688193fe7701e461617 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 3 Jul 2009 13:00:50 +1000 Subject: Warn annoyingly on non-notifyable properties --- src/declarative/fx/qfxtext.h | 2 +- src/declarative/qml/qmlbasicscript.cpp | 32 ++++++++++++++++++++++---------- src/declarative/qml/qmlengine.cpp | 6 +++--- src/declarative/qml/qmlengine_p.h | 5 +++-- src/declarative/qml/qmlexpression.cpp | 28 ++++++++++++++++++++++++---- src/declarative/qml/qmlmetaproperty.cpp | 3 ++- 6 files changed, 55 insertions(+), 21 deletions(-) diff --git a/src/declarative/fx/qfxtext.h b/src/declarative/fx/qfxtext.h index 104d18c..ee38a94 100644 --- a/src/declarative/fx/qfxtext.h +++ b/src/declarative/fx/qfxtext.h @@ -60,7 +60,7 @@ class Q_DECLARATIVE_EXPORT QFxText : public QFxItem Q_ENUMS(TextStyle) Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) - Q_PROPERTY(QmlFont *font READ font) + Q_PROPERTY(QmlFont *font READ font CONSTANT) Q_PROPERTY(QColor color READ color WRITE setColor) Q_PROPERTY(TextStyle style READ style WRITE setStyle) Q_PROPERTY(QColor styleColor READ styleColor WRITE setStyleColor) diff --git a/src/declarative/qml/qmlbasicscript.cpp b/src/declarative/qml/qmlbasicscript.cpp index 0cfb587..478491f 100644 --- a/src/declarative/qml/qmlbasicscript.cpp +++ b/src/declarative/qml/qmlbasicscript.cpp @@ -603,7 +603,10 @@ bool QmlBasicScriptCompiler::parseName(AST::Node *node, instr.type = ScriptInstruction::FetchD0Constant; instr.constant.idx = d0Idx; QMetaProperty prop = context->metaObject()->property(d0Idx); - instr.constant.notify = prop.notifySignalIndex(); + if (prop.isConstant()) + instr.constant.notify = 0; + else + instr.constant.notify = prop.notifySignalIndex(); instr.constant.type = prop.userType(); } else if (d1Idx != -1) { @@ -611,7 +614,10 @@ bool QmlBasicScriptCompiler::parseName(AST::Node *node, instr.type = ScriptInstruction::FetchD1Constant; instr.constant.idx = d1Idx; QMetaProperty prop = component->metaObject()->property(d1Idx); - instr.constant.notify = prop.notifySignalIndex(); + if (prop.isConstant()) + instr.constant.notify = 0; + else + instr.constant.notify = prop.notifySignalIndex(); instr.constant.type = prop.userType(); } else { @@ -635,7 +641,10 @@ bool QmlBasicScriptCompiler::parseName(AST::Node *node, instr.type = ScriptInstruction::FetchConstant; instr.constant.idx = idx; QMetaProperty prop = loadedType->metaObject()->property(idx); - instr.constant.notify = prop.notifySignalIndex(); + if (prop.isConstant()) + instr.constant.notify = 0; + else + instr.constant.notify = prop.notifySignalIndex(); instr.constant.type = prop.userType(); } else { int nref = data.count(); @@ -804,7 +813,7 @@ QVariant QmlBasicScript::run(QmlContext *context, void *voidCache, CacheState *c { stack.push(contextPrivate->propertyValues.at(instr.fetch.idx)); enginePrivate->capturedProperties << - QmlEnginePrivate::CapturedProperty(context, contextPrivate->notifyIndex + instr.fetch.idx); + QmlEnginePrivate::CapturedProperty(context, -1, contextPrivate->notifyIndex + instr.fetch.idx); state = Reset; } break; @@ -814,8 +823,9 @@ QVariant QmlBasicScript::run(QmlContext *context, void *voidCache, CacheState *c QObject *obj = contextPrivate->defaultObjects.at(0); stack.push(fetch_value(obj, instr.constant.idx, instr.constant.type)); - enginePrivate->capturedProperties << - QmlEnginePrivate::CapturedProperty(obj, instr.constant.notify); + if (instr.constant.notify != 0) + enginePrivate->capturedProperties << + QmlEnginePrivate::CapturedProperty(obj, instr.constant.idx, instr.constant.notify); state = Reset; } break; @@ -825,8 +835,9 @@ QVariant QmlBasicScript::run(QmlContext *context, void *voidCache, CacheState *c QObject *obj = contextPrivate->defaultObjects.at(1); stack.push(fetch_value(obj, instr.constant.idx, instr.constant.type)); - enginePrivate->capturedProperties << - QmlEnginePrivate::CapturedProperty(obj, instr.constant.notify); + if (instr.constant.notify != 0) + enginePrivate->capturedProperties << + QmlEnginePrivate::CapturedProperty(obj, instr.constant.idx, instr.constant.notify); state = Reset; } break; @@ -837,8 +848,9 @@ QVariant QmlBasicScript::run(QmlContext *context, void *voidCache, CacheState *c QObject *obj = qvariant_cast(o); stack.push(fetch_value(obj, instr.constant.idx, instr.constant.type)); - enginePrivate->capturedProperties << - QmlEnginePrivate::CapturedProperty(obj, instr.constant.notify); + if (instr.constant.notify != 0) + enginePrivate->capturedProperties << + QmlEnginePrivate::CapturedProperty(obj, instr.constant.idx, instr.constant.notify); state = Reset; } break; diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index f26e389..9eb169e 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -208,7 +208,7 @@ QmlContext *QmlEnginePrivate::setCurrentBindContext(QmlContext *c) } QmlEnginePrivate::CapturedProperty::CapturedProperty(const QmlMetaProperty &p) -: object(p.object()), notifyIndex(p.property().notifySignalIndex()) +: object(p.object()), coreIndex(p.coreIndex()), notifyIndex(p.property().notifySignalIndex()) { } @@ -357,7 +357,7 @@ bool QmlEnginePrivate::loadCache(QmlBasicScriptNodeCache &cache, const QString & cache.type = QmlBasicScriptNodeCache::Variant; cache.context = context; cache.contextIndex = *iter; - capturedProperties << CapturedProperty(context->q_ptr, *iter + context->notifyIndex); + capturedProperties << CapturedProperty(context->q_ptr, -1, *iter + context->notifyIndex); return true; } @@ -1008,7 +1008,7 @@ QScriptValue QmlContextScriptClass::property(const QScriptValue &object, } else { rv = scriptEngine->newVariant(value); } - engine->d_func()->capturedProperties << QmlEnginePrivate::CapturedProperty(bindContext, index + bindContext->d_func()->notifyIndex); + engine->d_func()->capturedProperties << QmlEnginePrivate::CapturedProperty(bindContext, -1, index + bindContext->d_func()->notifyIndex); return rv; } default: diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index e91c4ee..25f6edf 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -103,11 +103,12 @@ public: QScriptValue propertyObject(const QScriptString &propName, QObject *, uint id = 0); struct CapturedProperty { - CapturedProperty(QObject *o, int n) - : object(o), notifyIndex(n) {} + CapturedProperty(QObject *o, int c, int n) + : object(o), coreIndex(c), notifyIndex(n) {} CapturedProperty(const QmlMetaProperty &); QObject *object; + int coreIndex; int notifyIndex; }; QPODVector capturedProperties; diff --git a/src/declarative/qml/qmlexpression.cpp b/src/declarative/qml/qmlexpression.cpp index 1e7b149..84352b8 100644 --- a/src/declarative/qml/qmlexpression.cpp +++ b/src/declarative/qml/qmlexpression.cpp @@ -357,21 +357,41 @@ QVariant QmlExpression::value() QMetaObject::connect(prop.object, prop.notifyIndex, d->proxy, changedIndex); } else { - // ### FIXME - //QString warn = QLatin1String("Expression depends on property without a NOTIFY signal: [") + QLatin1String(prop.object->metaObject()->className()) + QLatin1String("].") + prop.name; - //log.addWarning(warn); + const QMetaObject *metaObj = prop.object->metaObject(); + QMetaProperty metaProp = + metaObj->property(prop.coreIndex); + + QString warn = QLatin1String("Expression depends on non-NOTIFYable property: ") + + QLatin1String(metaObj->className()) + + QLatin1String("::") + + QLatin1String(metaProp.name()); + log.addWarning(warn); } } d->addLog(log); } else { + bool outputWarningHeader = false; for (int ii = 0; ii < ep->capturedProperties.count(); ++ii) { const QmlEnginePrivate::CapturedProperty &prop = ep->capturedProperties.at(ii); - if (prop.notifyIndex != -1) + if (prop.notifyIndex != -1) { QMetaObject::connect(prop.object, prop.notifyIndex, d->proxy, changedIndex); + } else { + if (!outputWarningHeader) { + outputWarningHeader = true; + qWarning() << "QmlExpression: Expression" << expression() << "depends on non-NOTIFYable properties:"; + } + + const QMetaObject *metaObj = prop.object->metaObject(); + QMetaProperty metaProp = + metaObj->property(prop.coreIndex); + + qWarning().nospace() << " " << metaObj->className() + << "::" << metaProp.name(); + } } } } else { diff --git a/src/declarative/qml/qmlmetaproperty.cpp b/src/declarative/qml/qmlmetaproperty.cpp index 90acd72..ee24074 100644 --- a/src/declarative/qml/qmlmetaproperty.cpp +++ b/src/declarative/qml/qmlmetaproperty.cpp @@ -882,7 +882,8 @@ bool QmlMetaProperty::hasChangedNotifier() const */ bool QmlMetaProperty::needsChangedNotifier() const { - return type() & Property && !(type() & Attached); + return type() & Property && !(type() & Attached) && + !property().isConstant(); } /*! -- cgit v0.12 From 3320e130954229d513be62f7d6a6410ca5e5f523 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 3 Jul 2009 13:06:48 +1000 Subject: Add CONSTANT attribute where applicable --- src/declarative/fx/qfxitem.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/declarative/fx/qfxitem.h b/src/declarative/fx/qfxitem.h index 2b45f73..5fb4eff 100644 --- a/src/declarative/fx/qfxitem.h +++ b/src/declarative/fx/qfxitem.h @@ -103,9 +103,9 @@ class Q_DECLARATIVE_EXPORT QFxItem : public QSimpleCanvasItem, public QmlParserS Q_PROPERTY(QString id READ id WRITE setId) Q_PROPERTY(QmlList* children READ children DESIGNABLE false) Q_PROPERTY(QmlList* resources READ resources DESIGNABLE false) - Q_PROPERTY(QFxAnchors * anchors READ anchors DESIGNABLE false) + Q_PROPERTY(QFxAnchors * anchors READ anchors DESIGNABLE false CONSTANT) Q_PROPERTY(QmlList *data READ data DESIGNABLE false) - Q_PROPERTY(QFxContents * contents READ contents DESIGNABLE false) + Q_PROPERTY(QFxContents * contents READ contents DESIGNABLE false CONSTANT) Q_PROPERTY(QmlList* states READ states DESIGNABLE false) Q_PROPERTY(QmlList* transitions READ transitions DESIGNABLE false) Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) @@ -116,13 +116,13 @@ class Q_DECLARATIVE_EXPORT QFxItem : public QSimpleCanvasItem, public QmlParserS Q_PROPERTY(qreal z READ z WRITE setZ) Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged) Q_PROPERTY(qreal height READ height WRITE setHeight NOTIFY heightChanged) - Q_PROPERTY(QFxAnchorLine left READ left) - Q_PROPERTY(QFxAnchorLine right READ right) - Q_PROPERTY(QFxAnchorLine horizontalCenter READ horizontalCenter) - Q_PROPERTY(QFxAnchorLine top READ top) - Q_PROPERTY(QFxAnchorLine bottom READ bottom) - Q_PROPERTY(QFxAnchorLine verticalCenter READ verticalCenter) - Q_PROPERTY(QFxAnchorLine baseline READ baseline) + Q_PROPERTY(QFxAnchorLine left READ left CONSTANT) + Q_PROPERTY(QFxAnchorLine right READ right CONSTANT) + Q_PROPERTY(QFxAnchorLine horizontalCenter READ horizontalCenter CONSTANT) + Q_PROPERTY(QFxAnchorLine top READ top CONSTANT) + Q_PROPERTY(QFxAnchorLine bottom READ bottom CONSTANT) + Q_PROPERTY(QFxAnchorLine verticalCenter READ verticalCenter CONSTANT) + Q_PROPERTY(QFxAnchorLine baseline READ baseline CONSTANT) Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged) Q_PROPERTY(bool flipVertically READ flipVertically WRITE setFlipVertically) Q_PROPERTY(bool flipHorizontally READ flipHorizontally WRITE setFlipHorizontally) -- cgit v0.12 From 2283f0d39dfc0ffde1ac5fdb2928a534e164bef3 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 3 Jul 2009 15:34:22 +1000 Subject: Doc --- doc/src/declarative/components.qdoc | 92 ---------------------------------- doc/src/declarative/qmlforcpp.qdoc | 55 -------------------- doc/src/declarative/qtdeclarative.qdoc | 4 +- 3 files changed, 2 insertions(+), 149 deletions(-) delete mode 100644 doc/src/declarative/components.qdoc diff --git a/doc/src/declarative/components.qdoc b/doc/src/declarative/components.qdoc deleted file mode 100644 index d7a4ba6..0000000 --- a/doc/src/declarative/components.qdoc +++ /dev/null @@ -1,92 +0,0 @@ -/*! -\page components.html -\target components -\title Components - -A \bold component is a reusable, encapsulated Qml element with a well-defined interface. - -Writing and using components allows you to: -\list -\o Reuse sections of Qml without copy-and-paste. -\o Have consistent Look and Feel between different parts of your UI. -\o Create new Qml elements without writing a new C++ class. (See \l {cppitem}{Creating Qml elements in C++}) -\endlist - -Components are placed in \e .qml files, allowing \e to then be used as a tag -elsewhere. For example, if you have a Slider.qml file, you can then use \c {Slider { ... }} to -make a slider, just as if it was a built-in type. - -Components may be collected into \l {qmlmodules}{modules}. - -\section1 Example: Creating a MyButton Component - -This example describes how to create a component from an existing snippet of Qml. - -Assume you have an existing UI with a single 'Save' button, defined as follows: - -\code -Image { - source: "pics/button-background.png" - Text { - text: "Save" - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - } - MouseRegion { - anchors.fill: parent - onClick: { saveData() } - } -} -\endcode - -For the next release, you plan to add 'Cancel' and 'Reset' buttons. Rather than copying and pasting the above markup, you can create a component: - -\list 1 -\o Create a file called MyButton.qml, and copy the relevant Qml snippet into that file. -\o Make some minor changes to define the component's interface: - -\code -Image { - property string label - signal clicked - source: "pics/button-background.png" - Text { - text: parent.label - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - } - MouseRegion { - anchors.fill: parent - onClick: { parent.click.emit() } - } -} -\endcode - -The \a label property and \a click signal that were added effectively become part of the 'public API' of the MyButton component. In a similar manner, the Text and MouseRegion elements become invisible to anyone using the component. Note that the Text element now binds in its data from \a label, and the MouseRegion emits a generic signal. - -\o The component can now be used elsewhere as MyButton: - -\code -MyButton { label: "Save"; onClicked: saveData() } -... -MyButton { label: "Cancel"; onClicked: cancelData() } -... -MyButton { label: "Reset"; onClicked: resetData() } -\endcode - -\endlist - -\section1 Placing .qml Files - -When one component refers to a another, the second must be found either in the same directory -as the first, or in a directory imported using the \c import statement: - -\code -import "library" -\endcode - -\section1 Namespaces - -Namespaces for QML will be supported in Qt 4.6. - -*/ diff --git a/doc/src/declarative/qmlforcpp.qdoc b/doc/src/declarative/qmlforcpp.qdoc index c95cb71..38f5665 100644 --- a/doc/src/declarative/qmlforcpp.qdoc +++ b/doc/src/declarative/qmlforcpp.qdoc @@ -567,61 +567,6 @@ engine will automatically set the property as the target of the value source. - \section1 Extending types in QML - - QML is designed to allow you to build fully working types without writing - a line of C++ code. This is, for example, used extensively when designing - applications using the Fluid UI primitives. To create new types, it is - necessary to be able to define new signals, slots and properties in QML. - - In this example, a Button is extended to have an additional - "text2" property (which always returns "Hello world!") and an additional - signal "clicked2" that is also emitted when the button is clicked. Not - a very useful extension, but an extension nonetheless. - - \table - \row - \o - \code - QmlEngine engine; - QmlComponent component(&engine, qmlData); - QObject *object = component.create(); - // Will print "Hello world!" - qDebug() << object->property("text2"); - // Will be emitted whenever the button is clicked - QObject::connect(object, SIGNAL(clicked2()), this, SLOT(...)); - \endcode - \o - \code - Button { - property string text2 - signal clicked2 - - text: "Hello!" - text2: "Hello world!" - onClicked: clicked2.emit() - } - \endcode - \endtable - - The general syntax for defining new properties and signals is: - - \list - \o - \code - [default] property [: ] - \endcode - - Where type can be one of \e int, \e bool, \e double, \e real, \e string, - \e color, \e date, \e var or \e variant. - - \o - \code - signal - \endcode - Currently only parameterless signals are supported. - \endlist - \section1 Parser Status Generally using QML is a breeze - you implement your classes in C++, add diff --git a/doc/src/declarative/qtdeclarative.qdoc b/doc/src/declarative/qtdeclarative.qdoc index 4fe9994..c945b24 100644 --- a/doc/src/declarative/qtdeclarative.qdoc +++ b/doc/src/declarative/qtdeclarative.qdoc @@ -68,15 +68,15 @@ \o \l {qmlforcpp}{QML For C++ Programmers} \endlist - Core Features: + Core QML Features: \list \o \l {binding}{Data Binding} \o \l {anchor-layout}{Layout Anchors} \o \l {qmlanimation}{Animation} \o \l {qmleffects}{Visual Effects} - \o \l {components}{Components} \o \l {qmlmodules}{Modules} \o \l {qmlfocus}{Keyboard Focus} + \o \l {Extending types from QML} \endlist QML Reference: -- cgit v0.12 From 602f05025c09093650a7030da6084665cf716e08 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Fri, 3 Jul 2009 15:34:22 +1000 Subject: Doc --- doc/src/declarative/components.qdoc | 92 --------- doc/src/declarative/extending.qdoc | 348 +++++++++++++++++++++++++++++++++ doc/src/declarative/qmlforcpp.qdoc | 55 ------ doc/src/declarative/qtdeclarative.qdoc | 4 +- 4 files changed, 350 insertions(+), 149 deletions(-) delete mode 100644 doc/src/declarative/components.qdoc create mode 100644 doc/src/declarative/extending.qdoc diff --git a/doc/src/declarative/components.qdoc b/doc/src/declarative/components.qdoc deleted file mode 100644 index d7a4ba6..0000000 --- a/doc/src/declarative/components.qdoc +++ /dev/null @@ -1,92 +0,0 @@ -/*! -\page components.html -\target components -\title Components - -A \bold component is a reusable, encapsulated Qml element with a well-defined interface. - -Writing and using components allows you to: -\list -\o Reuse sections of Qml without copy-and-paste. -\o Have consistent Look and Feel between different parts of your UI. -\o Create new Qml elements without writing a new C++ class. (See \l {cppitem}{Creating Qml elements in C++}) -\endlist - -Components are placed in \e .qml files, allowing \e to then be used as a tag -elsewhere. For example, if you have a Slider.qml file, you can then use \c {Slider { ... }} to -make a slider, just as if it was a built-in type. - -Components may be collected into \l {qmlmodules}{modules}. - -\section1 Example: Creating a MyButton Component - -This example describes how to create a component from an existing snippet of Qml. - -Assume you have an existing UI with a single 'Save' button, defined as follows: - -\code -Image { - source: "pics/button-background.png" - Text { - text: "Save" - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - } - MouseRegion { - anchors.fill: parent - onClick: { saveData() } - } -} -\endcode - -For the next release, you plan to add 'Cancel' and 'Reset' buttons. Rather than copying and pasting the above markup, you can create a component: - -\list 1 -\o Create a file called MyButton.qml, and copy the relevant Qml snippet into that file. -\o Make some minor changes to define the component's interface: - -\code -Image { - property string label - signal clicked - source: "pics/button-background.png" - Text { - text: parent.label - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - } - MouseRegion { - anchors.fill: parent - onClick: { parent.click.emit() } - } -} -\endcode - -The \a label property and \a click signal that were added effectively become part of the 'public API' of the MyButton component. In a similar manner, the Text and MouseRegion elements become invisible to anyone using the component. Note that the Text element now binds in its data from \a label, and the MouseRegion emits a generic signal. - -\o The component can now be used elsewhere as MyButton: - -\code -MyButton { label: "Save"; onClicked: saveData() } -... -MyButton { label: "Cancel"; onClicked: cancelData() } -... -MyButton { label: "Reset"; onClicked: resetData() } -\endcode - -\endlist - -\section1 Placing .qml Files - -When one component refers to a another, the second must be found either in the same directory -as the first, or in a directory imported using the \c import statement: - -\code -import "library" -\endcode - -\section1 Namespaces - -Namespaces for QML will be supported in Qt 4.6. - -*/ diff --git a/doc/src/declarative/extending.qdoc b/doc/src/declarative/extending.qdoc new file mode 100644 index 0000000..7163a93 --- /dev/null +++ b/doc/src/declarative/extending.qdoc @@ -0,0 +1,348 @@ +/*! +\page qml-extending-types.html +\title Extending types from QML + +Many of the elements available for use in QML are implemented in +\l {QML for C++ Programmers}{C++}. These types are know as "core types". QML +allows programmers to build new, fully functional elements without using C++. +Existing core types can be extended, and new types defined entirely in the QML +language. + +\tableofcontents + +\section1 Adding new properties + +New properties can be added to an existing type. These new properties are +available for use within QML, and also appear as regular Qt properties on the +C++ object, accessible through the regular property access mechanisms. + +Like all properties in QML, custom properties are typed. The type is used to +define the property's behavior, and also determines the C++ type of the created +Qt property. The following table shows the list of types available when +declaring a new property, and the corresponding C++ type. + +\table +\header \o QML Type Name \o C++ Type Name +\row \o int \o int +\row \o bool \o bool +\row \o double \o double +\row \o real \o double +\row \o string \o QString +\row \o url \o QUrl +\row \o color \o QColor +\row \o date \o QDate +\row \o var \o QVariant +\row \o variant \o QVariant +\endtable + +QML supports two methods for adding a new property to a type - a new property +definition, and a property alias. + +\section2 Property definitions + +Property definitions add a new property to an existing type. The storage of the +property is managed by QML. The defined property may be read, written and bound +to and from. + +The syntax for defining a new property is: +\code + [default] property [: defaultValue] +\endcode + +This declaration may appear anywhere within a type body, but it is customary to +include it at the top. Attempting to declare two properties with the same name +in the same type block is an error. However, a new property may reuse the name +of an existing property on the type. This should be done with caution, as the +existing property will be hidden, and become inaccessible. + +The must be one of the QML type names shown in the above table. +Additionally, an optional default value of the property can be provided. The +default value is a convenient shortcut, but is behaviorally identical to doing +it in two steps, like this: + +\code + // Use default value + property int myProperty: 10 + + // Longer, but behaviorally identical + property int myProperty + myProperty: 10 +\endcode + +If specified, the optional "default" attribute marks the new property as the +types default property, overriding any existing default property. Using the +default attribute twice in the same type block is an error. + +The following example shows how to declare a new "innerColor" property that +controls the color of the inner rectangle. + +\code + Rect { + property color innerColor: "black" + + color: "red"; width: 100; height: 100 + Rect { + anchors.centeredIn: parent + width: parent.width - 10 + height: parent.height - 10 + color: innerColor + } + } +\endcode + +\section2 Property aliases + +Property aliases are a more advanced form of property declaration. Unlike a +property definition, that allocates a new, unique storage space for the +property, a property alias connects the newly declared property (called the +aliasing property) to an existing property (the aliased property). Read +operations on the aliasing property act as read operations on the aliased +property, and write operations on the aliasing property as write operations on +the aliased property. + +A property alias declaration looks a lot like a property definition: +\code + [default] property alias : +\endcode + +As the aliasing property has the same type as the aliased property, an explicit +type is omitted, and the special "alias" keyword is used. Instead of a default +value, a property alias includes a compulsary alias reference. The alias +reference is used to locate the aliased property. While similar to a property +binding, the alias reference syntax is highly restricted. + +An alias reference takes the form +\code + . +\endcode +where must refer to an object id within the same component as the type +declaring the alias, and refers to a property on this object. The +alias reference syntax may become more flexibly in future releases. + +Here is the property definition example rewritten to use property aliases. +\code +Rect { + property alias innerColor: InnerRect.color + + color: "red"; width: 100; height: 100 + Rect { + id: InnerRect + anchors.centeredIn: parent + width: parent.width - 10 + height: parent.height - 10 + color: "black" + } +} +\endcode + +Aliases are most useful when \l {Defining new Components}. Consequently +they have several apparent limitations that only make sense in this context. + +Aliases are only activated once the component specifying them is completed. The +most obvious consequence of this is that the component itself cannot generally +use the aliased property directly. For example, this will not work: + +\code + // Does NOT work + property alias innerColor: InnerRect.color + innerColor: "black" +\endcode + +This behavior is required to allow type developers to redefine the behavior +of existing property names while continuing to use the existing behavior within +the type they are building, something that is not possible with property +definitions. In the example used so far, this could allows the developer to fix +the external rectangle's color as "red" and redefine the "color" property to +refer to the inner rectangle, like this: + +\code +Rect { + property alias color: InnerRect.color + + color: "red"; width: 100; height: 100 + Rect { + id: InnerRect + anchors.centeredIn: parent + width: parent.width - 10 + height: parent.height - 10 + color: "black" + } +} +\endcode + +Users of this type would not be able to affect the color of the red rectangle, +but would find using the "color" property, rather than the strange new +"innerColor" property, much more familiar. + +A second, much less significant, consequence of the delayed activation of +aliases is that an alias reference cannot refer to another aliasing property +declared within the same component. This will not work: + +\code + // Does NOT work + id: Root + property alias innerColor: InnerRect.color + property alias innerColor2: Root.innerColor +\endcode + +From outside the component, aliasing properties appear as regular Qt properties +and consequently can be used in alias references. + +\section1 Adding new signals + +New signals can be added to an existing type. These new signals are available +for use within QML, and also appear as regular Qt signals on the C++ object that +can be used in Qt signal/slot connections. + +The syntax for defining a new signal is: +\code +signal [([ [, ...]])] +\endcode + +This declaration may appear anywhere within a type body, but it is customary to +include it at the top. Attempting to declare two signals or methods with the +same name in the same type block is an error. However, a new signal may reuse +the name of an existing signal on the type. This should be done with caution, +as the existing signal may be hidden and become inaccessible. + +The options for parameter types are the same as for property types (see +\l {Adding new properties}. If this signal has no parameters, the parameter +list may be omitted entirely. + +Here are three examples of signal declarations: +\code + Item { + signal clicked + signal hovered() + signal performAction(string action, var actionArgument) + } +\endcode + +\section1 Adding new methods + +New methods can be added to an existing type. These new methods are available +for use within QML, and also appear as regular Qt slots on the C++ object that +can be used in Qt signal/slot connections. + +\code +function ([[, ...]]) { } +\endcode + +This declaration may appear anywhere within a type body, but it is customary to +include it at the top. Attempting to declare two methods or signals with the +same name in the same type block is an error. However, a new method may reuse +the name of an existing method on the type. This should be done with caution, +as the existing method may be hidden and become inaccessible. + +Methods parameters are not typed. In C++ these parameters are of type QVariant. +The body of the method is written in JavaScript and may access the parameters by +name. + +This example adds a new method that behaves like a child: +\code +Item { + function say(text) { + print("You said " + text); + } +} +\endcode + +\section1 Defining new Components + +A component is a reusable type with a well-defined interface built entirely in +QML. Components appear as regular QML elements, and can be used interchangably +with core types. Components allow developers to create new types to be reused +in other projects without the use of C++. Components can also help to reduce +duplication inside one project by limiting the need for large numbers of +copy-and-pasted blocks. + +Any snippet of QML code can become a component, just by placing it in the file +".qml" where is the new element name, and begins with an uppercase +letter. These QML files automatically become available as new QML element types +to other QML components and applications in the same directory. + +For example, here we show how a component named "Box" is defined and used +multiple times by an application. + +\table +\row +\o application.qml +\code +Rect { + width: 100; height: 400; + Box { x: 0; y: 0 } + Box { x: 0; y: 150 } + Box { x: 0; y: 300 } +} +\endcode +\o Box.qml +\code +Rect { + width: 100; height: 100; + color: "blue" +} +\endcode +\endtable + +Components may be collected into \l {Modules of Components} that gives the +developer more freedom than just putting files in the same directory. + +\section2 Building reusable components + +A component type built to be reused by others must have a well defined +interface. In QML, an interface consists of a defined collection of +properties, signals and methods. Users of a component have access to all the +properties, signals and methods defined on the root element of the component. + +In the component example above, the root element of the "Box" component is a +Rect. As the Rect type has a "color" property, this property is accessible to +users of the Box component. For example, the application.qml can be modified +to show three different colored boxes like this: +\code +Rect { + width: 100; height: 400; + Box { x: 0; y: 0; color: "red"; } + Box { x: 0; y: 150; color: "yellow"; } + Box { x: 0; y: 300; color: "green"; } +} +\endcode + +As expected, adding additional properties to the root element of Box, makes them +available externally. Here we add a "text" property: + +\table +\row +\o application.qml +\code +Rect { + width: 100; height: 400; + Box { x: 0; y: 0; color: "red"; text: "stop" } + Box { x: 0; y: 150; color: "yellow"; text: "slow" } + Box { x: 0; y: 300; color: "green"; text: "go" } +} +\endcode +\o Box.qml +\code +Rect { + property alias text: MyText.text + width: 100; height: 100; + color: "blue" + Text { + id: MyText + anchors.centeredIn: parent + } +} +\endcode +\endtable + +Methods and signals may be added in the same way. + +As all external methods, signals and properties are accessible to external +users, developers should ensure that setting these properties does not have +any undesirable side-effects. For most resiliance, root level properties should +only be used for literal default values. When a root level property must be +used inside the component - such as the children property - property aliases +can be used to redirect this property to a "safe" location for external users. +Try to think of the root level properties as being "owned" by the components +user, rather than the component itself. +*/ diff --git a/doc/src/declarative/qmlforcpp.qdoc b/doc/src/declarative/qmlforcpp.qdoc index c95cb71..38f5665 100644 --- a/doc/src/declarative/qmlforcpp.qdoc +++ b/doc/src/declarative/qmlforcpp.qdoc @@ -567,61 +567,6 @@ engine will automatically set the property as the target of the value source. - \section1 Extending types in QML - - QML is designed to allow you to build fully working types without writing - a line of C++ code. This is, for example, used extensively when designing - applications using the Fluid UI primitives. To create new types, it is - necessary to be able to define new signals, slots and properties in QML. - - In this example, a Button is extended to have an additional - "text2" property (which always returns "Hello world!") and an additional - signal "clicked2" that is also emitted when the button is clicked. Not - a very useful extension, but an extension nonetheless. - - \table - \row - \o - \code - QmlEngine engine; - QmlComponent component(&engine, qmlData); - QObject *object = component.create(); - // Will print "Hello world!" - qDebug() << object->property("text2"); - // Will be emitted whenever the button is clicked - QObject::connect(object, SIGNAL(clicked2()), this, SLOT(...)); - \endcode - \o - \code - Button { - property string text2 - signal clicked2 - - text: "Hello!" - text2: "Hello world!" - onClicked: clicked2.emit() - } - \endcode - \endtable - - The general syntax for defining new properties and signals is: - - \list - \o - \code - [default] property [: ] - \endcode - - Where type can be one of \e int, \e bool, \e double, \e real, \e string, - \e color, \e date, \e var or \e variant. - - \o - \code - signal - \endcode - Currently only parameterless signals are supported. - \endlist - \section1 Parser Status Generally using QML is a breeze - you implement your classes in C++, add diff --git a/doc/src/declarative/qtdeclarative.qdoc b/doc/src/declarative/qtdeclarative.qdoc index 4fe9994..c945b24 100644 --- a/doc/src/declarative/qtdeclarative.qdoc +++ b/doc/src/declarative/qtdeclarative.qdoc @@ -68,15 +68,15 @@ \o \l {qmlforcpp}{QML For C++ Programmers} \endlist - Core Features: + Core QML Features: \list \o \l {binding}{Data Binding} \o \l {anchor-layout}{Layout Anchors} \o \l {qmlanimation}{Animation} \o \l {qmleffects}{Visual Effects} - \o \l {components}{Components} \o \l {qmlmodules}{Modules} \o \l {qmlfocus}{Keyboard Focus} + \o \l {Extending types from QML} \endlist QML Reference: -- cgit v0.12