diff options
author | Qt Continuous Integration System <qt-info@nokia.com> | 2010-11-30 01:17:51 (GMT) |
---|---|---|
committer | Qt Continuous Integration System <qt-info@nokia.com> | 2010-11-30 01:17:51 (GMT) |
commit | e42d95d4e9d0dc547c604939f88de95180e74fa0 (patch) | |
tree | b4c34e7ca6eae4c2fdb0a838da6c8cf39d1d0eb3 | |
parent | 4973e73c064f706aaadbda4c4cb27ff5c577702e (diff) | |
parent | 08ec866fc93c14b9aac8c5ad7092564b6dfecf31 (diff) | |
download | Qt-e42d95d4e9d0dc547c604939f88de95180e74fa0.zip Qt-e42d95d4e9d0dc547c604939f88de95180e74fa0.tar.gz Qt-e42d95d4e9d0dc547c604939f88de95180e74fa0.tar.bz2 |
Merge branch '4.7' of scm.dev.nokia.troll.no:qt/qt-qml into 4.7-integration
* '4.7' of scm.dev.nokia.troll.no:qt/qt-qml:
Remove expect-fails from passing tests
Add license to example code
Improve consistency in handling of aliases, bindings and value types
Move KeyNavigation example to snippets, plus some doc rewording
43 files changed, 1410 insertions, 311 deletions
diff --git a/doc/src/snippets/declarative/keynavigation.qml b/doc/src/snippets/declarative/keynavigation.qml new file mode 100644 index 0000000..e11cdf1 --- /dev/null +++ b/doc/src/snippets/declarative/keynavigation.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** 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 1.0 + +Grid { + width: 100; height: 100 + columns: 2 + + Rectangle { + id: topLeft + width: 50; height: 50 + color: focus ? "red" : "lightgray" + focus: true + + KeyNavigation.right: topRight + KeyNavigation.down: bottomLeft + } + + Rectangle { + id: topRight + width: 50; height: 50 + color: focus ? "red" : "lightgray" + + KeyNavigation.left: topLeft + KeyNavigation.down: bottomRight + } + + Rectangle { + id: bottomLeft + width: 50; height: 50 + color: focus ? "red" : "lightgray" + + KeyNavigation.right: bottomRight + KeyNavigation.up: topLeft + } + + Rectangle { + id: bottomRight + width: 50; height: 50 + color: focus ? "red" : "lightgray" + + KeyNavigation.left: bottomLeft + KeyNavigation.up: topRight + } +} +//![0] diff --git a/src/declarative/graphicsitems/qdeclarativeitem.cpp b/src/declarative/graphicsitems/qdeclarativeitem.cpp index e0df751..9d6fe12 100644 --- a/src/declarative/graphicsitems/qdeclarativeitem.cpp +++ b/src/declarative/graphicsitems/qdeclarativeitem.cpp @@ -421,58 +421,28 @@ void QDeclarativeItemKeyFilter::componentComplete() \since 4.7 \brief The KeyNavigation attached property supports key navigation by arrow keys. - It is common in key-based UIs to use arrow keys to navigate - between focused items. The KeyNavigation property provides a - convenient way of specifying which item will gain focus - when an arrow key is pressed. The following example provides - key navigation for a 2x2 grid of items. - - \code - Grid { - columns: 2 - width: 100; height: 100 - Rectangle { - id: item1 - focus: true - width: 50; height: 50 - color: focus ? "red" : "lightgray" - KeyNavigation.right: item2 - KeyNavigation.down: item3 - } - Rectangle { - id: item2 - width: 50; height: 50 - color: focus ? "red" : "lightgray" - KeyNavigation.left: item1 - KeyNavigation.down: item4 - } - Rectangle { - id: item3 - width: 50; height: 50 - color: focus ? "red" : "lightgray" - KeyNavigation.right: item4 - KeyNavigation.up: item1 - } - Rectangle { - id: item4 - width: 50; height: 50 - color: focus ? "red" : "lightgray" - KeyNavigation.left: item3 - KeyNavigation.up: item2 - } - } - \endcode + Key-based user interfaces commonly allow the use of arrow keys to navigate between + focusable items. The KeyNavigation attached property enables this behavior by providing a + convenient way to specify the item that should gain focus when an arrow or tab key is pressed. + + The following example provides key navigation for a 2x2 grid of items: - By default KeyNavigation receives key events after the item it is attached to. - If the item accepts an arrow key event, the KeyNavigation - attached property will not receive an event for that key. Setting the - \l priority property to KeyNavigation.BeforeItem allows handling - of the key events before normal item processing. + \snippet doc/src/snippets/declarative/keynavigation.qml 0 - If an item has been set for a direction and the KeyNavigation - attached property receives the corresponding - key press and release events, the events will be accepted by - KeyNavigation and will not propagate any further. + The top-left item initially receives focus by setting \l {Item::}{focus} to + \c true. When an arrow key is pressed, the focus will move to the + appropriate item, as defined by the value that has been set for + the KeyNavigation \l left, \l right, \l up or \l down properties. + + Note that if a KeyNavigation attached property receives the key press and release + events for a requested arrow or tab key, the event is accepted and does not + propagate any further. + + By default, KeyNavigation receives key events after the item to which it is attached. + If the item accepts the key event, the KeyNavigation attached property will not + receive an event for that key. Setting the \l priority property to + \c KeyNavigation.BeforeItem allows the event to be used for key navigation + before the item, rather than after. \sa {Keys}{Keys attached property} */ @@ -599,7 +569,7 @@ void QDeclarativeKeyNavigationAttached::setBacktab(QDeclarativeItem *i) \list \o KeyNavigation.BeforeItem - process the key events before normal - item key processing. If the event is accepted it will not + item key processing. If the event is used for key navigation, it will be accepted and will not be passed on to the item. \o KeyNavigation.AfterItem (default) - process the key events after normal item key handling. If the item accepts the key event it will not be diff --git a/src/declarative/qml/qdeclarativebinding.cpp b/src/declarative/qml/qdeclarativebinding.cpp index cb6ad8c..a7fbf44 100644 --- a/src/declarative/qml/qdeclarativebinding.cpp +++ b/src/declarative/qml/qdeclarativebinding.cpp @@ -55,6 +55,158 @@ QT_BEGIN_NAMESPACE +QDeclarativeAbstractBinding::QDeclarativeAbstractBinding() +: m_object(0), m_propertyIndex(-1), m_mePtr(0), m_prevBinding(0), m_nextBinding(0) +{ +} + +QDeclarativeAbstractBinding::~QDeclarativeAbstractBinding() +{ + Q_ASSERT(m_prevBinding == 0); + Q_ASSERT(m_mePtr == 0); +} + +/*! +Destroy the binding. Use this instead of calling delete. + +Bindings are free to implement their own memory management, so the delete operator is not +necessarily safe. The default implementation clears the binding, removes it from the object +and calls delete. +*/ +void QDeclarativeAbstractBinding::destroy() +{ + removeFromObject(); + clear(); + + delete this; +} + +/*! +Add this binding to \a object. + +This transfers ownership of the binding to the object, marks the object's property as +being bound. + +However, it does not enable the binding itself or call update() on it. +*/ +void QDeclarativeAbstractBinding::addToObject(QObject *object, int index) +{ + Q_ASSERT(object); + + if (m_object == object && m_propertyIndex == index) + return; + + removeFromObject(); + + Q_ASSERT(!m_prevBinding); + + m_object = object; + m_propertyIndex = index; + + QDeclarativeData *data = QDeclarativeData::get(object, true); + + if (index & 0xFF000000) { + // Value type + + int coreIndex = index & 0xFFFFFF; + + // Find the value type proxy (if there is one) + QDeclarativeValueTypeProxyBinding *proxy = 0; + if (data->hasBindingBit(coreIndex)) { + QDeclarativeAbstractBinding *b = data->bindings; + while (b && b->propertyIndex() != coreIndex) + b = b->m_nextBinding; + Q_ASSERT(b && b->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy); + proxy = static_cast<QDeclarativeValueTypeProxyBinding *>(b); + } + + if (!proxy) { + proxy = new QDeclarativeValueTypeProxyBinding(object, coreIndex); + proxy->addToObject(object, coreIndex); + } + + m_nextBinding = proxy->m_bindings; + if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; + m_prevBinding = &proxy->m_bindings; + proxy->m_bindings = this; + + } else { + m_nextBinding = data->bindings; + if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; + m_prevBinding = &data->bindings; + data->bindings = this; + + data->setBindingBit(m_object, index); + } +} + +/*! +Remove the binding from the object. +*/ +void QDeclarativeAbstractBinding::removeFromObject() +{ + if (m_prevBinding) { + int index = propertyIndex(); + + *m_prevBinding = m_nextBinding; + if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding; + m_prevBinding = 0; + m_nextBinding = 0; + + if (index & 0xFF000000) { + // Value type - we don't remove the proxy from the object. It will sit their happily + // doing nothing until it is removed by a write, a binding change or it is reused + // to hold more sub-bindings. + } else if (m_object) { + QDeclarativeData *data = QDeclarativeData::get(m_object, false); + if (data) data->clearBindingBit(index); + } + + m_object = 0; + m_propertyIndex = -1; + } +} + +static void bindingDummyDeleter(QDeclarativeAbstractBinding *) +{ +} + +QDeclarativeAbstractBinding::Pointer QDeclarativeAbstractBinding::weakPointer() +{ + if (m_selfPointer.isNull()) + m_selfPointer = QSharedPointer<QDeclarativeAbstractBinding>(this, bindingDummyDeleter); + + return m_selfPointer.toWeakRef(); +} + +void QDeclarativeAbstractBinding::clear() +{ + if (m_mePtr) { + *m_mePtr = 0; + m_mePtr = 0; + } +} + +QString QDeclarativeAbstractBinding::expression() const +{ + return QLatin1String("<Unknown>"); +} + +QObject *QDeclarativeAbstractBinding::object() const +{ + return m_object; +} + +int QDeclarativeAbstractBinding::propertyIndex() const +{ + return m_propertyIndex; +} + +void QDeclarativeAbstractBinding::setEnabled(bool enabled, QDeclarativePropertyPrivate::WriteFlags flags) +{ + if (enabled) update(flags); +} + void QDeclarativeBindingPrivate::refresh() { Q_Q(QDeclarativeBinding); @@ -255,20 +407,8 @@ void QDeclarativeBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteF d->enabled = e; setNotifyOnValueChanged(e); - QDeclarativeAbstractBinding::setEnabled(e, flags); - - if (e) { - addToObject(d->property.object()); + if (e) update(flags); - } else { - removeFromObject(); - } -} - -int QDeclarativeBinding::propertyIndex() -{ - Q_D(QDeclarativeBinding); - return QDeclarativePropertyPrivate::bindingIndex(d->property); } bool QDeclarativeBinding::enabled() const @@ -283,127 +423,6 @@ QString QDeclarativeBinding::expression() const return QDeclarativeExpression::expression(); } -QDeclarativeAbstractBinding::QDeclarativeAbstractBinding() -: m_object(0), m_mePtr(0), m_prevBinding(0), m_nextBinding(0) -{ -} - -QDeclarativeAbstractBinding::~QDeclarativeAbstractBinding() -{ - Q_ASSERT(m_prevBinding == 0); - Q_ASSERT(m_mePtr == 0); -} - -void QDeclarativeAbstractBinding::destroy() -{ - removeFromObject(); - clear(); - - delete this; -} - -void QDeclarativeAbstractBinding::addToObject(QObject *object) -{ - Q_ASSERT(object); - - if (m_object == object) - return; - - int index = propertyIndex(); - - removeFromObject(); - - Q_ASSERT(!m_prevBinding); - - m_object = object; - QDeclarativeData *data = QDeclarativeData::get(object, true); - - if (index & 0xFF000000) { - // Value type - - int coreIndex = index & 0xFFFFFF; - - // Find the value type proxy (if there is one) - QDeclarativeValueTypeProxyBinding *proxy = 0; - if (data->hasBindingBit(coreIndex)) { - QDeclarativeAbstractBinding *b = data->bindings; - while (b && b->propertyIndex() != coreIndex) - b = b->m_nextBinding; - Q_ASSERT(b && b->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy); - proxy = static_cast<QDeclarativeValueTypeProxyBinding *>(b); - } - - if (!proxy) - proxy = new QDeclarativeValueTypeProxyBinding(object, coreIndex); - proxy->addToObject(object); - - m_nextBinding = proxy->m_bindings; - if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; - m_prevBinding = &proxy->m_bindings; - proxy->m_bindings = this; - - } else { - m_nextBinding = data->bindings; - if (m_nextBinding) m_nextBinding->m_prevBinding = &m_nextBinding; - m_prevBinding = &data->bindings; - data->bindings = this; - - data->setBindingBit(m_object, index); - } -} - -void QDeclarativeAbstractBinding::removeFromObject() -{ - if (m_prevBinding) { - int index = propertyIndex(); - - *m_prevBinding = m_nextBinding; - if (m_nextBinding) m_nextBinding->m_prevBinding = m_prevBinding; - m_prevBinding = 0; - m_nextBinding = 0; - - if (index & 0xFF000000) { - // Value type - we don't remove the proxy from the object. It will sit their happily - // doing nothing for ever more. - } else if (m_object) { - QDeclarativeData *data = QDeclarativeData::get(m_object, false); - if (data) data->clearBindingBit(index); - } - - m_object = 0; - } -} - -static void bindingDummyDeleter(QDeclarativeAbstractBinding *) -{ -} - -QDeclarativeAbstractBinding::Pointer QDeclarativeAbstractBinding::weakPointer() -{ - if (m_selfPointer.isNull()) - m_selfPointer = QSharedPointer<QDeclarativeAbstractBinding>(this, bindingDummyDeleter); - - return m_selfPointer.toWeakRef(); -} - -void QDeclarativeAbstractBinding::clear() -{ - if (m_mePtr) { - *m_mePtr = 0; - m_mePtr = 0; - } -} - -QString QDeclarativeAbstractBinding::expression() const -{ - return QLatin1String("<Unknown>"); -} - -void QDeclarativeAbstractBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags) -{ - if (e) m_mePtr = 0; -} - QDeclarativeValueTypeProxyBinding::QDeclarativeValueTypeProxyBinding(QObject *o, int index) : m_object(o), m_index(index), m_bindings(0) { @@ -421,16 +440,10 @@ QDeclarativeValueTypeProxyBinding::~QDeclarativeValueTypeProxyBinding() void QDeclarativeValueTypeProxyBinding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags) { if (e) { - addToObject(m_object); - QDeclarativeAbstractBinding *bindings = m_bindings; - m_bindings = 0; recursiveEnable(bindings, flags); } else { - removeFromObject(); - QDeclarativeAbstractBinding *bindings = m_bindings; - m_bindings = 0; recursiveDisable(bindings); } } @@ -440,13 +453,7 @@ void QDeclarativeValueTypeProxyBinding::recursiveEnable(QDeclarativeAbstractBind if (!b) return; - QDeclarativeAbstractBinding *next = b->m_nextBinding; - b->m_prevBinding = 0; - b->m_nextBinding = 0; - Q_ASSERT(b->m_mePtr == 0); - b->m_mePtr = &b; - - recursiveEnable(next, flags); + recursiveEnable(b->m_nextBinding, flags); if (b) b->setEnabled(true, flags); @@ -459,19 +466,8 @@ void QDeclarativeValueTypeProxyBinding::recursiveDisable(QDeclarativeAbstractBin recursiveDisable(b->m_nextBinding); - b->setEnabled(false, 0); - - Q_ASSERT(b->m_prevBinding == 0); - Q_ASSERT(b->m_nextBinding == 0); - b->m_nextBinding = m_bindings; - if (b->m_nextBinding) b->m_nextBinding->m_prevBinding = &b->m_nextBinding; - b->m_prevBinding = &m_bindings; - m_bindings = b; -} - -int QDeclarativeValueTypeProxyBinding::propertyIndex() -{ - return m_index; + if (b) + b->setEnabled(false, 0); } void QDeclarativeValueTypeProxyBinding::update(QDeclarativePropertyPrivate::WriteFlags) @@ -488,4 +484,25 @@ QDeclarativeAbstractBinding *QDeclarativeValueTypeProxyBinding::binding(int prop return binding; } +/*! +Removes a collection of bindings, corresponding to the set bits in \a mask. +*/ +void QDeclarativeValueTypeProxyBinding::removeBindings(quint32 mask) +{ + QDeclarativeAbstractBinding *binding = m_bindings; + while (binding) { + if (mask & (1 << (binding->propertyIndex() >> 24))) { + QDeclarativeAbstractBinding *remove = binding; + binding = remove->m_nextBinding; + *remove->m_prevBinding = remove->m_nextBinding; + if (remove->m_nextBinding) remove->m_nextBinding->m_prevBinding = remove->m_prevBinding; + remove->m_prevBinding = 0; + remove->m_nextBinding = 0; + remove->destroy(); + } else { + binding = binding->m_nextBinding; + } + } +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativebinding_p.h b/src/declarative/qml/qdeclarativebinding_p.h index 0b9bde6..7823a3d 100644 --- a/src/declarative/qml/qdeclarativebinding_p.h +++ b/src/declarative/qml/qdeclarativebinding_p.h @@ -78,14 +78,16 @@ public: enum Type { PropertyBinding, ValueTypeProxy }; virtual Type bindingType() const { return PropertyBinding; } + QObject *object() const; + int propertyIndex() const; + void setEnabled(bool e) { setEnabled(e, QDeclarativePropertyPrivate::DontRemoveBinding); } virtual void setEnabled(bool, QDeclarativePropertyPrivate::WriteFlags) = 0; - virtual int propertyIndex() = 0; void update() { update(QDeclarativePropertyPrivate::DontRemoveBinding); } virtual void update(QDeclarativePropertyPrivate::WriteFlags) = 0; - void addToObject(QObject *); + void addToObject(QObject *, int); void removeFromObject(); static Pointer getPointer(QDeclarativeAbstractBinding *p) { return p ? p->weakPointer() : Pointer(); } @@ -98,12 +100,14 @@ private: Pointer weakPointer(); friend class QDeclarativeData; + friend class QDeclarativeComponentPrivate; friend class QDeclarativeValueTypeProxyBinding; friend class QDeclarativePropertyPrivate; friend class QDeclarativeVME; friend class QtSharedPointer::ExternalRefCount<QDeclarativeAbstractBinding>; QObject *m_object; + int m_propertyIndex; QDeclarativeAbstractBinding **m_mePtr; QDeclarativeAbstractBinding **m_prevBinding; QDeclarativeAbstractBinding *m_nextBinding; @@ -118,11 +122,12 @@ public: virtual Type bindingType() const { return ValueTypeProxy; } virtual void setEnabled(bool, QDeclarativePropertyPrivate::WriteFlags); - virtual int propertyIndex(); virtual void update(QDeclarativePropertyPrivate::WriteFlags); QDeclarativeAbstractBinding *binding(int propertyIndex); + void removeBindings(quint32 mask); + protected: ~QDeclarativeValueTypeProxyBinding(); @@ -154,7 +159,6 @@ public: // Inherited from QDeclarativeAbstractBinding virtual void setEnabled(bool, QDeclarativePropertyPrivate::WriteFlags flags); - virtual int propertyIndex(); virtual void update(QDeclarativePropertyPrivate::WriteFlags flags); virtual QString expression() const; diff --git a/src/declarative/qml/qdeclarativecompiledbindings.cpp b/src/declarative/qml/qdeclarativecompiledbindings.cpp index fbe5829..5c295b9 100644 --- a/src/declarative/qml/qdeclarativecompiledbindings.cpp +++ b/src/declarative/qml/qdeclarativecompiledbindings.cpp @@ -187,7 +187,6 @@ public: // Inherited from QDeclarativeAbstractBinding virtual void setEnabled(bool, QDeclarativePropertyPrivate::WriteFlags flags); - virtual int propertyIndex(); virtual void update(QDeclarativePropertyPrivate::WriteFlags flags); virtual void destroy(); @@ -294,14 +293,6 @@ QDeclarativeAbstractBinding *QDeclarativeCompiledBindings::configBinding(int ind void QDeclarativeCompiledBindingsPrivate::Binding::setEnabled(bool e, QDeclarativePropertyPrivate::WriteFlags flags) { - if (e) { - addToObject(target); - } else { - removeFromObject(); - } - - QDeclarativeAbstractBinding::setEnabled(e, flags); - if (enabled != e) { enabled = e; @@ -309,11 +300,6 @@ void QDeclarativeCompiledBindingsPrivate::Binding::setEnabled(bool e, QDeclarati } } -int QDeclarativeCompiledBindingsPrivate::Binding::propertyIndex() -{ - return property & 0xFFFF; -} - void QDeclarativeCompiledBindingsPrivate::Binding::update(QDeclarativePropertyPrivate::WriteFlags flags) { parent->run(this, flags); diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp index 645402e..df428b1 100644 --- a/src/declarative/qml/qdeclarativecompiler.cpp +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -946,6 +946,16 @@ void QDeclarativeCompiler::genObject(QDeclarativeParser::Object *obj) QDeclarativePropertyCache::Data::IsVMEFunction, QDeclarativePropertyCache::Data::IsVMESignal); + // Add flag for alias properties + if (!obj->synthdata.isEmpty()) { + const QDeclarativeVMEMetaData *vmeMetaData = + reinterpret_cast<const QDeclarativeVMEMetaData *>(obj->synthdata.constData()); + for (int ii = 0; ii < vmeMetaData->aliasCount; ++ii) { + int index = obj->metaObject()->propertyOffset() + vmeMetaData->propertyCount + ii; + propertyCache->property(index)->flags |= QDeclarativePropertyCache::Data::IsAlias; + } + } + if (obj == unitRoot) { propertyCache->addref(); output->rootPropertyCache = propertyCache; @@ -1003,7 +1013,8 @@ void QDeclarativeCompiler::genObjectBody(QDeclarativeParser::Object *obj) seenDefer = true; continue; } - genValueProperty(prop, obj); + if (!prop->isAlias) + genValueProperty(prop, obj); } if (seenDefer) { QDeclarativeInstruction defer; @@ -1113,25 +1124,56 @@ void QDeclarativeCompiler::genObjectBody(QDeclarativeParser::Object *obj) } foreach(Property *prop, obj->valueTypeProperties) { - QDeclarativeInstruction fetch; - fetch.type = QDeclarativeInstruction::FetchValueType; - fetch.fetchValue.property = prop->index; - fetch.fetchValue.type = prop->type; - fetch.line = prop->location.start.line; + if (!prop->isAlias) + genValueTypeProperty(obj, prop); + } - output->bytecode << fetch; + foreach(Property *prop, obj->valueProperties) { + if (prop->isDeferred) + continue; + if (prop->isAlias) + genValueProperty(prop, obj); + } + + foreach(Property *prop, obj->valueTypeProperties) { + if (prop->isAlias) + genValueTypeProperty(obj, prop); + } +} +void QDeclarativeCompiler::genValueTypeProperty(QDeclarativeParser::Object *obj,QDeclarativeParser::Property *prop) +{ + QDeclarativeInstruction fetch; + fetch.type = QDeclarativeInstruction::FetchValueType; + fetch.fetchValue.property = prop->index; + fetch.fetchValue.type = prop->type; + fetch.fetchValue.bindingSkipList = 0; + fetch.line = prop->location.start.line; + + if (obj->type == -1 || output->types.at(obj->type).component) { + // We only have to do this if this is a composite type. If it is a builtin + // type it can't possibly already have bindings that need to be cleared. foreach(Property *vprop, prop->value->valueProperties) { - genPropertyAssignment(vprop, prop->value, prop); + if (!vprop->values.isEmpty()) { + Q_ASSERT(vprop->index >= 0 && vprop->index < 32); + fetch.fetchValue.bindingSkipList |= (1 << vprop->index); + } } + } - QDeclarativeInstruction pop; - pop.type = QDeclarativeInstruction::PopValueType; - pop.fetchValue.property = prop->index; - pop.fetchValue.type = prop->type; - pop.line = prop->location.start.line; - output->bytecode << pop; + output->bytecode << fetch; + + foreach(Property *vprop, prop->value->valueProperties) { + genPropertyAssignment(vprop, prop->value, prop); } + + QDeclarativeInstruction pop; + pop.type = QDeclarativeInstruction::PopValueType; + pop.fetchValue.property = prop->index; + pop.fetchValue.type = prop->type; + pop.fetchValue.bindingSkipList = 0; + pop.line = prop->location.start.line; + output->bytecode << pop; } void QDeclarativeCompiler::genComponent(QDeclarativeParser::Object *obj) @@ -1440,10 +1482,22 @@ bool QDeclarativeCompiler::buildProperty(QDeclarativeParser::Property *prop, if (p.name()) { prop->type = p.userType(); } - } - if (prop->index != -1) - prop->parent->setBindingBit(prop->index); + // Check if this is an alias + if (prop->index != -1 && + prop->parent && + prop->parent->type != -1 && + output->types.at(prop->parent->type).component) { + + QDeclarativePropertyCache *cache = output->types.at(prop->parent->type).component->rootPropertyCache; + if (cache && cache->property(prop->index) && + cache->property(prop->index)->flags & QDeclarativePropertyCache::Data::IsAlias) + prop->isAlias = true; + } + + if (prop->index != -1 && !prop->values.isEmpty()) + prop->parent->setBindingBit(prop->index); + } if (!prop->isDefault && prop->name == "id" && !ctxt.isSubContext()) { @@ -1778,6 +1832,12 @@ bool QDeclarativeCompiler::buildGroupedProperty(QDeclarativeParser::Property *pr COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop->name))); } + + if (prop->isAlias) { + foreach (Property *vtProp, prop->value->properties) + vtProp->isAlias = true; + } + COMPILE_CHECK(buildValueTypeProperty(enginePrivate->valueTypes[prop->type], prop->value, obj, ctxt.incr())); obj->addValueTypeProperty(prop); @@ -2423,7 +2483,7 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeParser::Object *obj, Dyn if (p.type == Object::DynamicProperty::Alias) { if (mode == ResolveAliases) { ((QDeclarativeVMEMetaData *)dynamicData.data())->aliasCount++; - compileAlias(builder, dynamicData, obj, p); + COMPILE_CHECK(compileAlias(builder, dynamicData, obj, p)); } else { // Need a fake signal so that the metaobject remains consistent across // the resolve and non-resolve alias runs @@ -2694,7 +2754,10 @@ void QDeclarativeCompiler::genBindingAssignment(QDeclarativeParser::Value *bindi } QDeclarativeInstruction store; - store.type = QDeclarativeInstruction::StoreBinding; + if (!prop->isAlias) + store.type = QDeclarativeInstruction::StoreBinding; + else + store.type = QDeclarativeInstruction::StoreBindingOnAlias; store.assignBinding.value = output->indexForByteArray(ref.compiledData); store.assignBinding.context = ref.bindingContext.stack; store.assignBinding.owner = ref.bindingContext.owner; @@ -2769,13 +2832,17 @@ bool QDeclarativeCompiler::completeComponentBuild() expr.expression = binding.expression; expr.imports = unit->imports(); - int index = bindingCompiler.compile(expr, enginePrivate); - if (index != -1) { - binding.dataType = BindingReference::Experimental; - binding.compiledIndex = index; - componentStat.optimizedBindings.append(iter.key()->location); - continue; - } + // ### We don't currently optimize for bindings on alias's - because + // of the solution to QTBUG-13719 + if (!binding.property->isAlias) { + int index = bindingCompiler.compile(expr, enginePrivate); + if (index != -1) { + binding.dataType = BindingReference::Experimental; + binding.compiledIndex = index; + componentStat.optimizedBindings.append(iter.key()->location); + continue; + } + } binding.dataType = BindingReference::QtScript; diff --git a/src/declarative/qml/qdeclarativecompiler_p.h b/src/declarative/qml/qdeclarativecompiler_p.h index 5cd1fd2..7d76ad9 100644 --- a/src/declarative/qml/qdeclarativecompiler_p.h +++ b/src/declarative/qml/qdeclarativecompiler_p.h @@ -252,6 +252,7 @@ private: void genObject(QDeclarativeParser::Object *obj); void genObjectBody(QDeclarativeParser::Object *obj); + void genValueTypeProperty(QDeclarativeParser::Object *obj,QDeclarativeParser::Property *); void genComponent(QDeclarativeParser::Object *obj); void genValueProperty(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj); void genListProperty(QDeclarativeParser::Property *prop, QDeclarativeParser::Object *obj); diff --git a/src/declarative/qml/qdeclarativecomponent.cpp b/src/declarative/qml/qdeclarativecomponent.cpp index 2686ce3..63bde0f 100644 --- a/src/declarative/qml/qdeclarativecomponent.cpp +++ b/src/declarative/qml/qdeclarativecomponent.cpp @@ -876,9 +876,12 @@ void QDeclarativeComponentPrivate::complete(QDeclarativeEnginePrivate *enginePri QDeclarativeEnginePrivate::SimpleList<QDeclarativeAbstractBinding> bv = state->bindValues.at(ii); for (int jj = 0; jj < bv.count; ++jj) { - if(bv.at(jj)) + if(bv.at(jj)) { + // XXX akennedy + bv.at(jj)->m_mePtr = 0; bv.at(jj)->setEnabled(true, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } } QDeclarativeEnginePrivate::clear(bv); } diff --git a/src/declarative/qml/qdeclarativeinstruction.cpp b/src/declarative/qml/qdeclarativeinstruction.cpp index 1767d2f..818c15d 100644 --- a/src/declarative/qml/qdeclarativeinstruction.cpp +++ b/src/declarative/qml/qdeclarativeinstruction.cpp @@ -63,7 +63,7 @@ void QDeclarativeCompiledData::dump(QDeclarativeInstruction *instr, int idx) qWarning().nospace() << idx << "\t\t" << line << "\t" << "INIT\t\t\t" << instr->init.bindingsSize << "\t" << instr->init.parserStatusSize << "\t" << instr->init.contextCache << "\t" << instr->init.compiledBinding; break; case QDeclarativeInstruction::CreateObject: - qWarning().nospace() << idx << "\t\t" << line << "\t" << "CREATE\t\t\t" << instr->create.type << "\t\t\t" << types.at(instr->create.type).className; + qWarning().nospace() << idx << "\t\t" << line << "\t" << "CREATE\t\t\t" << instr->create.type << "\t" << instr->create.bindingBits << "\t\t" << types.at(instr->create.type).className; break; case QDeclarativeInstruction::CreateSimpleObject: qWarning().nospace() << idx << "\t\t" << line << "\t" << "CREATE_SIMPLE\t\t" << instr->createSimple.typeSize; @@ -174,6 +174,9 @@ void QDeclarativeCompiledData::dump(QDeclarativeInstruction *instr, int idx) case QDeclarativeInstruction::StoreBinding: qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_BINDING\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; break; + case QDeclarativeInstruction::StoreBindingOnAlias: + qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_BINDING_ALIAS\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; + break; case QDeclarativeInstruction::StoreCompiledBinding: qWarning().nospace() << idx << "\t\t" << line << "\t" << "STORE_COMPILED_BINDING\t" << instr->assignBinding.property << "\t" << instr->assignBinding.value << "\t" << instr->assignBinding.context; break; @@ -203,7 +206,7 @@ void QDeclarativeCompiledData::dump(QDeclarativeInstruction *instr, int idx) qWarning().nospace() << idx << "\t\t" << line << "\t" << "FETCH\t\t\t" << instr->fetch.property; break; case QDeclarativeInstruction::FetchValueType: - qWarning().nospace() << idx << "\t\t" << line << "\t" << "FETCH_VALUE\t\t" << instr->fetchValue.property << "\t" << instr->fetchValue.type; + qWarning().nospace() << idx << "\t\t" << line << "\t" << "FETCH_VALUE\t\t" << instr->fetchValue.property << "\t" << instr->fetchValue.type << "\t" << instr->fetchValue.bindingSkipList; break; case QDeclarativeInstruction::PopFetchedObject: qWarning().nospace() << idx << "\t\t" << line << "\t" << "POP"; diff --git a/src/declarative/qml/qdeclarativeinstruction_p.h b/src/declarative/qml/qdeclarativeinstruction_p.h index f0b032c..94676fc 100644 --- a/src/declarative/qml/qdeclarativeinstruction_p.h +++ b/src/declarative/qml/qdeclarativeinstruction_p.h @@ -132,6 +132,7 @@ public: AssignCustomType, /* assignCustomType */ StoreBinding, /* assignBinding */ + StoreBindingOnAlias, /* assignBinding */ StoreCompiledBinding, /* assignBinding */ StoreValueSource, /* assignValueSource */ StoreValueInterceptor, /* assignValueInterceptor */ @@ -214,6 +215,7 @@ public: struct FetchValueInstruction { int property; int type; + quint32 bindingSkipList; }; struct FetchQmlListInstruction { int property; diff --git a/src/declarative/qml/qdeclarativeparser.cpp b/src/declarative/qml/qdeclarativeparser.cpp index 8d00ef8..effecb1 100644 --- a/src/declarative/qml/qdeclarativeparser.cpp +++ b/src/declarative/qml/qdeclarativeparser.cpp @@ -205,13 +205,13 @@ QDeclarativeParser::Object::DynamicSlot::DynamicSlot(const DynamicSlot &o) QDeclarativeParser::Property::Property() : parent(0), type(0), index(-1), value(0), isDefault(true), isDeferred(false), - isValueTypeSubProperty(false) + isValueTypeSubProperty(false), isAlias(false) { } QDeclarativeParser::Property::Property(const QByteArray &n) : parent(0), type(0), index(-1), value(0), name(n), isDefault(false), - isDeferred(false), isValueTypeSubProperty(false) + isDeferred(false), isValueTypeSubProperty(false), isAlias(false) { } diff --git a/src/declarative/qml/qdeclarativeparser_p.h b/src/declarative/qml/qdeclarativeparser_p.h index 77184c2..633847d 100644 --- a/src/declarative/qml/qdeclarativeparser_p.h +++ b/src/declarative/qml/qdeclarativeparser_p.h @@ -358,6 +358,9 @@ namespace QDeclarativeParser bool isDeferred; // True if this property is a value-type pseudo-property bool isValueTypeSubProperty; + // True if this property is a property alias. Set by the + // QDeclarativeCompiler + bool isAlias; LocationSpan location; LocationRange listValueRange; diff --git a/src/declarative/qml/qdeclarativeproperty.cpp b/src/declarative/qml/qdeclarativeproperty.cpp index 1395e97..df0917f 100644 --- a/src/declarative/qml/qdeclarativeproperty.cpp +++ b/src/declarative/qml/qdeclarativeproperty.cpp @@ -607,26 +607,7 @@ QDeclarativePropertyPrivate::binding(const QDeclarativeProperty &that) if (!that.isProperty() || !that.d->object) return 0; - QDeclarativeData *data = QDeclarativeData::get(that.d->object); - if (!data) - return 0; - - if (!data->hasBindingBit(that.d->core.coreIndex)) - return 0; - - QDeclarativeAbstractBinding *binding = data->bindings; - while (binding && binding->propertyIndex() != that.d->core.coreIndex) - binding = binding->m_nextBinding; - - if (binding && that.d->valueType.valueTypeCoreIdx != -1) { - if (binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) { - QDeclarativeValueTypeProxyBinding *proxy = static_cast<QDeclarativeValueTypeProxyBinding *>(binding); - - binding = proxy->binding(bindingIndex(that)); - } - } - - return binding; + return binding(that.d->object, that.d->core.coreIndex, that.d->valueType.valueTypeCoreIdx); } /*! @@ -658,12 +639,106 @@ QDeclarativePropertyPrivate::setBinding(const QDeclarativeProperty &that, } QDeclarativeAbstractBinding * +QDeclarativePropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex) +{ + QDeclarativeData *data = QDeclarativeData::get(object); + if (!data) + return 0; + + QDeclarativePropertyCache::Data *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { + const QDeclarativeVMEMetaObject *vme = + static_cast<const QDeclarativeVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); + + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) + return 0; + + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + return binding(aObject, aCoreIndex, (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex); + } + + if (!data->hasBindingBit(coreIndex)) + return 0; + + QDeclarativeAbstractBinding *binding = data->bindings; + while (binding && binding->propertyIndex() != coreIndex) + binding = binding->m_nextBinding; + + if (binding && valueTypeIndex != -1) { + if (binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) { + int index = coreIndex | (valueTypeIndex << 24); + binding = static_cast<QDeclarativeValueTypeProxyBinding *>(binding)->binding(index); + } + } + + return binding; +} + +void QDeclarativePropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, + QObject **targetObject, int *targetBindingIndex) +{ + int coreIndex = bindingIndex & 0xFFFFFF; + int valueTypeIndex = bindingIndex >> 24; + if (valueTypeIndex == 0) valueTypeIndex = -1; + + QDeclarativeData *data = QDeclarativeData::get(object, false); + if (data) { + QDeclarativePropertyCache::Data *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { + const QDeclarativeVMEMetaObject *vme = + static_cast<const QDeclarativeVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + + int aBindingIndex = aCoreIndex; + if (aValueTypeIndex != -1) + aBindingIndex |= aValueTypeIndex << 24; + else if (valueTypeIndex != -1) + aBindingIndex |= valueTypeIndex << 24; + + findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex); + return; + } + } + } + + *targetObject = object; + *targetBindingIndex = bindingIndex; +} + +QDeclarativeAbstractBinding * QDeclarativePropertyPrivate::setBinding(QObject *object, int coreIndex, int valueTypeIndex, QDeclarativeAbstractBinding *newBinding, WriteFlags flags) { QDeclarativeData *data = QDeclarativeData::get(object, 0 != newBinding); QDeclarativeAbstractBinding *binding = 0; + if (data) { + QDeclarativePropertyCache::Data *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { + const QDeclarativeVMEMetaObject *vme = + static_cast<const QDeclarativeVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); + + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { + if (newBinding) newBinding->destroy(); + return 0; + } + + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + return setBinding(aObject, aCoreIndex, (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex, + newBinding, flags); + } + } + if (data && data->hasBindingBit(coreIndex)) { binding = data->bindings; @@ -671,16 +746,72 @@ QDeclarativePropertyPrivate::setBinding(QObject *object, int coreIndex, int valu binding = binding->m_nextBinding; } - if (binding && valueTypeIndex != -1 && binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) { - int index = coreIndex | (valueTypeIndex << 24); + int index = coreIndex; + if (valueTypeIndex != -1) + index |= (valueTypeIndex << 24); + + if (binding && valueTypeIndex != -1 && binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) binding = static_cast<QDeclarativeValueTypeProxyBinding *>(binding)->binding(index); + + if (binding) { + binding->removeFromObject(); + binding->setEnabled(false, 0); } + if (newBinding) { + newBinding->addToObject(object, index); + newBinding->setEnabled(true, flags); + } + + return binding; +} + +QDeclarativeAbstractBinding * +QDeclarativePropertyPrivate::setBindingNoEnable(QObject *object, int coreIndex, int valueTypeIndex, + QDeclarativeAbstractBinding *newBinding) +{ + QDeclarativeData *data = QDeclarativeData::get(object, 0 != newBinding); + QDeclarativeAbstractBinding *binding = 0; + + if (data) { + QDeclarativePropertyCache::Data *propertyData = + data->propertyCache?data->propertyCache->property(coreIndex):0; + if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { + const QDeclarativeVMEMetaObject *vme = + static_cast<const QDeclarativeVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); + + QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; + if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { + if (newBinding) newBinding->destroy(); + return 0; + } + + // This will either be a value type sub-reference or an alias to a value-type sub-reference not both + Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); + return setBindingNoEnable(aObject, aCoreIndex, (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex, + newBinding); + } + } + + if (data && data->hasBindingBit(coreIndex)) { + binding = data->bindings; + + while (binding && binding->propertyIndex() != coreIndex) + binding = binding->m_nextBinding; + } + + int index = coreIndex; + if (valueTypeIndex != -1) + index |= (valueTypeIndex << 24); + + if (binding && valueTypeIndex != -1 && binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) + binding = static_cast<QDeclarativeValueTypeProxyBinding *>(binding)->binding(index); + if (binding) - binding->setEnabled(false); + binding->removeFromObject(); if (newBinding) - newBinding->setEnabled(true, flags); + newBinding->addToObject(object, index); return binding; } @@ -1392,11 +1523,26 @@ static inline int QMetaObject_methods(const QMetaObject *metaObject) int className; int classInfoCount, classInfoData; int methodCount, methodData; + int propertyCount, propertyData; }; return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount; } +static inline int QMetaObject_properties(const QMetaObject *metaObject) +{ + struct Private + { + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + int propertyCount, propertyData; + }; + + return reinterpret_cast<const Private *>(metaObject->d.data)->propertyCount; +} + static inline void flush_vme_signal(const QObject *object, int index) { QDeclarativeData *data = static_cast<QDeclarativeData *>(QObjectPrivate::get(const_cast<QObject *>(object))->declarativeData); @@ -1437,4 +1583,19 @@ bool QDeclarativePropertyPrivate::connect(const QObject *sender, int signal_inde return QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); } +/*! +Return \a metaObject's [super] meta object that provides data for \a property. +*/ +const QMetaObject *QDeclarativePropertyPrivate::metaObjectForProperty(const QMetaObject *metaObject, int property) +{ + int propertyOffset = metaObject->propertyOffset(); + + while (propertyOffset > property) { + metaObject = metaObject->d.superdata; + propertyOffset -= QMetaObject_properties(metaObject); + } + + return metaObject; +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/qdeclarativeproperty_p.h b/src/declarative/qml/qdeclarativeproperty_p.h index a9d6979..6392f88 100644 --- a/src/declarative/qml/qdeclarativeproperty_p.h +++ b/src/declarative/qml/qdeclarativeproperty_p.h @@ -68,7 +68,7 @@ class QDeclarativeExpression; class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativePropertyPrivate { public: - enum WriteFlag { BypassInterceptor = 0x01, DontRemoveBinding = 0x02 }; + enum WriteFlag { BypassInterceptor = 0x01, DontRemoveBinding = 0x02, RemoveBindingOnAliasWrite = 0x04 }; Q_DECLARE_FLAGS(WriteFlags, WriteFlag) QDeclarativePropertyPrivate() @@ -108,9 +108,13 @@ public: const QVariant &value, int flags); static bool write(QObject *, const QDeclarativePropertyCache::Data &, const QVariant &, QDeclarativeContextData *, WriteFlags flags = 0); + static void findAliasTarget(QObject *, int, QObject **, int *); static QDeclarativeAbstractBinding *setBinding(QObject *, int coreIndex, int valueTypeIndex /* -1 */, QDeclarativeAbstractBinding *, WriteFlags flags = DontRemoveBinding); + static QDeclarativeAbstractBinding *setBindingNoEnable(QObject *, int coreIndex, int valueTypeIndex /* -1 */, + QDeclarativeAbstractBinding *); + static QDeclarativeAbstractBinding *binding(QObject *, int coreIndex, int valueTypeIndex /* -1 */); static QByteArray saveValueType(const QMetaObject *, int, const QMetaObject *, int); @@ -120,7 +124,6 @@ public: static bool equal(const QMetaObject *, const QMetaObject *); static bool canConvert(const QMetaObject *from, const QMetaObject *to); - // "Public" (to QML) methods static QDeclarativeAbstractBinding *binding(const QDeclarativeProperty &that); static QDeclarativeAbstractBinding *setBinding(const QDeclarativeProperty &that, @@ -136,6 +139,7 @@ public: static bool connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type = 0, int *types = 0); + static const QMetaObject *metaObjectForProperty(const QMetaObject *, int); }; Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativePropertyPrivate::WriteFlags) diff --git a/src/declarative/qml/qdeclarativepropertycache_p.h b/src/declarative/qml/qdeclarativepropertycache_p.h index 922010d..f7c5daa 100644 --- a/src/declarative/qml/qdeclarativepropertycache_p.h +++ b/src/declarative/qml/qdeclarativepropertycache_p.h @@ -83,6 +83,7 @@ public: IsConstant = 0x00000001, IsWritable = 0x00000002, IsResettable = 0x00000004, + IsAlias = 0x00000008, // These are mutualy exclusive IsFunction = 0x00000010, diff --git a/src/declarative/qml/qdeclarativevaluetype.cpp b/src/declarative/qml/qdeclarativevaluetype.cpp index b9e9cec..5dc6ffd 100644 --- a/src/declarative/qml/qdeclarativevaluetype.cpp +++ b/src/declarative/qml/qdeclarativevaluetype.cpp @@ -109,36 +109,54 @@ void QDeclarativeValueTypeFactory::registerValueTypes() QDeclarativeValueType *QDeclarativeValueTypeFactory::valueType(int t) { + QDeclarativeValueType *rv = 0; + switch (t) { case QVariant::Point: - return new QDeclarativePointValueType; + rv = new QDeclarativePointValueType; + break; case QVariant::PointF: - return new QDeclarativePointFValueType; + rv = new QDeclarativePointFValueType; + break; case QVariant::Size: - return new QDeclarativeSizeValueType; + rv = new QDeclarativeSizeValueType; + break; case QVariant::SizeF: - return new QDeclarativeSizeFValueType; + rv = new QDeclarativeSizeFValueType; + break; case QVariant::Rect: - return new QDeclarativeRectValueType; + rv = new QDeclarativeRectValueType; + break; case QVariant::RectF: - return new QDeclarativeRectFValueType; + rv = new QDeclarativeRectFValueType; + break; case QVariant::Vector2D: - return new QDeclarativeVector2DValueType; + rv = new QDeclarativeVector2DValueType; + break; case QVariant::Vector3D: - return new QDeclarativeVector3DValueType; + rv = new QDeclarativeVector3DValueType; + break; case QVariant::Vector4D: - return new QDeclarativeVector4DValueType; + rv = new QDeclarativeVector4DValueType; + break; case QVariant::Quaternion: - return new QDeclarativeQuaternionValueType; + rv = new QDeclarativeQuaternionValueType; + break; case QVariant::Matrix4x4: - return new QDeclarativeMatrix4x4ValueType; + rv = new QDeclarativeMatrix4x4ValueType; + break; case QVariant::EasingCurve: - return new QDeclarativeEasingValueType; + rv = new QDeclarativeEasingValueType; + break; case QVariant::Font: - return new QDeclarativeFontValueType; + rv = new QDeclarativeFontValueType; + break; default: - return 0; + break; } + + Q_ASSERT(!rv || rv->metaObject()->propertyCount() < 32); + return rv; } QDeclarativeValueType::QDeclarativeValueType(QObject *parent) diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp index db90aff..c742dec 100644 --- a/src/declarative/qml/qdeclarativevme.cpp +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -154,7 +154,8 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(ctxt->engine); int status = -1; //for dbus - QDeclarativePropertyPrivate::WriteFlags flags = QDeclarativePropertyPrivate::BypassInterceptor; + QDeclarativePropertyPrivate::WriteFlags flags = QDeclarativePropertyPrivate::BypassInterceptor | + QDeclarativePropertyPrivate::RemoveBindingOnAliasWrite; for (int ii = start; !isError() && ii < (start + count); ++ii) { const QDeclarativeInstruction &instr = comp->bytecode.at(ii); @@ -664,6 +665,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, break; case QDeclarativeInstruction::StoreBinding: + case QDeclarativeInstruction::StoreBindingOnAlias: { QObject *target = stack.at(stack.count() - 1 - instr.assignBinding.owner); @@ -675,14 +677,20 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, int coreIndex = mp.index(); - if (stack.count() == 1 && bindingSkipList.testBit(coreIndex)) + if ((stack.count() - instr.assignBinding.owner) == 1 && bindingSkipList.testBit(coreIndex)) break; QDeclarativeBinding *bind = new QDeclarativeBinding((void *)datas.at(instr.assignBinding.value).constData(), comp, context, ctxt, comp->name, instr.line, 0); bindValues.append(bind); bind->m_mePtr = &bindValues.values[bindValues.count - 1]; bind->setTarget(mp); - bind->addToObject(target); + + if (instr.type == QDeclarativeInstruction::StoreBindingOnAlias) { + QDeclarativeAbstractBinding *old = QDeclarativePropertyPrivate::setBindingNoEnable(target, coreIndex, QDeclarativePropertyPrivate::valueTypeCoreIndex(mp), bind); + if (old) { old->destroy(); } + } else { + bind->addToObject(target, QDeclarativePropertyPrivate::bindingIndex(mp)); + } } break; @@ -701,7 +709,7 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, ctxt->optimizedBindings->configBinding(instr.assignBinding.value, target, scope, property); bindValues.append(binding); binding->m_mePtr = &bindValues.values[bindValues.count - 1]; - binding->addToObject(target); + binding->addToObject(target, property); } break; @@ -874,8 +882,26 @@ QObject *QDeclarativeVME::run(QDeclarativeVMEStack<QObject *> &stack, case QDeclarativeInstruction::FetchValueType: { QObject *target = stack.top(); - QDeclarativeValueType *valueHandler = - ep->valueTypes[instr.fetchValue.type]; + + if (instr.fetchValue.bindingSkipList != 0) { + // Possibly need to clear bindings + QDeclarativeData *targetData = QDeclarativeData::get(target); + if (targetData) { + QDeclarativeAbstractBinding *binding = + QDeclarativePropertyPrivate::binding(target, instr.fetchValue.property, -1); + + if (binding && binding->bindingType() != QDeclarativeAbstractBinding::ValueTypeProxy) { + QDeclarativePropertyPrivate::setBinding(target, instr.fetchValue.property, -1, 0); + binding->destroy(); + } else if (binding) { + QDeclarativeValueTypeProxyBinding *proxy = + static_cast<QDeclarativeValueTypeProxyBinding *>(binding); + proxy->removeBindings(instr.fetchValue.bindingSkipList); + } + } + } + + QDeclarativeValueType *valueHandler = ep->valueTypes[instr.fetchValue.type]; valueHandler->read(target, instr.fetchValue.property); stack.push(valueHandler); } diff --git a/src/declarative/qml/qdeclarativevmemetaobject.cpp b/src/declarative/qml/qdeclarativevmemetaobject.cpp index e28062b..38c1709 100644 --- a/src/declarative/qml/qdeclarativevmemetaobject.cpp +++ b/src/declarative/qml/qdeclarativevmemetaobject.cpp @@ -46,6 +46,7 @@ #include "qdeclarativeexpression.h" #include "private/qdeclarativeexpression_p.h" #include "private/qdeclarativecontext_p.h" +#include "private/qdeclarativebinding_p.h" Q_DECLARE_METATYPE(QScriptValue); @@ -589,7 +590,21 @@ int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) if (d->isObjectAlias()) { *reinterpret_cast<QObject **>(a[0]) = target; return -1; - } else if (d->isValueTypeAlias()) { + } + + // Remove binding (if any) on write + if(c == QMetaObject::WriteProperty) { + int flags = *reinterpret_cast<int*>(a[3]); + if (flags & QDeclarativePropertyPrivate::RemoveBindingOnAliasWrite) { + QDeclarativeData *targetData = QDeclarativeData::get(target); + if (targetData && targetData->hasBindingBit(d->propertyIndex())) { + QDeclarativeAbstractBinding *binding = QDeclarativePropertyPrivate::setBinding(target, d->propertyIndex(), d->isValueTypeAlias()?d->valueTypeIndex():-1, 0); + if (binding) binding->destroy(); + } + } + } + + if (d->isValueTypeAlias()) { // Value type property QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(ctxt->engine); @@ -821,6 +836,36 @@ void QDeclarativeVMEMetaObject::setVMEProperty(int index, const QScriptValue &v) return writeVarProperty(index - propOffset, v); } +bool QDeclarativeVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const +{ + Q_ASSERT(index >= propOffset + metaData->propertyCount); + + *target = 0; + *coreIndex = -1; + *valueTypeIndex = -1; + + if (!ctxt) + return false; + + QDeclarativeVMEMetaData::AliasData *d = metaData->aliasData() + (index - propOffset - metaData->propertyCount); + QDeclarativeContext *context = ctxt->asQDeclarativeContext(); + QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(context); + + *target = ctxtPriv->data->idValues[d->contextIdx].data(); + if (!*target) + return false; + + if (d->isObjectAlias()) { + } else if (d->isValueTypeAlias()) { + *coreIndex = d->propertyIndex(); + *valueTypeIndex = d->valueTypeIndex(); + } else { + *coreIndex = d->propertyIndex(); + } + + return true; +} + void QDeclarativeVMEMetaObject::connectAlias(int aliasId) { if (!aConnected.testBit(aliasId)) { diff --git a/src/declarative/qml/qdeclarativevmemetaobject_p.h b/src/declarative/qml/qdeclarativevmemetaobject_p.h index 5134763..7b6b410 100644 --- a/src/declarative/qml/qdeclarativevmemetaobject_p.h +++ b/src/declarative/qml/qdeclarativevmemetaobject_p.h @@ -138,6 +138,7 @@ public: QDeclarativeCompiledData *compiledData); ~QDeclarativeVMEMetaObject(); + bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const; void registerInterceptor(int index, int valueIndex, QDeclarativePropertyValueInterceptor *interceptor); QScriptValue vmeMethod(int index); int vmeMethodLineNumber(int index); @@ -146,6 +147,7 @@ public: void setVMEProperty(int index, const QScriptValue &); void connectAliasSignal(int index); + protected: virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/AliasBindingsAssignCorrectlyType.qml b/tests/auto/declarative/qdeclarativeecmascript/data/AliasBindingsAssignCorrectlyType.qml new file mode 100644 index 0000000..0eda67d --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/AliasBindingsAssignCorrectlyType.qml @@ -0,0 +1,9 @@ +import QtQuick 1.0 + +QtObject { + id: root + + property real realProperty + property alias aliasProperty: root.realProperty +} + diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/AliasBindingsOverrideTargetType.qml b/tests/auto/declarative/qdeclarativeecmascript/data/AliasBindingsOverrideTargetType.qml new file mode 100644 index 0000000..f539fb6 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/AliasBindingsOverrideTargetType.qml @@ -0,0 +1,14 @@ +import QtQuick 1.0 +import Qt.test 1.0 + +MyTypeObject { + id: root + + property int data: 7 + + property int targetProperty: root.data * 43 - root.data + property alias aliasProperty: root.targetProperty + + pointProperty: Qt.point(data, data); + property alias pointAliasProperty: root.pointProperty +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/AliasBindingsOverrideTargetType3.qml b/tests/auto/declarative/qdeclarativeecmascript/data/AliasBindingsOverrideTargetType3.qml new file mode 100644 index 0000000..a4b0527 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/AliasBindingsOverrideTargetType3.qml @@ -0,0 +1,9 @@ +import QtQuick 1.0 + +QtObject { + id: root + + property int testProperty + property alias aliasProperty: root.testProperty +} + diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsAssignCorrectly.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsAssignCorrectly.qml new file mode 100644 index 0000000..f0808c4 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsAssignCorrectly.qml @@ -0,0 +1,59 @@ +import QtQuick 1.0 + +Item { + id: root + + property bool test: false + + property real testData: 9 + property real testData2: 9 + + states: State { + name: "change" + PropertyChanges { + target: myType + realProperty: if (testData2 > 3) 9; else 11; + } + } + + AliasBindingsAssignCorrectlyType { + id: myType + + aliasProperty: if (testData > 3) 14; else 12; + } + + Component.onCompleted: { + // Check original binding works + if (myType.aliasProperty != 14) return; + + testData = 2; + if (myType.aliasProperty != 12) return; + + // Change binding indirectly by modifying the "realProperty" + root.state = "change"; + if (myType.aliasProperty != 9) return; + + // Check the new binding works + testData2 = 1; + if (myType.aliasProperty != 11) return; + + // Try and trigger the old binding (that should have been removed) + testData = 6; + if (myType.aliasProperty != 11) return; + + // Restore the original binding + root.state = ""; + if (myType.aliasProperty != 14) return; + + // Test the restored binding works + testData = 0; + if (myType.aliasProperty != 12) return; + + // Test the old binding isn't somehow hanging around and still in effect + testData2 = 13; + if (myType.aliasProperty != 12) return; + + test = true; + } +} + diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsOverrideTarget.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsOverrideTarget.2.qml new file mode 100644 index 0000000..4f07cbf --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsOverrideTarget.2.qml @@ -0,0 +1,29 @@ +import QtQuick 1.0 + +Item { + id: me + property bool test: false + + property int value: 9 + + AliasBindingsOverrideTargetType { + id: aliasType + pointAliasProperty.x: me.value + } + + Component.onCompleted: { + if (aliasType.pointAliasProperty.x != 9) return; + + me.value = 11; + if (aliasType.pointAliasProperty.x != 11) return; + + aliasType.data = 8; + if (aliasType.pointAliasProperty.x != 11) return; + + me.value = 4; + if (aliasType.pointAliasProperty.x != 4) return; + + test = true; + } +} + diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsOverrideTarget.3.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsOverrideTarget.3.qml new file mode 100644 index 0000000..937ae91 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsOverrideTarget.3.qml @@ -0,0 +1,24 @@ +import QtQuick 1.0 + +Item { + id: root + property bool test: false; + + property int value1: 10 + property int value2: 11 + + AliasBindingsOverrideTargetType3 { + id: obj + + testProperty: root.value1 * 9 + aliasProperty: root.value2 * 10 + } + + Component.onCompleted: { + if (obj.testProperty != 110) return; + if (obj.aliasProperty != 110) return; + + test = true; + } +} + diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsOverrideTarget.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsOverrideTarget.qml new file mode 100644 index 0000000..a01dc5b --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasBindingsOverrideTarget.qml @@ -0,0 +1,28 @@ +import QtQuick 1.0 + +Item { + id: me + property bool test: false + + property int value: 9 + + AliasBindingsOverrideTargetType { + id: aliasType + aliasProperty: me.value + } + + Component.onCompleted: { + if (aliasType.aliasProperty != 9) return; + + me.value = 11; + if (aliasType.aliasProperty != 11) return; + + aliasType.data = 8; + if (aliasType.aliasProperty != 11) return; + + me.value = 4; + if (aliasType.aliasProperty != 4) return; + + test = true; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasWritesOverrideBindings.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasWritesOverrideBindings.2.qml new file mode 100644 index 0000000..5bf9f6a --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasWritesOverrideBindings.2.qml @@ -0,0 +1,29 @@ +import QtQuick 1.0 + +Item { + id: me + property bool test: false + + property int value: 9 + + AliasBindingsOverrideTargetType { + id: aliasType + } + + Component.onCompleted: { + if (aliasType.aliasProperty != 294) return; + + aliasType.data = 8; + if (aliasType.aliasProperty != 336) return; + + aliasType.aliasProperty = 4; + if (aliasType.aliasProperty != 4) return; + + aliasType.data = 7; + if (aliasType.aliasProperty != 4) return; + + test = true; + } +} + + diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasWritesOverrideBindings.3.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasWritesOverrideBindings.3.qml new file mode 100644 index 0000000..a23ad4a --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasWritesOverrideBindings.3.qml @@ -0,0 +1,23 @@ +import QtQuick 1.0 + +Item { + id: me + property bool test: false + + property int value: 9 + + AliasBindingsOverrideTargetType { + id: aliasType + pointAliasProperty.x: 9 + } + + Component.onCompleted: { + if (aliasType.pointAliasProperty.x != 9) return; + + aliasType.data = 8; + if (aliasType.pointAliasProperty.x != 9) return; + + test = true; + } +} + diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/aliasWritesOverrideBindings.qml b/tests/auto/declarative/qdeclarativeecmascript/data/aliasWritesOverrideBindings.qml new file mode 100644 index 0000000..ac20371 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/aliasWritesOverrideBindings.qml @@ -0,0 +1,23 @@ +import QtQuick 1.0 + +Item { + id: me + property bool test: false + + property int value: 9 + + AliasBindingsOverrideTargetType { + id: aliasType + aliasProperty: 11 + } + + Component.onCompleted: { + if (aliasType.aliasProperty != 11) return; + + aliasType.data = 8; + if (aliasType.aliasProperty != 11) return; + + test = true; + } +} + diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/writeRemovesBinding.qml b/tests/auto/declarative/qdeclarativeecmascript/data/writeRemovesBinding.qml new file mode 100644 index 0000000..035f037 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/writeRemovesBinding.qml @@ -0,0 +1,46 @@ +import QtQuick 1.0 + +QtObject { + id: root + + property bool test: false + + property real data: 9 + property real binding: data + + property alias aliasProperty: root.aliasBinding + property real aliasBinding: data + + Component.onCompleted: { + // Non-aliased properties + if (binding != 9) return; + + data = 11; + if (binding != 11) return; + + binding = 6; + if (binding != 6) return; + + data = 3; + if (binding != 6) return; + + + // Writing through an aliased property + if (aliasProperty != 3) return; + if (aliasBinding != 3) return; + + data = 4; + if (aliasProperty != 4) return; + if (aliasBinding != 4) return; + + aliasProperty = 19; + if (aliasProperty != 19) return; + if (aliasBinding != 19) return; + + data = 5; + if (aliasProperty != 19) return; + if (aliasBinding != 19) return; + + test = true; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp index 652404c..14755f32 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -164,6 +164,10 @@ private slots: void in(); void sharedAttachedObject(); void objectName(); + void writeRemovesBinding(); + void aliasBindingsAssignCorrectly(); + void aliasBindingsOverrideTarget(); + void aliasWritesOverrideBindings(); void include(); @@ -741,11 +745,9 @@ void tst_qdeclarativeecmascript::constantsOverrideBindings() QVERIFY(object != 0); QCOMPARE(object->property("c1").toInt(), 0); - QEXPECT_FAIL("", "QTBUG-13719", Continue); QCOMPARE(object->property("c3").toInt(), 10); object->setProperty("c1", QVariant(9)); QCOMPARE(object->property("c1").toInt(), 9); - QEXPECT_FAIL("", "QTBUG-13719", Continue); QCOMPARE(object->property("c3").toInt(), 10); } } @@ -2671,6 +2673,97 @@ void tst_qdeclarativeecmascript::objectName() delete o; } +void tst_qdeclarativeecmascript::writeRemovesBinding() +{ + QDeclarativeComponent component(&engine, TEST_FILE("writeRemovesBinding.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; +} + +// Test bindings assigned to alias properties actually assign to the alias' target +void tst_qdeclarativeecmascript::aliasBindingsAssignCorrectly() +{ + QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsAssignCorrectly.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; +} + +// Test bindings assigned to alias properties override a binding on the target (QTBUG-13719) +void tst_qdeclarativeecmascript::aliasBindingsOverrideTarget() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.2.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasBindingsOverrideTarget.3.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } +} + +// Test that writes to alias properties override bindings on the alias target (QTBUG-13719) +void tst_qdeclarativeecmascript::aliasWritesOverrideBindings() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.2.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("aliasWritesOverrideBindings.3.qml")); + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test").toBool(), true); + + delete o; + } +} + QTEST_MAIN(tst_qdeclarativeecmascript) #include "tst_qdeclarativeecmascript.moc" diff --git a/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp b/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp index db1f191..4470d65 100644 --- a/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp +++ b/tests/auto/declarative/qdeclarativeinstruction/tst_qdeclarativeinstruction.cpp @@ -458,6 +458,7 @@ void tst_qdeclarativeinstruction::dump() i.type = QDeclarativeInstruction::FetchValueType; i.fetchValue.property = 34; i.fetchValue.type = 6; + i.fetchValue.bindingSkipList = 7; data->bytecode << i; } @@ -538,7 +539,7 @@ void tst_qdeclarativeinstruction::dump() << "Index\tLine\tOperation\t\tData1\tData2\tData3\tComments" << "-------------------------------------------------------------------------------" << "0\t\t0\tINIT\t\t\t0\t3\t-1\t-1" - << "1\t\t1\tCREATE\t\t\t0\t\t\t\"Test\"" + << "1\t\t1\tCREATE\t\t\t0\t-1\t\t\"Test\"" << "2\t\t2\tSETID\t\t\t0\t\t\t\"testId\"" << "3\t\t3\tSET_DEFAULT" << "4\t\t4\tCREATE_COMPONENT\t3" @@ -578,7 +579,7 @@ void tst_qdeclarativeinstruction::dump() << "38\t\t40\tFETCH_ATTACHED\t\t23" << "39\t\t42\tFETCH_QLIST\t\t32" << "40\t\t43\tFETCH\t\t\t33" - << "41\t\t44\tFETCH_VALUE\t\t34\t6" + << "41\t\t44\tFETCH_VALUE\t\t34\t6\t7" << "42\t\t45\tPOP" << "43\t\t46\tPOP_QLIST" << "44\t\t47\tPOP_VALUE\t\t35\t8" diff --git a/tests/auto/declarative/qdeclarativeproperty/data/aliasPropertyBindings.qml b/tests/auto/declarative/qdeclarativeproperty/data/aliasPropertyBindings.qml new file mode 100644 index 0000000..a253a58 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeproperty/data/aliasPropertyBindings.qml @@ -0,0 +1,19 @@ +import QtQuick 1.0 + +Item { + id: root + + property real test: 9 + property real test2: 3 + + property real realProperty: test * test + test + property alias aliasProperty: root.realProperty + + states: State { + name: "switch" + PropertyChanges { + target: root + aliasProperty: 32 * test2 + } + } +} diff --git a/tests/auto/declarative/qdeclarativeproperty/tst_qdeclarativeproperty.cpp b/tests/auto/declarative/qdeclarativeproperty/tst_qdeclarativeproperty.cpp index 0ca0e03..3cc71bb 100644 --- a/tests/auto/declarative/qdeclarativeproperty/tst_qdeclarativeproperty.cpp +++ b/tests/auto/declarative/qdeclarativeproperty/tst_qdeclarativeproperty.cpp @@ -132,6 +132,7 @@ private slots: // Bugs void crashOnValueProperty(); + void aliasPropertyBindings(); void copy(); private: @@ -1308,6 +1309,74 @@ void tst_qdeclarativeproperty::crashOnValueProperty() QCOMPARE(p.read(), QVariant(20)); } +// QTBUG-13719 +void tst_qdeclarativeproperty::aliasPropertyBindings() +{ + QDeclarativeComponent component(&engine, TEST_FILE("aliasPropertyBindings.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("realProperty").toReal(), 90.); + QCOMPARE(object->property("aliasProperty").toReal(), 90.); + + object->setProperty("test", 10); + + QCOMPARE(object->property("realProperty").toReal(), 110.); + QCOMPARE(object->property("aliasProperty").toReal(), 110.); + + QDeclarativeProperty realProperty(object, QLatin1String("realProperty")); + QDeclarativeProperty aliasProperty(object, QLatin1String("aliasProperty")); + + // Check there is a binding on these two properties + QVERIFY(QDeclarativePropertyPrivate::binding(realProperty) != 0); + QVERIFY(QDeclarativePropertyPrivate::binding(aliasProperty) != 0); + + // Check that its the same binding on these two properties + QCOMPARE(QDeclarativePropertyPrivate::binding(realProperty), + QDeclarativePropertyPrivate::binding(aliasProperty)); + + // Change the binding + object->setProperty("state", QString("switch")); + + QVERIFY(QDeclarativePropertyPrivate::binding(realProperty) != 0); + QVERIFY(QDeclarativePropertyPrivate::binding(aliasProperty) != 0); + QCOMPARE(QDeclarativePropertyPrivate::binding(realProperty), + QDeclarativePropertyPrivate::binding(aliasProperty)); + + QCOMPARE(object->property("realProperty").toReal(), 96.); + QCOMPARE(object->property("aliasProperty").toReal(), 96.); + + // Check the old binding really has not effect any more + object->setProperty("test", 4); + + QCOMPARE(object->property("realProperty").toReal(), 96.); + QCOMPARE(object->property("aliasProperty").toReal(), 96.); + + object->setProperty("test2", 9); + + QCOMPARE(object->property("realProperty").toReal(), 288.); + QCOMPARE(object->property("aliasProperty").toReal(), 288.); + + // Revert + object->setProperty("state", QString("")); + + QVERIFY(QDeclarativePropertyPrivate::binding(realProperty) != 0); + QVERIFY(QDeclarativePropertyPrivate::binding(aliasProperty) != 0); + QCOMPARE(QDeclarativePropertyPrivate::binding(realProperty), + QDeclarativePropertyPrivate::binding(aliasProperty)); + + QCOMPARE(object->property("realProperty").toReal(), 20.); + QCOMPARE(object->property("aliasProperty").toReal(), 20.); + + object->setProperty("test2", 3); + + QCOMPARE(object->property("realProperty").toReal(), 20.); + QCOMPARE(object->property("aliasProperty").toReal(), 20.); + + delete object; +} + void tst_qdeclarativeproperty::copy() { PropertyObject object; diff --git a/tests/auto/declarative/qdeclarativevaluetypes/data/BindingsSpliceCorrectlyType.qml b/tests/auto/declarative/qdeclarativevaluetypes/data/BindingsSpliceCorrectlyType.qml new file mode 100644 index 0000000..f625d08 --- /dev/null +++ b/tests/auto/declarative/qdeclarativevaluetypes/data/BindingsSpliceCorrectlyType.qml @@ -0,0 +1,7 @@ +import Test 1.0 + +MyTypeObject { + property bool boldProperty: false + + font.bold: boldProperty +} diff --git a/tests/auto/declarative/qdeclarativevaluetypes/data/BindingsSpliceCorrectlyType4.qml b/tests/auto/declarative/qdeclarativevaluetypes/data/BindingsSpliceCorrectlyType4.qml new file mode 100644 index 0000000..0bdccce --- /dev/null +++ b/tests/auto/declarative/qdeclarativevaluetypes/data/BindingsSpliceCorrectlyType4.qml @@ -0,0 +1,7 @@ +import Test 1.0 + +MyTypeObject { + property int dataProperty: 7 + + point: Qt.point(dataProperty, dataProperty) +} diff --git a/tests/auto/declarative/qdeclarativevaluetypes/data/BindingsSpliceCorrectlyType5.qml b/tests/auto/declarative/qdeclarativevaluetypes/data/BindingsSpliceCorrectlyType5.qml new file mode 100644 index 0000000..151c499 --- /dev/null +++ b/tests/auto/declarative/qdeclarativevaluetypes/data/BindingsSpliceCorrectlyType5.qml @@ -0,0 +1,7 @@ +import Test 1.0 + +MyTypeObject { + property int dataProperty: 7 + + point.x: dataProperty +} diff --git a/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.1.qml b/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.1.qml new file mode 100644 index 0000000..7012143 --- /dev/null +++ b/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.1.qml @@ -0,0 +1,29 @@ +import Test 1.0 +import QtQuick 1.0 + +BindingsSpliceCorrectlyType { + property bool test: false + + property bool italicProperty: false + + font.italic: italicProperty + + Component.onCompleted: { + // Test initial state + if (font.italic != false) return; + if (font.bold != false) return; + + // Test italic binding worked + italicProperty = true; + + if (font.italic != true) return; + if (font.bold != false) return; + + // Test bold binding worked + boldProperty = true; + if (font.italic != true) return; + if (font.bold != true) return; + + test = true; + } +} diff --git a/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.2.qml b/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.2.qml new file mode 100644 index 0000000..69dbcab --- /dev/null +++ b/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.2.qml @@ -0,0 +1,31 @@ +import Test 1.0 +import QtQuick 1.0 + +BindingsSpliceCorrectlyType { + property bool test: false + + property bool italicProperty: false + + font.italic: italicProperty + font.bold: false + + Component.onCompleted: { + // Test initial state + if (font.italic != false) return; + if (font.bold != false) return; + + // Test italic binding worked + italicProperty = true; + + if (font.italic != true) return; + if (font.bold != false) return; + + // Test bold binding was removed by constant write + boldProperty = true; + if (font.italic != true) return; + if (font.bold != false) return; + + test = true; + } +} + diff --git a/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.3.qml b/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.3.qml new file mode 100644 index 0000000..669feb9 --- /dev/null +++ b/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.3.qml @@ -0,0 +1,36 @@ +import Test 1.0 +import QtQuick 1.0 + +BindingsSpliceCorrectlyType { + property bool test: false + + property bool italicProperty: false + property bool boldProperty2: false + + font.italic: italicProperty + font.bold: boldProperty2 + + Component.onCompleted: { + // Test initial state + if (font.italic != false) return; + if (font.bold != false) return; + + // Test italic binding worked + italicProperty = true; + + if (font.italic != true) return; + if (font.bold != false) return; + + // Test bold binding was overridden + boldProperty = true; + if (font.italic != true) return; + if (font.bold != false) return; + + boldProperty2 = true; + if (font.italic != true) return; + if (font.bold != true) return; + + test = true; + } +} + diff --git a/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.4.qml b/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.4.qml new file mode 100644 index 0000000..f28584f --- /dev/null +++ b/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.4.qml @@ -0,0 +1,27 @@ +import Test 1.0 +import QtQuick 1.0 + +BindingsSpliceCorrectlyType4 { + property bool test: false + + property int dataProperty2: 8 + + point.x: dataProperty2 + + Component.onCompleted: { + if (point.x != 8) return; + if (point.y != 4) return; + + dataProperty = 9; + + if (point.x != 8) return; + if (point.y != 4) return; + + dataProperty2 = 13; + + if (point.x != 13) return; + if (point.y != 4) return; + + test = true; + } +} diff --git a/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.5.qml b/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.5.qml new file mode 100644 index 0000000..1214c83 --- /dev/null +++ b/tests/auto/declarative/qdeclarativevaluetypes/data/bindingsSpliceCorrectly.5.qml @@ -0,0 +1,27 @@ +import Test 1.0 +import QtQuick 1.0 + +BindingsSpliceCorrectlyType5 { + property bool test: false + + property int dataProperty2: 8 + + point: Qt.point(dataProperty2, dataProperty2); + + Component.onCompleted: { + if (point.x != 8) return; + if (point.y != 8) return; + + dataProperty = 9; + + if (point.x != 8) return; + if (point.y != 8) return; + + dataProperty2 = 13; + + if (point.x != 13) return; + if (point.y != 13) return; + + test = true; + } +} diff --git a/tests/auto/declarative/qdeclarativevaluetypes/tst_qdeclarativevaluetypes.cpp b/tests/auto/declarative/qdeclarativevaluetypes/tst_qdeclarativevaluetypes.cpp index a4819f3..c243a75 100644 --- a/tests/auto/declarative/qdeclarativevaluetypes/tst_qdeclarativevaluetypes.cpp +++ b/tests/auto/declarative/qdeclarativevaluetypes/tst_qdeclarativevaluetypes.cpp @@ -94,6 +94,7 @@ private slots: void conflictingBindings(); void returnValues(); void varAssignment(); + void bindingsSpliceCorrectly(); private: QDeclarativeEngine engine; @@ -942,6 +943,61 @@ void tst_qdeclarativevaluetypes::varAssignment() delete object; } +// Test bindings splice together correctly +void tst_qdeclarativevaluetypes::bindingsSpliceCorrectly() +{ + { + QDeclarativeComponent component(&engine, TEST_FILE("bindingsSpliceCorrectly.1.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("bindingsSpliceCorrectly.2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } + + + { + QDeclarativeComponent component(&engine, TEST_FILE("bindingsSpliceCorrectly.3.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("bindingsSpliceCorrectly.4.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } + + { + QDeclarativeComponent component(&engine, TEST_FILE("bindingsSpliceCorrectly.5.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; + } +} + QTEST_MAIN(tst_qdeclarativevaluetypes) #include "tst_qdeclarativevaluetypes.moc" |