QObject: port to new property system

Extended QObjectPrivate::ExtraData to store a pointer
to its parent, and reimplemented qGetBindingStorage()
function for QObjectPrivate::ExtraData.
This allows to use Q_OBJECT_COMPAT_PROPERTY macro
for a property, stored in QObjectPrivate::ExtraData
and solves all the problems with calling a custom
setter.

Task-number: QTBUG-85520
Change-Id: I40e01c29430846359ef9160fa1ae97c702be9a18
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
This commit is contained in:
Ivan Solovev 2020-12-14 12:27:53 +01:00
parent 696f5ffc64
commit 1a65a4faf5
7 changed files with 68 additions and 15 deletions

View File

@ -67,7 +67,7 @@ quintptr Q_CORE_EXPORT qtHookData[] = {
// The required sizes and offsets are tested in tests/auto/other/toolsupport. // The required sizes and offsets are tested in tests/auto/other/toolsupport.
// When this fails and the change was intentional, adjust the test and // When this fails and the change was intentional, adjust the test and
// adjust this value here. // adjust this value here.
20 21
}; };
static_assert(QHooks::LastHookIndex == sizeof(qtHookData) / sizeof(qtHookData[0])); static_assert(QHooks::LastHookIndex == sizeof(qtHookData) / sizeof(qtHookData[0]));

View File

@ -1214,6 +1214,11 @@ QObjectPrivate::Connection::~Connection()
QString QObject::objectName() const QString QObject::objectName() const
{ {
Q_D(const QObject); Q_D(const QObject);
if (!d->extraData && QtPrivate::isAnyBindingEvaluating()) {
QObjectPrivate *dd = const_cast<QObjectPrivate *>(d);
// extraData is mutable, so this should be safe
dd->extraData = new QObjectPrivate::ExtraData(dd);
}
return d->extraData ? d->extraData->objectName : QString(); return d->extraData ? d->extraData->objectName : QString();
} }
@ -1223,15 +1228,28 @@ QString QObject::objectName() const
void QObject::setObjectName(const QString &name) void QObject::setObjectName(const QString &name)
{ {
Q_D(QObject); Q_D(QObject);
if (!d->extraData) if (!d->extraData)
d->extraData = new QObjectPrivate::ExtraData; d->extraData = new QObjectPrivate::ExtraData(d);
d->extraData->objectName.removeBindingUnlessInWrapper();
if (d->extraData->objectName != name) { if (d->extraData->objectName != name) {
d->extraData->objectName = name; d->extraData->objectName.setValueBypassingBindings(name);
emit objectNameChanged(d->extraData->objectName, QPrivateSignal()); d->extraData->objectName.notify(); // also emits a signal
} }
} }
QBindable<QString> QObject::bindableObjectName()
{
Q_D(QObject);
if (!d->extraData)
d->extraData = new QObjectPrivate::ExtraData(d);
return QBindable<QString>(&d->extraData->objectName);
}
/*! \fn void QObject::objectNameChanged(const QString &objectName) /*! \fn void QObject::objectNameChanged(const QString &objectName)
This signal is emitted after the object's name has been changed. The new object name is passed as \a objectName. This signal is emitted after the object's name has been changed. The new object name is passed as \a objectName.
@ -1731,7 +1749,7 @@ int QObject::startTimer(int interval, Qt::TimerType timerType)
} }
int timerId = thisThreadData->eventDispatcher.loadRelaxed()->registerTimer(interval, timerType, this); int timerId = thisThreadData->eventDispatcher.loadRelaxed()->registerTimer(interval, timerType, this);
if (!d->extraData) if (!d->extraData)
d->extraData = new QObjectPrivate::ExtraData; d->extraData = new QObjectPrivate::ExtraData(d);
d->extraData->runningTimers.append(timerId); d->extraData->runningTimers.append(timerId);
return timerId; return timerId;
} }
@ -2164,7 +2182,7 @@ void QObject::installEventFilter(QObject *obj)
} }
if (!d->extraData) if (!d->extraData)
d->extraData = new QObjectPrivate::ExtraData; d->extraData = new QObjectPrivate::ExtraData(d);
// clean up unused items in the list // clean up unused items in the list
d->extraData->eventFilters.removeAll((QObject *)nullptr); d->extraData->eventFilters.removeAll((QObject *)nullptr);
@ -3971,7 +3989,7 @@ bool QObject::setProperty(const char *name, const QVariant &value)
int id = meta->indexOfProperty(name); int id = meta->indexOfProperty(name);
if (id < 0) { if (id < 0) {
if (!d->extraData) if (!d->extraData)
d->extraData = new QObjectPrivate::ExtraData; d->extraData = new QObjectPrivate::ExtraData(d);
const int idx = d->extraData->propertyNames.indexOf(name); const int idx = d->extraData->propertyNames.indexOf(name);

View File

@ -120,7 +120,8 @@ class Q_CORE_EXPORT QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged) Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged
BINDABLE bindableObjectName)
Q_DECLARE_PRIVATE(QObject) Q_DECLARE_PRIVATE(QObject)
public: public:
@ -137,6 +138,7 @@ public:
QString objectName() const; QString objectName() const;
void setObjectName(const QString &name); void setObjectName(const QString &name);
QBindable<QString> bindableObjectName();
inline bool isWidgetType() const { return d_ptr->isWidget; } inline bool isWidgetType() const { return d_ptr->isWidget; }
inline bool isWindowType() const { return d_ptr->isWindow; } inline bool isWindowType() const { return d_ptr->isWindow; }

View File

@ -61,6 +61,7 @@
#include "QtCore/qsharedpointer.h" #include "QtCore/qsharedpointer.h"
#include "QtCore/qvariant.h" #include "QtCore/qvariant.h"
#include "QtCore/qproperty.h" #include "QtCore/qproperty.h"
#include "QtCore/private/qproperty_p.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -102,12 +103,26 @@ class Q_CORE_EXPORT QObjectPrivate : public QObjectData
public: public:
struct ExtraData struct ExtraData
{ {
ExtraData() {} ExtraData(QObjectPrivate *ptr) : parent(ptr) { }
inline void setObjectNameForwarder(const QString &name)
{
parent->q_func()->setObjectName(name);
}
inline void nameChangedForwarder(const QString &name)
{
emit parent->q_func()->objectNameChanged(name, QObject::QPrivateSignal());
}
QList<QByteArray> propertyNames; QList<QByteArray> propertyNames;
QList<QVariant> propertyValues; QList<QVariant> propertyValues;
QList<int> runningTimers; QList<int> runningTimers;
QList<QPointer<QObject>> eventFilters; QList<QPointer<QObject>> eventFilters;
QString objectName; Q_OBJECT_COMPAT_PROPERTY(QObjectPrivate::ExtraData, QString, objectName,
&QObjectPrivate::ExtraData::setObjectNameForwarder,
&QObjectPrivate::ExtraData::nameChangedForwarder)
QObjectPrivate *parent;
}; };
typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **); typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);
@ -369,8 +384,9 @@ public:
cd->ref.ref(); cd->ref.ref();
connections.storeRelaxed(cd); connections.storeRelaxed(cd);
} }
public: public:
ExtraData *extraData; // extra data set by the user mutable ExtraData *extraData; // extra data set by the user
// This atomic requires acquire/release semantics in a few places, // This atomic requires acquire/release semantics in a few places,
// e.g. QObject::moveToThread must synchronize with QCoreApplication::postEvent, // e.g. QObject::moveToThread must synchronize with QCoreApplication::postEvent,
// because postEvent is thread-safe. // because postEvent is thread-safe.
@ -619,7 +635,14 @@ inline QBindingStorage *qGetBindingStorage(QObjectPrivate *o)
{ {
return &o->bindingStorage; return &o->bindingStorage;
} }
inline const QBindingStorage *qGetBindingStorage(const QObjectPrivate::ExtraData *ed)
{
return &ed->parent->bindingStorage;
}
inline QBindingStorage *qGetBindingStorage(QObjectPrivate::ExtraData *ed)
{
return &ed->parent->bindingStorage;
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -9,9 +9,10 @@ qt_internal_add_test(tst_qobject
tst_qobject.cpp tst_qobject.cpp
DEFINES DEFINES
QT_DISABLE_DEPRECATED_BEFORE=0 QT_DISABLE_DEPRECATED_BEFORE=0
PUBLIC_LIBRARIES LIBRARIES
Qt::CorePrivate Qt::CorePrivate
Qt::Network Qt::Network
Qt::TestPrivate
) )
## Scopes: ## Scopes:

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com> ** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
@ -28,6 +28,7 @@
****************************************************************************/ ****************************************************************************/
#include <QTest> #include <QTest>
#include <QtTest/private/qpropertytesthelper_p.h>
#include <QStringListModel> #include <QStringListModel>
#include <QAbstractEventDispatcher> #include <QAbstractEventDispatcher>
#include <QScopedValueRollback> #include <QScopedValueRollback>
@ -161,6 +162,7 @@ private slots:
void functorReferencesConnection(); void functorReferencesConnection();
void disconnectDisconnects(); void disconnectDisconnects();
void singleShotConnection(); void singleShotConnection();
void objectNameBinding();
}; };
struct QObjectCreatedOnShutdown struct QObjectCreatedOnShutdown
@ -8115,6 +8117,13 @@ void tst_QObject::singleShotConnection()
} }
} }
void tst_QObject::objectNameBinding()
{
QObject obj;
QTestPrivate::testReadWritePropertyBasics<QObject, QString>(obj, "test1", "test2",
"objectName");
}
// Test for QtPrivate::HasQ_OBJECT_Macro // Test for QtPrivate::HasQ_OBJECT_Macro
static_assert(QtPrivate::HasQ_OBJECT_Macro<tst_QObject>::Value); static_assert(QtPrivate::HasQ_OBJECT_Macro<tst_QObject>::Value);
static_assert(!QtPrivate::HasQ_OBJECT_Macro<SiblingDeleter>::Value); static_assert(!QtPrivate::HasQ_OBJECT_Macro<SiblingDeleter>::Value);

View File

@ -109,7 +109,7 @@ void tst_toolsupport::offsets_data()
QTestData &data = QTest::newRow("sizeof(QObjectPrivate::ExtraData)") QTestData &data = QTest::newRow("sizeof(QObjectPrivate::ExtraData)")
<< sizeof(QObjectPrivate::ExtraData); << sizeof(QObjectPrivate::ExtraData);
// Please heed the comment at the top of this file when changing this line: // Please heed the comment at the top of this file when changing this line:
data << 60 << 120; // 4 * QList + 1 * QString data << 64 << 128; // 4 * QList + 1 * QString + ptr
} }
#if RUN_MEMBER_OFFSET_TEST #if RUN_MEMBER_OFFSET_TEST