aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/src/qml/basictypes.qdoc17
-rw-r--r--doc/src/qml/javascriptblocks.qdoc13
-rw-r--r--doc/src/qml/propertybinding.qdoc19
-rw-r--r--doc/src/snippets/qml/DynamicText.qml52
-rw-r--r--doc/src/snippets/qml/qtBinding.1.qml55
-rw-r--r--doc/src/snippets/qml/qtBinding.2.qml58
-rw-r--r--doc/src/snippets/qml/qtBinding.3.qml63
-rw-r--r--doc/src/snippets/qml/qtBinding.4.qml54
-rw-r--r--src/qml/qml/qqmlproperty.cpp20
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp59
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions_p.h1
-rw-r--r--src/qml/qml/v8/qv8engine.cpp5
-rw-r--r--src/qml/qml/v8/qv8engine_p.h10
-rw-r--r--src/qml/qml/v8/qv8qobjectwrapper.cpp52
-rw-r--r--src/qml/qml/v8/qv8valuetypewrapper.cpp7
-rw-r--r--tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml4
-rw-r--r--tests/auto/qml/qqmlecmascript/data/functionAssignment.1.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/functionAssignment.2.qml14
-rw-r--r--tests/auto/qml/qqmlecmascript/data/functionAssignment.3.qml18
-rw-r--r--tests/auto/qml/qqmlecmascript/data/functionAssignment.js4
-rw-r--r--tests/auto/qml/qqmlecmascript/data/propertyVar.11.qml21
-rw-r--r--tests/auto/qml/qqmlecmascript/data/propertyVar.12.qml19
-rw-r--r--tests/auto/qml/qqmlecmascript/data/propertyVar.13.qml19
-rw-r--r--tests/auto/qml/qqmlecmascript/data/propertyVar.14.qml21
-rw-r--r--tests/auto/qml/qqmlecmascript/data/propertyVar.15.qml20
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp30
-rw-r--r--tests/auto/qml/qqmlvaluetypes/data/bindingAssignment.2.qml12
-rw-r--r--tests/auto/qml/qqmlvaluetypes/data/bindingAssignment.qml5
-rw-r--r--tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp20
-rw-r--r--tests/auto/quick/qquickloader/data/initialPropertyValues.binding.qml2
30 files changed, 639 insertions, 57 deletions
diff --git a/doc/src/qml/basictypes.qdoc b/doc/src/qml/basictypes.qdoc
index 0f66a96731..c179cbff2e 100644
--- a/doc/src/qml/basictypes.qdoc
+++ b/doc/src/qml/basictypes.qdoc
@@ -436,10 +436,9 @@
\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, except that you
- cannot assign a JavaScript function to such a property.
- For example, var properties can store numbers, strings, objects and
- arrays:
+ It is equivalent to a regular JavaScript variable.
+ For example, var properties can store numbers, strings, objects,
+ arrays and functions:
\qml
Item {
@@ -454,13 +453,10 @@
property var aVector3d: Qt.vector3d(100, 100, 100)
property var anArray: [1, 2, 3, "four", "five", (function() { return "six"; })]
property var anObject: { "foo": 10, "bar": 20 }
+ property var aFunction: (function() { return "one"; })
}
\endqml
- Attempting to assign a JavaScript function to a var property will result in
- a binding assignment as per other property types. You can assign a JavaScript
- array containing a single function element instead.
-
It is important to note that changes in regular properties of JavaScript
objects assigned to a var property will \bold{not} trigger updates of bindings
that access them. The example below will display "The car has 4 wheels" as
@@ -481,6 +477,11 @@
}
\endqml
+ If the onCompleted handler instead had \tt{"car = new Object({wheels: 6})"}
+ then the text would be updated to say "The car has 6 wheels"., since the
+ car property itself would be changed, which causes a change notification
+ to be emitted.
+
A \c var type property can also hold an image or pixmap.
A \c var which contains a QPixmap or QImage is known as a
"scarce resource" and the declarative engine will attempt to
diff --git a/doc/src/qml/javascriptblocks.qdoc b/doc/src/qml/javascriptblocks.qdoc
index 0c1d4c284c..5a30324d1a 100644
--- a/doc/src/qml/javascriptblocks.qdoc
+++ b/doc/src/qml/javascriptblocks.qdoc
@@ -72,7 +72,7 @@ Rectangle {
id: button
width: 200; height: 80; color: "lightsteelblue"
- MouseArea {
+ MouseArea {
id: mousearea
anchors.fill: parent
@@ -103,7 +103,7 @@ Rectangle {
color: mousearea.pressed ? "steelblue" : "lightsteelblue"
- MouseArea {
+ MouseArea {
id: mousearea
anchors.fill: parent
}
@@ -366,6 +366,15 @@ Likewise, the \l {Component::onDestruction} attached property is triggered on
component destruction.
+\section1 JavaScript and Property Binding
+
+Property bindings can be created in JavaScript by assigning the property the value returned
+by calling Qt.binding() where the parameter to Qt.binding() is a \c function
+that returns the required value.
+
+See \l {qml-javascript-assignment}{Property Assignment versus Property Binding} for details.
+
+
\section1 QML JavaScript Restrictions
QML executes standard JavaScript code, with the following restrictions:
diff --git a/doc/src/qml/propertybinding.qdoc b/doc/src/qml/propertybinding.qdoc
index 6dd862f55b..ce5ad0be4a 100644
--- a/doc/src/qml/propertybinding.qdoc
+++ b/doc/src/qml/propertybinding.qdoc
@@ -87,7 +87,7 @@ The property binding causes the width of the \c Rectangle to update whenever the
\c {parent}'s width changes.
Assigning a property value (using the equals sign "\c {=}") does not create a
-property binding.
+property binding (unless explicitly assigned, see below).
\snippet doc/src/snippets/qml/properties.qml property assignment
Instead of creating a property binding, the assignment simply sets the \c Rectangle
@@ -100,9 +100,9 @@ and if any code explicitly re-sets this value, the property binding is removed.
\section1 Binding to JavaScript Functions
The \c{property : value} syntax for property binding is QML-specific and cannot
-be used in JavaScript. Instead, to bind a property from JavaScript, assign a \c
-function to the property that returns the required value. The following code
-correctly creates
+be used in JavaScript. Instead, to bind a property from JavaScript, assign the
+result returned by the \c{Qt.binding()} function to the property. This will cause
+a binding assignment on the specified property. The following code correctly creates
the binding in JavaScript rather than QML:
\qml
@@ -110,7 +110,7 @@ Item {
width: 100
Component.onCompleted: {
- height = (function() { return width * 2 })
+ height = Qt.binding(function() { return width * 2 })
}
}
\endqml
@@ -124,10 +124,8 @@ binding.
For example, the \c Component.onCompleted handler below is defined within the
scope of the \l Item, and references to \c width within this scope would refer
to the \l Item's width, rather than that of the \l Rectangle. To bind the \l
-Rectangle's \c height to its own \c width, the function needs to explicitly
-refer to \c this.width rather than just \c width. Otherwise, the height of the
-\l Rectangle would be bound to the width of the \l Item and not the \l
-Rectangle.
+Rectangle's \c height to its own \c width, the function passed to Qt.binding()
+needs to explicitly refer to \c this.width rather than just \c width.
\qml
Item {
@@ -141,7 +139,8 @@ Item {
}
Component.onCompleted: {
- rect.height = (function() { return this.width * 2 })
+ rect.height = Qt.binding(function() { return this.width * 2 })
+ console.log("rect.height = " + rect.height) // prints 200
}
}
\endqml
diff --git a/doc/src/snippets/qml/DynamicText.qml b/doc/src/snippets/qml/DynamicText.qml
new file mode 100644
index 0000000000..9711702037
--- /dev/null
+++ b/doc/src/snippets/qml/DynamicText.qml
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//![0]
+import QtQuick 2.0
+
+Text {
+ id: textElement
+ width: 200
+ height: 200
+ text: "Default text"
+ property string dynamicText: "Dynamic text"
+ onTextChanged: console.log(text)
+}
+//![0]
diff --git a/doc/src/snippets/qml/qtBinding.1.qml b/doc/src/snippets/qml/qtBinding.1.qml
new file mode 100644
index 0000000000..acec88a47f
--- /dev/null
+++ b/doc/src/snippets/qml/qtBinding.1.qml
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+//![0]
+Item {
+ property bool someCondition: true
+ property int edgePosition
+
+ Component.onCompleted: {
+ if (someCondition == true) {
+ // bind to the result of the binding expression passed to Qt.binding()
+ edgePosition = Qt.binding(function() { return x + width })
+ }
+ }
+}
+//![0]
diff --git a/doc/src/snippets/qml/qtBinding.2.qml b/doc/src/snippets/qml/qtBinding.2.qml
new file mode 100644
index 0000000000..9b78bc395a
--- /dev/null
+++ b/doc/src/snippets/qml/qtBinding.2.qml
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+//![0]
+Item {
+ id: root
+ property string dynamicText: "Root text"
+
+ Component.onCompleted: {
+ var c = Qt.createComponent("DynamicText.qml")
+
+ var obj1 = c.createObject(root, { 'text': Qt.binding(function() { return dynamicText + ' extra text' }) })
+ root.dynamicText = "Modified root text"
+
+ var obj2 = c.createObject(root, { 'text': Qt.binding(function() { return this.dynamicText + ' extra text' }) })
+ obj2.dynamicText = "Modified text element text"
+ }
+}
+//![0]
diff --git a/doc/src/snippets/qml/qtBinding.3.qml b/doc/src/snippets/qml/qtBinding.3.qml
new file mode 100644
index 0000000000..a27914cc15
--- /dev/null
+++ b/doc/src/snippets/qml/qtBinding.3.qml
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+//![0]
+Item {
+ id: root
+ property string dynamicText: "Root text"
+
+ Loader {
+ id: loaderOne
+ onLoaded: root.dynamicText = "Modified root text"
+ }
+
+ Loader {
+ id: loaderTwo
+ onLoaded: item.dynamicText = "Modified dynamic text"
+ }
+
+ Component.onCompleted: {
+ loaderOne.setSource("DynamicText.qml", { 'text': Qt.binding(function() { return dynamicText + ' extra text' }) })
+ loaderTwo.setSource("DynamicText.qml", { 'text': Qt.binding(function() { return this.dynamicText + ' extra text' }) })
+ }
+}
+//![0]
diff --git a/doc/src/snippets/qml/qtBinding.4.qml b/doc/src/snippets/qml/qtBinding.4.qml
new file mode 100644
index 0000000000..0155957a59
--- /dev/null
+++ b/doc/src/snippets/qml/qtBinding.4.qml
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+//![0]
+Item {
+ width: 50
+ property var storedBindings: [ Qt.binding(function() { return x + width }) ] // stored
+ property int a: Qt.binding(function() { return x + width }) // error!
+ property int b
+
+ Component.onCompleted: {
+ b = storedBindings[0] // causes binding assignment
+ }
+}
+//![0]
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index 6321592e9a..00cb65d106 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -1486,6 +1486,17 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
if (expression->hasError()) {
return false;
+ } else if (isVmeProperty) {
+ typedef QQmlVMEMetaObject VMEMO;
+ if (!result.IsEmpty() && result->IsFunction()
+ && !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) {
+ // we explicitly disallow this case to avoid confusion. Users can still store one
+ // in an array in a var property if they need to, but the common case is user error.
+ expression->delayedError()->error.setDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
+ return false;
+ }
+ VMEMO *vmemo = static_cast<VMEMO *>(const_cast<QMetaObject *>(object->metaObject()));
+ vmemo->setVMEProperty(core.coreIndex, result);
} else if (isUndefined && core.isResettable()) {
void *args[] = { 0 };
QMetaObject::metacall(object, QMetaObject::ResetProperty, core.coreIndex, args);
@@ -1495,12 +1506,11 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
expression->delayedError()->error.setDescription(QLatin1String("Unable to assign [undefined] to ") + QLatin1String(QMetaType::typeName(type)));
return false;
} else if (result->IsFunction()) {
- expression->delayedError()->error.setDescription(QLatin1String("Unable to assign a function to a property."));
+ if (!result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty())
+ expression->delayedError()->error.setDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
+ else
+ expression->delayedError()->error.setDescription(QLatin1String("Unable to assign a function to a property of any type other than var."));
return false;
- } else if (isVmeProperty) {
- typedef QQmlVMEMetaObject VMEMO;
- VMEMO *vmemo = static_cast<VMEMO *>(const_cast<QMetaObject *>(object->metaObject()));
- vmemo->setVMEProperty(core.coreIndex, result);
} else if (!writeValueProperty(object, engine, core, value, context, flags)) {
if (watcher.wasDeleted())
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index 7d7aaa2148..8101408ced 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -1316,6 +1316,65 @@ v8::Handle<v8::Value> locale(const v8::Arguments &args)
return QQmlLocale::locale(v8engine, code);
}
+/*!
+ \qmlmethod Qt::binding(function)
+
+ Returns a JS object representing a binding expression which may be
+ assigned to any property in imperative code to cause a binding
+ assignment.
+
+ There are two main use-cases for the function: firstly, in imperative
+ JavaScript code to cause a binding assignment:
+
+ \snippet doc/src/snippets/declarative/qtBinding.1.qml 0
+
+ and secondly, when defining initial property values of dynamically
+ constructed objects (via Component.createObject() or
+ Loader.setSource()) as being bound to the result of an expression.
+
+ For example, assuming the existence of a DynamicText component:
+ \snippet doc/src/snippets/declarative/DynamicText.qml 0
+
+ the output from:
+ \snippet doc/src/snippets/declarative/qtBinding.2.qml 0
+
+ and from:
+ \snippet doc/src/snippets/declarative/qtBinding.3.qml 0
+
+ should both be:
+ \code
+ Root text extra text
+ Modified root text extra text
+ Dynamic text extra text
+ Modified dynamic text extra text
+ \endcode
+
+ This function cannot be used in property binding declarations
+ (see the documentation on \l{qml-javascript-assignment}{binding
+ declarations and binding assignments}) except when the result is
+ stored in an array bound to a var property.
+
+ \snippet doc/src/snippets/declarative/qtBinding.4.qml 0
+
+ Note: in QtQuick 1.x, all function assignment was treated as
+ binding assignment, so the Qt.binding() function is new in
+ QtQuick 2.0.
+
+ \since QtQuick 2.0
+*/
+v8::Handle<v8::Value> binding(const v8::Arguments &args)
+{
+ QString code;
+ if (args.Length() != 1)
+ V8THROW_ERROR("binding() requires 1 argument");
+ if (!args[0]->IsFunction())
+ V8THROW_TYPE("binding(): argument (binding expression) must be a function");
+
+ v8::Handle<v8::Object> rv = args[0]->ToObject()->Clone();
+ rv->SetHiddenValue(V8ENGINE()->bindingFlagKey(), v8::Boolean::New(true));
+ return rv;
+}
+
} // namespace QQmlBuiltinFunctions
QT_END_NAMESPACE
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
index ddb1c64243..bbfe88a292 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
@@ -103,6 +103,7 @@ v8::Handle<v8::Value> qsTrId(const v8::Arguments &args);
v8::Handle<v8::Value> qsTrIdNoOp(const v8::Arguments &args);
v8::Handle<v8::Value> stringArg(const v8::Arguments &args);
v8::Handle<v8::Value> locale(const v8::Arguments &args);
+v8::Handle<v8::Value> binding(const v8::Arguments &args);
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp
index 8e8223fea1..e49bc62917 100644
--- a/src/qml/qml/v8/qv8engine.cpp
+++ b/src/qml/qml/v8/qv8engine.cpp
@@ -147,6 +147,8 @@ QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership)
QV8GCCallback::registerGcPrologueCallback();
m_strongReferencer = qPersistentNew(v8::Object::New());
+ m_bindingFlagKey = qPersistentNew(v8::String::New("qml::binding"));
+
m_stringWrapper.init();
m_contextWrapper.init(this);
m_qobjectWrapper.init(this);
@@ -191,6 +193,8 @@ QV8Engine::~QV8Engine()
m_contextWrapper.destroy();
m_stringWrapper.destroy();
+ qPersistentDispose(m_bindingFlagKey);
+
m_originalGlobalObject.destroy();
if (m_ownsV8Context)
@@ -598,6 +602,7 @@ void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global)
qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this));
qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this));
qt->Set(v8::String::New("locale"), V8FUNCTION(locale, this));
+ qt->Set(v8::String::New("binding"), V8FUNCTION(binding, this));
if (m_engine) {
qt->Set(v8::String::New("application"), newQObject(new QQuickApplication(m_engine)));
diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h
index bc57b27085..825d7a3583 100644
--- a/src/qml/qml/v8/qv8engine_p.h
+++ b/src/qml/qml/v8/qv8engine_p.h
@@ -357,6 +357,9 @@ public:
// a QVariant wrapper
inline v8::Handle<v8::Value> newQVariant(const QVariant &);
+ // Return the JS string key for the "function is a binding" flag
+ inline v8::Handle<v8::String> bindingFlagKey() const;
+
// Return the network access manager for this engine. By default this returns the network
// access manager of the QQmlEngine. It is overridden in the case of a threaded v8
// instance (like in WorkerScript).
@@ -461,6 +464,8 @@ protected:
v8::Persistent<v8::Context> m_context;
QScriptOriginalGlobalObject m_originalGlobalObject;
+ v8::Persistent<v8::String> m_bindingFlagKey;
+
QV8StringWrapper m_stringWrapper;
QV8ContextWrapper m_contextWrapper;
QV8QObjectWrapper m_qobjectWrapper;
@@ -609,6 +614,11 @@ v8::Handle<v8::Value> QV8Engine::newSequence(int sequenceType, QObject *object,
return m_sequenceWrapper.newSequence(sequenceType, object, property, succeeded);
}
+v8::Handle<v8::String> QV8Engine::bindingFlagKey() const
+{
+ return m_bindingFlagKey;
+}
+
// XXX Can this be made more optimal? It is called prior to resolving each and every
// unqualified name in QV8ContextWrapper.
bool QV8Engine::startsWithUpper(v8::Handle<v8::String> string)
diff --git a/src/qml/qml/v8/qv8qobjectwrapper.cpp b/src/qml/qml/v8/qv8qobjectwrapper.cpp
index 78b2cb719c..59b58ca503 100644
--- a/src/qml/qml/v8/qv8qobjectwrapper.cpp
+++ b/src/qml/qml/v8/qv8qobjectwrapper.cpp
@@ -519,7 +519,7 @@ v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject
return v8::Handle<v8::Value>();
}
- if (result->isFunction()) {
+ if (result->isFunction() && !result->isVMEProperty()) {
if (result->isVMEFunction()) {
return ((QQmlVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
} else if (result->isV8Function()) {
@@ -579,24 +579,34 @@ static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropert
v8::Handle<v8::Value> value)
{
QQmlBinding *newBinding = 0;
-
if (value->IsFunction()) {
- QQmlContextData *context = engine->callingContext();
- v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
-
- v8::Local<v8::StackTrace> trace =
- v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
- v8::StackTrace::kScriptName));
- v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
- int lineNumber = frame->GetLineNumber();
- int columNumber = frame->GetColumn();
- QString url = engine->toString(frame->GetScriptName());
-
- newBinding = new QQmlBinding(&function, object, context);
- newBinding->setSourceLocation(url, lineNumber, columNumber);
- newBinding->setTarget(object, *property, context);
- newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
- QQmlBinding::RequiresThisObject);
+ if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) {
+ if (!property->isVMEProperty()) {
+ // assigning a JS function to a non-var-property is not allowed.
+ QString error = QLatin1String("Cannot assign JavaScript function to ") +
+ QLatin1String(QMetaType::typeName(property->propType));
+ v8::ThrowException(v8::Exception::Error(engine->toString(error)));
+ return;
+ }
+ } else {
+ // binding assignment.
+ QQmlContextData *context = engine->callingContext();
+ v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
+
+ v8::Local<v8::StackTrace> trace =
+ v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
+ v8::StackTrace::kScriptName));
+ v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
+ int lineNumber = frame->GetLineNumber();
+ int columNumber = frame->GetColumn();
+ QString url = engine->toString(frame->GetScriptName());
+
+ newBinding = new QQmlBinding(&function, object, context);
+ newBinding->setSourceLocation(url, lineNumber, columNumber);
+ newBinding->setTarget(object, *property, context);
+ newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
+ QQmlBinding::RequiresThisObject);
+ }
}
QQmlAbstractBinding *oldBinding =
@@ -604,6 +614,12 @@ static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropert
if (oldBinding)
oldBinding->destroy();
+ if (!newBinding && property->isVMEProperty()) {
+ // allow assignment of "special" values (null, undefined, function) to var properties
+ static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
+ return;
+ }
+
#define PROPERTY_STORE(cpptype, value) \
cpptype o = value; \
int status = -1; \
diff --git a/src/qml/qml/v8/qv8valuetypewrapper.cpp b/src/qml/qml/v8/qv8valuetypewrapper.cpp
index 54d871d5f0..d54f04a9b0 100644
--- a/src/qml/qml/v8/qv8valuetypewrapper.cpp
+++ b/src/qml/qml/v8/qv8valuetypewrapper.cpp
@@ -324,6 +324,13 @@ v8::Handle<v8::Value> QV8ValueTypeWrapper::Setter(v8::Local<v8::String> property
QQmlBinding *newBinding = 0;
if (value->IsFunction()) {
+ if (value->ToObject()->GetHiddenValue(r->engine->bindingFlagKey()).IsEmpty()) {
+ // assigning a JS function to a non-var-property is not allowed.
+ QString error = QLatin1String("Cannot assign JavaScript function to value-type property");
+ v8::ThrowException(v8::Exception::Error(r->engine->toString(error)));
+ return value;
+ }
+
QQmlContextData *context = r->engine->callingContext();
v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
diff --git a/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml b/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml
index 122c6a87c8..989b295cb5 100644
--- a/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml
+++ b/tests/auto/qml/qqmlcomponent/data/createObjectWithScript.qml
@@ -37,7 +37,7 @@ Item{
root.declarativerectangle = a.createObject(root, {"x":17,"y":17, "color":"white", "border.width":3, "innerRect.border.width": 20});
root.declarativeitem = b.createObject(root, {"x":17,"y":17,"testBool":true,"testInt":17,"testObject":root});
- root.bindingTestObject = c.createObject(root, {'testValue': (function(){return width * 3}) }) // use root.width
- root.bindingThisTestObject = c.createObject(root, {'testValue': (function(){return this.width * 3}) }) // use width of Item within 'c'
+ root.bindingTestObject = c.createObject(root, {'testValue': Qt.binding(function(){return width * 3}) }) // use root.width
+ root.bindingThisTestObject = c.createObject(root, {'testValue': Qt.binding(function(){return this.width * 3}) }) // use width of Item within 'c'
}
}
diff --git a/tests/auto/qml/qqmlecmascript/data/functionAssignment.1.qml b/tests/auto/qml/qqmlecmascript/data/functionAssignment.1.qml
index 09540f1f6e..0b1b45b41b 100644
--- a/tests/auto/qml/qqmlecmascript/data/functionAssignment.1.qml
+++ b/tests/auto/qml/qqmlecmascript/data/functionAssignment.1.qml
@@ -2,4 +2,6 @@ import Qt.test 1.0
MyQmlObject {
property variant a: function myFunction() { return 2; }
+ property variant b: Qt.binding(function() { return 2; })
+ property var c: Qt.binding(function() { return 2; })
}
diff --git a/tests/auto/qml/qqmlecmascript/data/functionAssignment.2.qml b/tests/auto/qml/qqmlecmascript/data/functionAssignment.2.qml
index 0f78eaf1dc..3d8fd85a88 100644
--- a/tests/auto/qml/qqmlecmascript/data/functionAssignment.2.qml
+++ b/tests/auto/qml/qqmlecmascript/data/functionAssignment.2.qml
@@ -24,7 +24,7 @@ MyQmlObject {
function myFunction() {
return aNumber * 10;
}
- a = myFunction;
+ a = Qt.binding(myFunction);
}
property QtObject obj: QtObject {
@@ -34,7 +34,7 @@ MyQmlObject {
}
}
onAssignWithThisChanged: {
- a = obj.myFunction;
+ a = Qt.binding(obj.myFunction);
}
onAssignToPropertyFromJsFileChanged: {
@@ -47,8 +47,8 @@ MyQmlObject {
property Text text: Text { }
onAssignToValueTypeChanged: {
- text.font.pixelSize = (function() { return aNumber * 10; })
- a = (function() { return text.font.pixelSize; })
+ text.font.pixelSize = Qt.binding(function() { return aNumber * 10; })
+ a = Qt.binding(function() { return text.font.pixelSize; })
}
@@ -57,17 +57,17 @@ MyQmlObject {
onAssignFuncWithoutReturnChanged: {
function myFunction() {
}
- a = myFunction;
+ a = Qt.binding(myFunction);
}
onAssignWrongTypeChanged: {
function myFunction() {
return 'a string';
}
- aNumber = myFunction;
+ aNumber = Qt.binding(myFunction);
}
onAssignWrongTypeToValueTypeChanged: {
- text.font.pixelSize = (function() { return 'a string'; })
+ text.font.pixelSize = Qt.binding(function() { return 'a string'; })
}
}
diff --git a/tests/auto/qml/qqmlecmascript/data/functionAssignment.3.qml b/tests/auto/qml/qqmlecmascript/data/functionAssignment.3.qml
new file mode 100644
index 0000000000..c34a868949
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/functionAssignment.3.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+ property int t1: 1
+ property int t2: 2
+
+ function randomNumber() {
+ return 4;
+ }
+
+ Component.onCompleted: {
+ // shouldn't "convert" the randomNumber function into a binding permanently
+ t1 = Qt.binding(randomNumber)
+
+ // therefore, the following assignment should fail.
+ t2 = randomNumber
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/functionAssignment.js b/tests/auto/qml/qqmlecmascript/data/functionAssignment.js
index 14daa7629f..3ba4e193e6 100644
--- a/tests/auto/qml/qqmlecmascript/data/functionAssignment.js
+++ b/tests/auto/qml/qqmlecmascript/data/functionAssignment.js
@@ -1,6 +1,6 @@
function bindProperty()
{
- a = (function(){ return aNumber * 10 })
+ a = Qt.binding(function(){ return aNumber * 10 })
}
@@ -13,5 +13,5 @@ var testObj = new TestObject()
function bindPropertyWithThis()
{
- a = testObj.bindFunction
+ a = Qt.binding(testObj.bindFunction)
}
diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.11.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.11.qml
new file mode 100644
index 0000000000..63be26717e
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.11.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+ property var fnResult: testFunction(5)
+ property var f1: testFunction
+ property var f2
+
+ function testFunction(x) {
+ return x;
+ }
+
+ Component.onCompleted: {
+ f2 = testFunction;
+ if (fnResult != 5) return;
+ if (f1(6) != 6) return;
+ if (f2(7) != 7) return;
+ if (f1 != f2) return;
+ test = true;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.12.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.12.qml
new file mode 100644
index 0000000000..3510bd2350
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.12.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+ property var nullOne: null
+ property var nullTwo
+ property var undefOne: undefined
+ property var undefTwo
+
+ Component.onCompleted: {
+ nullTwo = null;
+ undefTwo = undefined;
+ if (nullOne != null) return;
+ if (nullOne != nullTwo) return;
+ if (undefOne != undefined) return;
+ if (undefOne != undefTwo) return;
+ test = true;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.13.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.13.qml
new file mode 100644
index 0000000000..14c7c677ae
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.13.qml
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+ property var f: b + 12
+ property int a: 100
+ property int b: testFunction()
+
+ function testFunction() {
+ return a * 3;
+ }
+
+ Component.onCompleted: {
+ if (f != 312) return;
+ a = 120;
+ if (f != 372) return;
+ test = true;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.14.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.14.qml
new file mode 100644
index 0000000000..a1e26661bb
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.14.qml
@@ -0,0 +1,21 @@
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+ property var f
+ property int a: 100
+ property int b
+
+ function testFunction() {
+ return a * 3;
+ }
+
+ Component.onCompleted: {
+ b = Qt.binding(testFunction);
+ f = Qt.binding(function() { return b + 12; });
+ if (f != 312) return;
+ a = 120;
+ if (f != 372) return;
+ test = true;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/propertyVar.15.qml b/tests/auto/qml/qqmlecmascript/data/propertyVar.15.qml
new file mode 100644
index 0000000000..5e5071fc2d
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/propertyVar.15.qml
@@ -0,0 +1,20 @@
+import QtQuick 2.0
+
+Item {
+ property bool test: false
+ property var storedBinding: [ Qt.binding(function() { return testFunction() + 12; }) ]
+ property int a: 100
+ property int b
+
+ function testFunction() {
+ return a * 3;
+ }
+
+ Component.onCompleted: {
+ b = storedBinding[0];
+ if (b != 312) return;
+ a = 120;
+ if (b != 372) return;
+ test = true;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 676557a81c..9c8d1e750b 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -195,6 +195,7 @@ private slots:
void functionAssignment_fromJS();
void functionAssignment_fromJS_data();
void functionAssignmentfromJS_invalid();
+ void functionAssignment_afterBinding();
void eval();
void function();
void functionException();
@@ -3768,6 +3769,11 @@ void tst_qqmlecmascript::propertyVar_data()
QTest::newRow("literal property assignment") << testFileUrl("propertyVar.8.qml");
QTest::newRow("qobject property assignment") << testFileUrl("propertyVar.9.qml");
QTest::newRow("base class var property assignment") << testFileUrl("propertyVar.10.qml");
+ QTest::newRow("javascript function assignment") << testFileUrl("propertyVar.11.qml");
+ QTest::newRow("javascript special assignment") << testFileUrl("propertyVar.12.qml");
+ QTest::newRow("declarative binding assignment") << testFileUrl("propertyVar.13.qml");
+ QTest::newRow("imperative binding assignment") << testFileUrl("propertyVar.14.qml");
+ QTest::newRow("stored binding assignment") << testFileUrl("propertyVar.15.qml");
}
void tst_qqmlecmascript::propertyVar()
@@ -5025,8 +5031,12 @@ void tst_qqmlecmascript::functionAssignment_fromBinding()
QQmlComponent component(&engine, testFileUrl("functionAssignment.1.qml"));
QString url = component.url().toString();
- QString warning = url + ":4: Unable to assign a function to a property.";
- QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData());
+ QString w1 = url + ":4: Unable to assign a function to a property of any type other than var.";
+ QString w2 = url + ":5: Invalid use of Qt.binding() in a binding declaration.";
+ QString w3 = url + ":6: Invalid use of Qt.binding() in a binding declaration.";
+ QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
+ QTest::ignoreMessage(QtWarningMsg, w2.toLatin1().constData());
+ QTest::ignoreMessage(QtWarningMsg, w3.toLatin1().constData());
MyQmlObject *o = qobject_cast<MyQmlObject *>(component.create());
QVERIFY(o != 0);
@@ -5094,6 +5104,22 @@ void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
delete o;
}
+void tst_qqmlecmascript::functionAssignment_afterBinding()
+{
+ QQmlComponent component(&engine, testFileUrl("functionAssignment.3.qml"));
+
+ QString url = component.url().toString();
+ QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
+ QTest::ignoreMessage(QtWarningMsg, w1.toLatin1().constData());
+
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+ QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
+ QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
+
+ delete o;
+}
+
void tst_qqmlecmascript::eval()
{
QQmlComponent component(&engine, testFileUrl("eval.qml"));
diff --git a/tests/auto/qml/qqmlvaluetypes/data/bindingAssignment.2.qml b/tests/auto/qml/qqmlvaluetypes/data/bindingAssignment.2.qml
new file mode 100644
index 0000000000..0da717ba5c
--- /dev/null
+++ b/tests/auto/qml/qqmlvaluetypes/data/bindingAssignment.2.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.0
+import Test 1.0
+
+MyTypeObject {
+ property int value: 10
+ rect.y: Qt.binding(function() { return value; }); // error.
+
+ Component.onCompleted: {
+ rect.x = 5;
+ rect.x = (function() { return value; }); // error.
+ }
+}
diff --git a/tests/auto/qml/qqmlvaluetypes/data/bindingAssignment.qml b/tests/auto/qml/qqmlvaluetypes/data/bindingAssignment.qml
index a65218669b..9b10803649 100644
--- a/tests/auto/qml/qqmlvaluetypes/data/bindingAssignment.qml
+++ b/tests/auto/qml/qqmlvaluetypes/data/bindingAssignment.qml
@@ -1,7 +1,12 @@
+import QtQuick 2.0
import Test 1.0
MyTypeObject {
property int value: 10
rect.x: value
+
+ Component.onCompleted: {
+ rect.y = Qt.binding(function() { return value + 5; });
+ }
}
diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
index 0aa223e733..c37a42fee5 100644
--- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
+++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp
@@ -871,17 +871,37 @@ void tst_qqmlvaluetypes::color()
// Test bindings can write to value types
void tst_qqmlvaluetypes::bindingAssignment()
{
+ // binding declaration
+ {
QQmlComponent component(&engine, testFileUrl("bindingAssignment.qml"));
MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
QVERIFY(object != 0);
QCOMPARE(object->rect().x(), 10);
+ QCOMPARE(object->rect().y(), 15);
object->setProperty("value", QVariant(92));
QCOMPARE(object->rect().x(), 92);
+ QCOMPARE(object->rect().y(), 97);
delete object;
+ }
+
+ // function assignment should fail without crashing
+ {
+ QString warning1 = testFileUrl("bindingAssignment.2.qml").toString() + QLatin1String(":6: Invalid use of Qt.binding() in a binding declaration.");
+ QString warning2 = testFileUrl("bindingAssignment.2.qml").toString() + QLatin1String(":10: Error: Cannot assign JavaScript function to value-type property");
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning1));
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning2));
+ QQmlComponent component(&engine, testFileUrl("bindingAssignment.2.qml"));
+ MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create());
+ QVERIFY(object != 0);
+ QCOMPARE(object->rect().x(), 5);
+ object->setProperty("value", QVariant(92));
+ QCOMPARE(object->rect().x(), 5);
+ delete object;
+ }
}
// Test bindings can read from value types
diff --git a/tests/auto/quick/qquickloader/data/initialPropertyValues.binding.qml b/tests/auto/quick/qquickloader/data/initialPropertyValues.binding.qml
index e0df50a74a..2ee60b0b50 100644
--- a/tests/auto/quick/qquickloader/data/initialPropertyValues.binding.qml
+++ b/tests/auto/quick/qquickloader/data/initialPropertyValues.binding.qml
@@ -16,6 +16,6 @@ Item {
}
Component.onCompleted: {
- loader.setSource("InitialPropertyValuesComponent.qml", {"canary": (function() { return root.bindable })});
+ loader.setSource("InitialPropertyValuesComponent.qml", {"canary": Qt.binding(function() { return root.bindable })});
}
}