Skip to content

Commit

Permalink
Add JavaScript "var" property type to QML
Browse files Browse the repository at this point in the history
This commit adds a new syntax which allows "var" type properties
which can have JavaScript objects (as well as other basic types)
assigned to them. Such JavaScript objects cannot be bound to.

Task-number: QMLNG-18
Change-Id: If7f5045f4669e0d5c1b8d0891ed765128d0bc1c6
Reviewed-on: http://codereview.qt-project.org/1466
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
  • Loading branch information
Chris Adams authored and Qt by Nokia committed Oct 6, 2011
1 parent 6bd1704 commit 752cd2a
Show file tree
Hide file tree
Showing 52 changed files with 1,519 additions and 126 deletions.
59 changes: 57 additions & 2 deletions doc/src/declarative/basictypes.qdoc
Expand Up @@ -422,14 +422,69 @@
\sa {QML Basic Types}
*/

/*!
\qmlbasictype var
\ingroup qmlbasictypes

\brief A var type is a generic property type.

A var is a generic property type capable of storing any data type.
It is equivalent to a regular JavaScript variable.
For example, var properties can store numbers, strings, objects and
arrays:

\qml
Item {
property var aNumber: 100
property var aBool: false
property var aString: "Hello world!"
property var anotherString: String("#FF008800")
property var aColor: Qt.rgba(0.2, 0.3, 0.4, 0.5)
property var aRect: Qt.rect(10, 10, 10, 10)
property var aPoint: Qt.point(10, 10)
property var aSize: Qt.size(10, 10)
property var aVector3d: Qt.vector3d(100, 100, 100)
property var anArray: [1, 2, 3, "four", "five"]
property var anObject: { "foo": 10, "bar": 20 }
}
\endqml

It is important to note that properties of JavaScript objects cannot
be bound to:

\qml
Item {
property var car: new vehicle(4)
property int wheelCount: car.wheels

function vehicle(wheels) {
this.wheels = wheels;
this.talk = function() { print("I have " + this.wheels + " wheels!"); }
}

Component.onCompleted: {
car.wheels = 6; // wheelCount will _not_ be updated
}
}
\endqml

\sa {QML Basic Types}
*/


/*!
\obsolete
\qmlbasictype variant
\ingroup qmlbasictypes

\brief A variant type is a generic property type.

A variant is a generic property type. A variant type property can hold
any of the \l {QML Basic Types}{basic type} values:
A variant is a generic property type. It is obsolete and exists only to
support old applications; new applications should use "var" type
properties instead.

A variant type property can hold any of the \l {QML Basic Types}{basic type}
values:

\qml
Item {
Expand Down
3 changes: 3 additions & 0 deletions doc/src/declarative/whatsnew.qdoc
Expand Up @@ -120,6 +120,9 @@ header and footer items).
ListView section.labelPositioning property added to allow keeping the current section label
at the start and/or next section label at the end of the view.

A new property type ("var") has been introduced which obsoletes "variant" properties in QML.
Properties of this type are equivalent to regular JavaScript variables. See the documentation
on \l{QML Basic Types} for more information about "var" properties.

\section2 QtQuick 1 is now a separate library and module

Expand Down
119 changes: 99 additions & 20 deletions src/declarative/qml/qdeclarativecompiler.cpp
Expand Up @@ -380,26 +380,54 @@ void QDeclarativeCompiler::genLiteralAssignment(QDeclarativeScript::Property *pr
if (v->value.isNumber()) {
double n = v->value.asNumber();
if (double(int(n)) == n) {
Instruction::StoreVariantInteger instr;
if (prop->core.isVMEProperty()) {
Instruction::StoreVarInteger instr;
instr.propertyIndex = prop->index;
instr.value = int(n);
output->addInstruction(instr);
} else {
Instruction::StoreVariantInteger instr;
instr.propertyIndex = prop->index;
instr.value = int(n);
output->addInstruction(instr);
}
} else {
if (prop->core.isVMEProperty()) {
Instruction::StoreVarDouble instr;
instr.propertyIndex = prop->index;
instr.value = n;
output->addInstruction(instr);
} else {
Instruction::StoreVariantDouble instr;
instr.propertyIndex = prop->index;
instr.value = n;
output->addInstruction(instr);
}
}
} else if (v->value.isBoolean()) {
if (prop->core.isVMEProperty()) {
Instruction::StoreVarBool instr;
instr.propertyIndex = prop->index;
instr.value = int(n);
instr.value = v->value.asBoolean();
output->addInstruction(instr);
} else {
Instruction::StoreVariantDouble instr;
Instruction::StoreVariantBool instr;
instr.propertyIndex = prop->index;
instr.value = n;
instr.value = v->value.asBoolean();
output->addInstruction(instr);
}
} else if(v->value.isBoolean()) {
Instruction::StoreVariantBool instr;
instr.propertyIndex = prop->index;
instr.value = v->value.asBoolean();
output->addInstruction(instr);
} else {
Instruction::StoreVariant instr;
instr.propertyIndex = prop->index;
instr.value = output->indexForString(v->value.asString());
output->addInstruction(instr);
if (prop->core.isVMEProperty()) {
Instruction::StoreVar instr;
instr.propertyIndex = prop->index;
instr.value = output->indexForString(v->value.asString());
output->addInstruction(instr);
} else {
Instruction::StoreVariant instr;
instr.propertyIndex = prop->index;
instr.value = output->indexForString(v->value.asString());
output->addInstruction(instr);
}
}
}
break;
Expand Down Expand Up @@ -1796,10 +1824,18 @@ void QDeclarativeCompiler::genPropertyAssignment(QDeclarativeScript::Property *p

} else if (prop->type == QMetaType::QVariant) {

Instruction::StoreVariantObject store;
store.line = v->object->location.start.line;
store.propertyIndex = prop->index;
output->addInstruction(store);
if (prop->core.isVMEProperty()) {
Instruction::StoreVarObject store;
store.line = v->object->location.start.line;
store.propertyIndex = prop->index;
output->addInstruction(store);
} else {
Instruction::StoreVariantObject store;
store.line = v->object->location.start.line;
store.propertyIndex = prop->index;
output->addInstruction(store);
}


} else {

Expand Down Expand Up @@ -2570,11 +2606,16 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn

const Object::DynamicProperty *defaultProperty = 0;
int aliasCount = 0;
int varPropCount = 0;
int totalPropCount = 0;
int firstPropertyVarIndex = 0;

for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) {

if (p->type == Object::DynamicProperty::Alias)
aliasCount++;
if (p->type == Object::DynamicProperty::Var)
varPropCount++;

if (p->isDefaultProperty &&
(resolveAlias || p->type != Object::DynamicProperty::Alias))
Expand Down Expand Up @@ -2628,6 +2669,7 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn
int metaType;
const char *cppType;
} builtinTypes[] = {
{ Object::DynamicProperty::Var, 0, "QVariant" },
{ Object::DynamicProperty::Variant, 0, "QVariant" },
{ Object::DynamicProperty::Int, QMetaType::Int, "int" },
{ Object::DynamicProperty::Bool, QMetaType::Bool, "bool" },
Expand Down Expand Up @@ -2706,6 +2748,9 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn
typeRef = p->typeRef;
}

if (p->type == Object::DynamicProperty::Var)
continue;

if (buildData) {
VMD *vmd = (QDeclarativeVMEMetaData *)dynamicData.data();
vmd->propertyCount++;
Expand All @@ -2726,6 +2771,31 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn

effectivePropertyIndex++;
}

if (varPropCount) {
VMD *vmd = (QDeclarativeVMEMetaData *)dynamicData.data();
if (buildData)
vmd->varPropertyCount = varPropCount;
firstPropertyVarIndex = effectivePropertyIndex;
totalPropCount = varPropCount + effectivePropertyIndex;
for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) {
if (p->type == Object::DynamicProperty::Var) {
QFastMetaBuilder::StringRef typeRef = typeRefs[p->type];
if (buildData) {
vmd->propertyCount++;
(vmd->propertyData() + effectivePropertyIndex)->propertyType = -1;
}

builder.setProperty(effectivePropertyIndex, p->nameRef, typeRef, (QMetaType::Type)-1,
QFastMetaBuilder::Writable, effectivePropertyIndex);

p->changedSignatureRef = builder.newString(p->name.utf8length() + strlen("Changed()"));
builder.setSignal(effectivePropertyIndex, p->changedSignatureRef);

effectivePropertyIndex++;
}
}
}

if (aliasCount) {
int aliasIndex = 0;
Expand Down Expand Up @@ -2913,9 +2983,19 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn

if (obj->type != -1) {
QDeclarativePropertyCache *cache = output->types[obj->type].createPropertyCache(engine)->copy();
cache->append(engine, &obj->extObject, QDeclarativePropertyCache::Data::NoFlags,
cache->append(engine, &obj->extObject,
QDeclarativePropertyCache::Data::NoFlags,
QDeclarativePropertyCache::Data::IsVMEFunction,
QDeclarativePropertyCache::Data::IsVMESignal);

// now we modify the flags appropriately for var properties.
int propertyOffset = obj->extObject.propertyOffset();
QDeclarativePropertyCache::Data *currPropData = 0;
for (int pvi = firstPropertyVarIndex; pvi < totalPropCount; ++pvi) {
currPropData = cache->property(pvi + propertyOffset);
currPropData->setFlags(currPropData->getFlags() | QDeclarativePropertyCache::Data::IsVMEProperty);
}

obj->synthCache = cache;
}

Expand Down Expand Up @@ -3205,8 +3285,7 @@ int QDeclarativeCompiler::genValueTypeData(QDeclarativeScript::Property *valueTy
int QDeclarativeCompiler::genPropertyData(QDeclarativeScript::Property *prop)
{
typedef QDeclarativePropertyPrivate QDPP;
QByteArray data = QDPP::saveProperty(prop->parent->metaObject(), prop->index, engine);

QByteArray data = QDPP::saveProperty(&prop->core);
return output->indexForByteArray(data);
}

Expand Down
5 changes: 5 additions & 0 deletions src/declarative/qml/qdeclarativeinstruction_p.h
Expand Up @@ -73,6 +73,10 @@ QT_BEGIN_NAMESPACE
F(StoreVariantInteger, storeInteger) \
F(StoreVariantDouble, storeDouble) \
F(StoreVariantBool, storeBool) \
F(StoreVar, storeString) \
F(StoreVarInteger, storeInteger) \
F(StoreVarDouble, storeDouble) \
F(StoreVarBool, storeBool) \
F(StoreString, storeString) \
F(StoreByteArray, storeByteArray) \
F(StoreUrl, storeUrl) \
Expand Down Expand Up @@ -109,6 +113,7 @@ QT_BEGIN_NAMESPACE
F(StoreObjectQList, common) \
F(AssignObjectList, assignObjectList) \
F(StoreVariantObject, storeObject) \
F(StoreVarObject, storeObject) \
F(StoreInterface, storeObject) \
F(FetchAttached, fetchAttached) \
F(FetchQList, fetchQmlList) \
Expand Down
16 changes: 15 additions & 1 deletion src/declarative/qml/qdeclarativeproperty.cpp
Expand Up @@ -1327,13 +1327,14 @@ bool QDeclarativePropertyPrivate::writeBinding(const QDeclarativeProperty &that,
QDeclarativeDeleteWatcher watcher(expression);

QVariant value;
bool isVmeProperty = pp->core.isVMEProperty();

if (isUndefined) {
} else if (that.propertyTypeCategory() == QDeclarativeProperty::List) {
value = engine->toVariant(result, qMetaTypeId<QList<QObject *> >());
} else if (result->IsNull() && that.propertyTypeCategory() == QDeclarativeProperty::Object) {
value = QVariant::fromValue((QObject *)0);
} else {
} else if (!isVmeProperty) {
value = engine->toVariant(result, type);
}

Expand All @@ -1351,6 +1352,8 @@ bool QDeclarativePropertyPrivate::writeBinding(const QDeclarativeProperty &that,
} else if (result->IsFunction()) {
expression->error.setDescription(QLatin1String("Unable to assign a function to a property."));
return false;
} else if (isVmeProperty) {
static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(that.object()->metaObject()))->setVMEProperty(that.index(), result);
} else if (object && !QDeclarativePropertyPrivate::write(that, value, flags)) {

if (watcher.wasDeleted())
Expand Down Expand Up @@ -1604,6 +1607,17 @@ QByteArray QDeclarativePropertyPrivate::saveProperty(const QMetaObject *metaObje
return rv;
}

QByteArray QDeclarativePropertyPrivate::saveProperty(QDeclarativePropertyCache::Data *core)
{
SerializedData sd;
memset(&sd, 0, sizeof(sd));
sd.isValueType = false;
sd.core = *core;

QByteArray rv((const char *)&sd, sizeof(sd));
return rv;
}

QDeclarativeProperty
QDeclarativePropertyPrivate::restore(const QByteArray &data, QObject *object, QDeclarativeContextData *ctxt)
{
Expand Down
1 change: 1 addition & 0 deletions src/declarative/qml/qdeclarativeproperty_p.h
Expand Up @@ -116,6 +116,7 @@ class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativePropertyPrivate : public QDeclara
QDeclarativeEngine *);
static QByteArray saveProperty(const QMetaObject *, int,
QDeclarativeEngine *);
static QByteArray saveProperty(QDeclarativePropertyCache::Data *);

static QDeclarativeProperty restore(const QByteArray &, QObject *, QDeclarativeContextData *);
static QDeclarativeProperty restore(const QDeclarativePropertyCache::Data &,
Expand Down
18 changes: 10 additions & 8 deletions src/declarative/qml/qdeclarativepropertycache_p.h
Expand Up @@ -95,19 +95,20 @@ class Q_DECLARATIVE_EXPORT QDeclarativePropertyCache : public QDeclarativeRefCou
IsEnumType = 0x00000100, // Property type is an enum
IsQList = 0x00000200, // Property type is a QML list
IsQmlBinding = 0x00000400, // Property type is a QDeclarativeBinding*
IsQJSValue = 0x00000800, // Property type is a QScriptValue
IsQJSValue = 0x00000800, // Property type is a QScriptValue
IsV8Handle = 0x00001000, // Property type is a QDeclarativeV8Handle
IsVMEProperty = 0x00002000, // Property type is a "var" property of VMEMO

// Apply only to IsFunctions
IsVMEFunction = 0x00002000, // Function was added by QML
HasArguments = 0x00004000, // Function takes arguments
IsSignal = 0x00008000, // Function is a signal
IsVMESignal = 0x00010000, // Signal was added by QML
IsV8Function = 0x00020000, // Function takes QDeclarativeV8Function* args
IsSignalHandler = 0x00040000, // Function is a signal handler
IsVMEFunction = 0x00004000, // Function was added by QML
HasArguments = 0x00008000, // Function takes arguments
IsSignal = 0x00010000, // Function is a signal
IsVMESignal = 0x00020000, // Signal was added by QML
IsV8Function = 0x00040000, // Function takes QDeclarativeV8Function* args
IsSignalHandler = 0x00080000, // Function is a signal handler

// Internal QDeclarativePropertyCache flags
NotFullyResolved = 0x00080000 // True if the type data is to be lazily resolved
NotFullyResolved = 0x00100000 // True if the type data is to be lazily resolved
};
Q_DECLARE_FLAGS(Flags, Flag)

Expand All @@ -129,6 +130,7 @@ class Q_DECLARATIVE_EXPORT QDeclarativePropertyCache : public QDeclarativeRefCou
bool isQmlBinding() const { return flags & IsQmlBinding; }
bool isQJSValue() const { return flags & IsQJSValue; }
bool isV8Handle() const { return flags & IsV8Handle; }
bool isVMEProperty() const { return flags & IsVMEProperty; }
bool isVMEFunction() const { return flags & IsVMEFunction; }
bool hasArguments() const { return flags & HasArguments; }
bool isSignal() const { return flags & IsSignal; }
Expand Down
3 changes: 2 additions & 1 deletion src/declarative/qml/qdeclarativescript.cpp
Expand Up @@ -920,7 +920,8 @@ bool ProcessAST::visit(AST::UiPublicMember *node)
// { "time", strlen("time"), Object::DynamicProperty::Time, "QTime", strlen("QTime") },
// { "date", strlen("date"), Object::DynamicProperty::Date, "QDate", strlen("QDate") },
{ "date", strlen("date"), Object::DynamicProperty::DateTime, "QDateTime", strlen("QDateTime") },
{ "variant", strlen("variant"), Object::DynamicProperty::Variant, "QVariant", strlen("QVariant") }
{ "variant", strlen("variant"), Object::DynamicProperty::Variant, "QVariant", strlen("QVariant") },
{ "var", strlen("var"), Object::DynamicProperty::Var, "QVariant", strlen("QVariant") }
};
static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
sizeof(propTypeNameToTypes[0]);
Expand Down
4 changes: 2 additions & 2 deletions src/declarative/qml/qdeclarativescript_p.h
Expand Up @@ -385,8 +385,8 @@ class Object : public QDeclarativePool::Class
{
DynamicProperty();

enum Type { Variant, Int, Bool, Real, String, Url, Color, Time,
Date, DateTime, Alias, Custom, CustomList };
enum Type { Var, Variant, Int, Bool, Real, String, Url, Color,
Time, Date, DateTime, Alias, Custom, CustomList };

bool isDefaultProperty;
Type type;
Expand Down

0 comments on commit 752cd2a

Please sign in to comment.