Only signal list variable assignment once
Lists are internally cleared and each element is pushed to the back from the source list in QML. Use nonsignaling operations for clear and push and activate manually having performed the operations. Fixes: QTBUG-112208 Change-Id: I1a995905f3fa758e4cc8c39b8576db668d84a067 Reviewed-by: Mikołaj Boc <Mikolaj.Boc@qt.io>
This commit is contained in:
parent
528807e80c
commit
7656d4922c
|
@ -1549,18 +1549,26 @@ bool QQmlPropertyPrivate::write(
|
|||
if (valueMetaObject.isNull())
|
||||
return false;
|
||||
|
||||
QQmlListProperty<void> prop;
|
||||
QQmlListProperty<QObject> prop;
|
||||
property.readProperty(object, &prop);
|
||||
|
||||
if (!prop.clear)
|
||||
if (!prop.clear || !prop.append)
|
||||
return false;
|
||||
|
||||
prop.clear(&prop);
|
||||
const bool useNonsignalingListOps = prop.clear == &QQmlVMEMetaObject::list_clear
|
||||
&& prop.append == &QQmlVMEMetaObject::list_append;
|
||||
|
||||
auto propClear =
|
||||
useNonsignalingListOps ? &QQmlVMEMetaObject::list_clear_nosignal : prop.clear;
|
||||
auto propAppend =
|
||||
useNonsignalingListOps ? &QQmlVMEMetaObject::list_append_nosignal : prop.append;
|
||||
|
||||
propClear(&prop);
|
||||
|
||||
const auto doAppend = [&](QObject *o) {
|
||||
if (o && !QQmlMetaObject::canConvert(o, valueMetaObject))
|
||||
o = nullptr;
|
||||
prop.append(&prop, o);
|
||||
propAppend(&prop, o);
|
||||
};
|
||||
|
||||
if (variantMetaType == QMetaType::fromType<QQmlListReference>()) {
|
||||
|
@ -1574,6 +1582,10 @@ bool QQmlPropertyPrivate::write(
|
|||
} else if (!iterateQObjectContainer(variantMetaType, value.data(), doAppend)) {
|
||||
doAppend(QQmlMetaType::toQObject(value));
|
||||
}
|
||||
if (useNonsignalingListOps) {
|
||||
Q_ASSERT(QQmlVMEMetaObject::get(object));
|
||||
QQmlVMEResolvedList(&prop).activateSignal();
|
||||
}
|
||||
} else if (variantMetaType == propertyMetaType) {
|
||||
QVariant v = value;
|
||||
property.writeProperty(object, v.data(), flags);
|
||||
|
|
|
@ -25,100 +25,96 @@
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class ResolvedList
|
||||
QQmlVMEResolvedList::QQmlVMEResolvedList(QQmlListProperty<QObject> *prop)
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(ResolvedList)
|
||||
// see QQmlVMEMetaObject::metaCall for how this was constructed
|
||||
auto encodedIndex = quintptr(prop->data);
|
||||
constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT;
|
||||
quintptr inheritanceDepth = encodedIndex >> (usableBits / 2);
|
||||
m_id = encodedIndex & ((quintptr(1) << (usableBits / 2)) - 1);
|
||||
|
||||
public:
|
||||
ResolvedList(QQmlListProperty<QObject> *prop)
|
||||
{
|
||||
// see QQmlVMEMetaObject::metaCall for how this was constructed
|
||||
auto encodedIndex = quintptr(prop->data);
|
||||
constexpr quintptr usableBits = sizeof(quintptr) * CHAR_BIT;
|
||||
quintptr inheritanceDepth = encodedIndex >> (usableBits / 2);
|
||||
m_id = encodedIndex & ((quintptr(1) << (usableBits / 2)) - 1);
|
||||
// walk up to the correct meta object if necessary
|
||||
auto mo = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(prop->object)->metaObject);
|
||||
while (inheritanceDepth--)
|
||||
mo = mo->parentVMEMetaObject();
|
||||
m_metaObject = mo;
|
||||
Q_ASSERT(m_metaObject);
|
||||
Q_ASSERT(::strstr(m_metaObject->toDynamicMetaObject(prop->object)
|
||||
->property(m_metaObject->propOffset() + m_id)
|
||||
.typeName(),
|
||||
"QQmlListProperty"));
|
||||
Q_ASSERT(m_metaObject->object == prop->object);
|
||||
|
||||
// walk up to the correct meta object if necessary
|
||||
auto mo = static_cast<QQmlVMEMetaObject *>(QObjectPrivate::get(prop->object)->metaObject);
|
||||
while (inheritanceDepth--)
|
||||
mo = mo->parentVMEMetaObject();
|
||||
m_metaObject = mo;
|
||||
Q_ASSERT(m_metaObject);
|
||||
Q_ASSERT(::strstr(m_metaObject->toDynamicMetaObject(prop->object)->property(
|
||||
m_metaObject->propOffset() + m_id).typeName(), "QQmlListProperty") );
|
||||
Q_ASSERT(m_metaObject->object == prop->object);
|
||||
|
||||
// readPropertyAsList() with checks transformed into Q_ASSERT
|
||||
// and without allocation.
|
||||
if (m_metaObject->propertyAndMethodStorage.isUndefined() &&
|
||||
m_metaObject->propertyAndMethodStorage.valueRef()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto *md = static_cast<QV4::MemberData *>(
|
||||
m_metaObject->propertyAndMethodStorage.asManaged())) {
|
||||
const auto *v = (md->data() + m_id)->as<QV4::VariantObject>();
|
||||
Q_ASSERT(v);
|
||||
Q_ASSERT(v->d());
|
||||
QVariant &data = v->d()->data();
|
||||
Q_ASSERT(data.userType() == qMetaTypeId<QVector<QQmlGuard<QObject>>>());
|
||||
m_list = static_cast<QVector<QQmlGuard<QObject>> *>(data.data());
|
||||
Q_ASSERT(m_list);
|
||||
}
|
||||
// readPropertyAsList() with checks transformed into Q_ASSERT
|
||||
// and without allocation.
|
||||
if (m_metaObject->propertyAndMethodStorage.isUndefined()
|
||||
&& m_metaObject->propertyAndMethodStorage.valueRef()) {
|
||||
return;
|
||||
}
|
||||
|
||||
~ResolvedList() = default;
|
||||
|
||||
QQmlVMEMetaObject *metaObject() const { return m_metaObject; }
|
||||
QVector<QQmlGuard<QObject>> *list() const { return m_list; }
|
||||
quintptr id() const { return m_id; }
|
||||
|
||||
void activateSignal() const
|
||||
{
|
||||
m_metaObject->activate(m_metaObject->object, int(m_id + m_metaObject->methodOffset()),
|
||||
nullptr);
|
||||
if (auto *md = static_cast<QV4::MemberData *>(
|
||||
m_metaObject->propertyAndMethodStorage.asManaged())) {
|
||||
const auto *v = (md->data() + m_id)->as<QV4::VariantObject>();
|
||||
Q_ASSERT(v);
|
||||
Q_ASSERT(v->d());
|
||||
QVariant &data = v->d()->data();
|
||||
Q_ASSERT(data.userType() == qMetaTypeId<QVector<QQmlGuard<QObject>>>());
|
||||
m_list = static_cast<QVector<QQmlGuard<QObject>> *>(data.data());
|
||||
Q_ASSERT(m_list);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QQmlVMEMetaObject *m_metaObject = nullptr;
|
||||
QVector<QQmlGuard<QObject>> *m_list = nullptr;
|
||||
quintptr m_id = 0;
|
||||
};
|
||||
QQmlVMEResolvedList::~QQmlVMEResolvedList() = default;
|
||||
|
||||
static void list_append(QQmlListProperty<QObject> *prop, QObject *o)
|
||||
void QQmlVMEResolvedList::activateSignal() const
|
||||
{
|
||||
const ResolvedList resolved(prop);
|
||||
m_metaObject->activate(m_metaObject->object, int(m_id + m_metaObject->methodOffset()), nullptr);
|
||||
}
|
||||
|
||||
void QQmlVMEMetaObject::list_append(QQmlListProperty<QObject> *prop, QObject *o)
|
||||
{
|
||||
const QQmlVMEResolvedList resolved(prop);
|
||||
resolved.list()->append(o);
|
||||
resolved.activateSignal();
|
||||
}
|
||||
|
||||
void QQmlVMEMetaObject::list_append_nosignal(QQmlListProperty<QObject> *prop, QObject *o)
|
||||
{
|
||||
QQmlVMEResolvedList(prop).list()->append(o);
|
||||
}
|
||||
|
||||
static qsizetype list_count(QQmlListProperty<QObject> *prop)
|
||||
{
|
||||
return ResolvedList(prop).list()->size();
|
||||
return QQmlVMEResolvedList(prop).list()->size();
|
||||
}
|
||||
|
||||
static QObject *list_at(QQmlListProperty<QObject> *prop, qsizetype index)
|
||||
{
|
||||
return ResolvedList(prop).list()->at(index);
|
||||
return QQmlVMEResolvedList(prop).list()->at(index);
|
||||
}
|
||||
|
||||
static void list_clear(QQmlListProperty<QObject> *prop)
|
||||
void QQmlVMEMetaObject::list_clear(QQmlListProperty<QObject> *prop)
|
||||
{
|
||||
const ResolvedList resolved(prop);
|
||||
const QQmlVMEResolvedList resolved(prop);
|
||||
resolved.list()->clear();
|
||||
resolved.activateSignal();
|
||||
}
|
||||
|
||||
void QQmlVMEMetaObject::list_clear_nosignal(QQmlListProperty<QObject> *prop)
|
||||
{
|
||||
QQmlVMEResolvedList(prop).list()->clear();
|
||||
}
|
||||
|
||||
static void list_replace(QQmlListProperty<QObject> *prop, qsizetype index, QObject *o)
|
||||
{
|
||||
const ResolvedList resolved(prop);
|
||||
const QQmlVMEResolvedList resolved(prop);
|
||||
resolved.list()->replace(index, o);
|
||||
resolved.activateSignal();
|
||||
}
|
||||
|
||||
static void list_removeLast(QQmlListProperty<QObject> *prop)
|
||||
{
|
||||
const ResolvedList resolved(prop);
|
||||
const QQmlVMEResolvedList resolved(prop);
|
||||
resolved.list()->removeLast();
|
||||
resolved.activateSignal();
|
||||
}
|
||||
|
|
|
@ -37,6 +37,26 @@
|
|||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QQmlVMEMetaObject;
|
||||
class QQmlVMEResolvedList
|
||||
{
|
||||
Q_DISABLE_COPY_MOVE(QQmlVMEResolvedList)
|
||||
|
||||
public:
|
||||
QQmlVMEResolvedList(QQmlListProperty<QObject> *prop);
|
||||
~QQmlVMEResolvedList();
|
||||
|
||||
QQmlVMEMetaObject *metaObject() const { return m_metaObject; }
|
||||
QVector<QQmlGuard<QObject>> *list() const { return m_list; }
|
||||
quintptr id() const { return m_id; }
|
||||
|
||||
void activateSignal() const;
|
||||
|
||||
private:
|
||||
QQmlVMEMetaObject *m_metaObject = nullptr;
|
||||
QVector<QQmlGuard<QObject>> *m_list = nullptr;
|
||||
quintptr m_id = 0;
|
||||
};
|
||||
|
||||
class QQmlVMEVariantQObjectPtr : public QQmlGuard<QObject>
|
||||
{
|
||||
public:
|
||||
|
@ -149,6 +169,11 @@ public:
|
|||
static QQmlVMEMetaObject *getForMethod(QObject *o, int coreIndex);
|
||||
static QQmlVMEMetaObject *getForSignal(QObject *o, int coreIndex);
|
||||
|
||||
static void list_append(QQmlListProperty<QObject> *prop, QObject *o);
|
||||
static void list_clear(QQmlListProperty<QObject> *prop);
|
||||
static void list_append_nosignal(QQmlListProperty<QObject> *prop, QObject *o);
|
||||
static void list_clear_nosignal(QQmlListProperty<QObject> *prop);
|
||||
|
||||
protected:
|
||||
int metaCall(QObject *o, QMetaObject::Call _c, int _id, void **_a) override;
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
property int signalCounter: 0
|
||||
property list<QtObject> sourceList: [ QtObject{}, QtObject{}, QtObject{} ]
|
||||
property list<QtObject> targetList1: sourceList
|
||||
|
||||
onTargetList1Changed: signalCounter++
|
||||
|
||||
function assignList() {
|
||||
targetList1 = sourceList
|
||||
}
|
||||
}
|
|
@ -217,6 +217,9 @@ private slots:
|
|||
|
||||
void bindToNonQObjectTarget();
|
||||
void assignVariantList();
|
||||
|
||||
void listAssignmentSignals();
|
||||
|
||||
private:
|
||||
QQmlEngine engine;
|
||||
};
|
||||
|
@ -2544,6 +2547,19 @@ void tst_qqmlproperty::assignVariantList()
|
|||
QCOMPARE(holder->doubleList(), doubleList);
|
||||
}
|
||||
|
||||
void tst_qqmlproperty::listAssignmentSignals()
|
||||
{
|
||||
QQmlEngine engine;
|
||||
QQmlComponent component(&engine, testFileUrl("listAssignmentSignals.qml"));
|
||||
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
|
||||
QScopedPointer<QObject> root(component.create());
|
||||
QVERIFY(!root.isNull());
|
||||
|
||||
QCOMPARE(root->property("signalCounter").toInt(), 1);
|
||||
QMetaObject::invokeMethod(root.get(), "assignList");
|
||||
QCOMPARE(root->property("signalCounter").toInt(), 2);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qqmlproperty)
|
||||
|
||||
#include "tst_qqmlproperty.moc"
|
||||
|
|
Loading…
Reference in New Issue