diff options
-rw-r--r-- | src/corelib/kernel/qobject.cpp | 22 | ||||
-rw-r--r-- | src/corelib/kernel/qobject.h | 11 | ||||
-rw-r--r-- | src/corelib/kernel/qobject_p.h | 12 | ||||
-rw-r--r-- | src/corelib/kernel/qproperty.cpp | 167 | ||||
-rw-r--r-- | src/corelib/kernel/qproperty.h | 21 | ||||
-rw-r--r-- | src/corelib/kernel/qproperty_p.h | 10 | ||||
-rw-r--r-- | tests/auto/other/toolsupport/tst_toolsupport.cpp | 4 |
7 files changed, 236 insertions, 11 deletions
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 4d0523c898..5bb225d396 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -1532,6 +1532,10 @@ void QObject::moveToThread(QThread *targetThread) qWarning("QObject::moveToThread: Widgets cannot be moved to a new thread"); return; } + if (!d->bindingStorage.isEmpty()) { + qWarning("QObject::moveToThread: Can not move objects that contain bindings or are used in bindings to a new thread."); + return; + } QThreadData *currentData = QThreadData::current(); QThreadData *targetData = targetThread ? QThreadData::get2(targetThread) : nullptr; @@ -4046,6 +4050,24 @@ QList<QByteArray> QObject::dynamicPropertyNames() const return QList<QByteArray>(); } +/*! + \internal +*/ +QBindingStorage *QObject::bindingStorage() +{ + Q_D(QObject); + return &d->bindingStorage; +} + +/*! + \internal +*/ +const QBindingStorage *QObject::bindingStorage() const +{ + Q_D(const QObject); + return &d->bindingStorage; +} + #endif // QT_NO_PROPERTIES diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index d1a0c3562c..21081fa569 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -62,6 +62,7 @@ QT_BEGIN_NAMESPACE +class QBindingStorage; class QEvent; class QTimerEvent; class QChildEvent; @@ -371,6 +372,8 @@ public: bool setProperty(const char *name, const QVariant &value); QVariant property(const char *name) const; QList<QByteArray> dynamicPropertyNames() const; + QBindingStorage *bindingStorage(); + const QBindingStorage *bindingStorage() const; #endif // QT_NO_PROPERTIES Q_SIGNALS: @@ -457,6 +460,14 @@ inline T qobject_cast(const QObject *object) template <class T> inline const char * qobject_interface_iid() { return nullptr; } +inline const QBindingStorage *qGetBindingStorage(const QObject *o) +{ + return o->bindingStorage(); +} +inline QBindingStorage *qGetBindingStorage(QObject *o) +{ + return o->bindingStorage(); +} #if defined(Q_CLANG_QDOC) # define Q_DECLARE_INTERFACE(IFace, IId) diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 1f13274945..5399014cf6 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -60,6 +60,7 @@ #include "QtCore/qreadwritelock.h" #include "QtCore/qsharedpointer.h" #include "QtCore/qvariant.h" +#include "QtCore/qproperty.h" QT_BEGIN_NAMESPACE @@ -383,6 +384,7 @@ public: // these objects are all used to indicate that a QObject was deleted // plus QPointer, which keeps a separate list QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount; + QBindingStorage bindingStorage; }; Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_MOVABLE_TYPE); @@ -604,6 +606,16 @@ struct Q_CORE_EXPORT QAbstractDynamicMetaObject : public QDynamicMetaObjectData, virtual int metaCall(QMetaObject::Call, int _id, void **) { return _id; } // Compat overload }; +inline const QBindingStorage *qGetBindingStorage(const QObjectPrivate *o) +{ + return &o->bindingStorage; +} +inline QBindingStorage *qGetBindingStorage(QObjectPrivate *o) +{ + return &o->bindingStorage; +} + + QT_END_NAMESPACE #endif // QOBJECT_P_H diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index e0a1341e20..7cacd3f873 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -111,7 +111,7 @@ bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged() QPropertyBindingPrivatePtr keepAlive {this}; QScopedValueRollback<bool> updateGuard(updating, true); - BindingEvaluationState evaluationFrame(this); + QBindingEvaluationState evaluationFrame(this); bool changed = false; @@ -276,9 +276,9 @@ QPropertyBindingPrivate *QPropertyBindingData::binding() const return nullptr; } -static thread_local BindingEvaluationState *currentBindingEvaluationState = nullptr; +static thread_local QBindingEvaluationState *currentBindingEvaluationState = nullptr; -BindingEvaluationState::BindingEvaluationState(QPropertyBindingPrivate *binding) +QBindingEvaluationState::QBindingEvaluationState(QPropertyBindingPrivate *binding) : binding(binding) { // store a pointer to the currentBindingEvaluationState to avoid a TLS lookup in @@ -289,7 +289,7 @@ BindingEvaluationState::BindingEvaluationState(QPropertyBindingPrivate *binding) binding->clearDependencyObservers(); } -BindingEvaluationState::~BindingEvaluationState() +QBindingEvaluationState::~QBindingEvaluationState() { *currentState = previousState; } @@ -1083,4 +1083,163 @@ QString QPropertyBindingError::description() const If the aliased property doesn't exist, all other method calls are ignored. */ +struct QBindingStorageData +{ + size_t size = 0; + size_t used = 0; + // Pair[] pairs; +}; + +struct QBindingStoragePrivate +{ + // This class basically implements a simple and fast hash map to store bindings for a QObject + // The reason that we're not using QHash is that QPropertyBindingData can not be copied, only + // moved. That doesn't work well together with an implicitly shared class. + struct Pair + { + QUntypedPropertyData *data; + QPropertyBindingData bindingData; + }; + static_assert(alignof(Pair) == alignof(void *)); + static_assert(alignof(size_t) == alignof(void *)); + + QBindingStorageData *&d; + + static inline Pair *pairs(QBindingStorageData *dd) + { + Q_ASSERT(dd); + return reinterpret_cast<Pair *>(dd + 1); + } + void reallocate(size_t newSize) + { + Q_ASSERT(!d || newSize > d->size); + size_t allocSize = sizeof(QBindingStorageData) + newSize*sizeof(Pair); + void *nd = malloc(allocSize); + memset(nd, 0, allocSize); + QBindingStorageData *newData = new (nd) QBindingStorageData; + newData->size = newSize; + if (!d) { + d = newData; + return; + } + newData->used = d->used; + Pair *p = pairs(d); + for (size_t i = 0; i < d->size; ++i, ++p) { + if (p->data) { + Pair *pp = pairs(newData); + size_t index = qHash(p->data); + while (pp[index].data) { + ++index; + if (index == newData->size) + index = 0; + } + new (pp + index) Pair{p->data, QPropertyBindingData(std::move(p->bindingData), p->data)}; + } + } + // data has been moved, no need to call destructors on old Pairs + free(d); + d = newData; + } + + QBindingStoragePrivate(QBindingStorageData *&_d) : d(_d) {} + + QPropertyBindingData *get(const QUntypedPropertyData *data) + { + if (!d) + return nullptr; + Q_ASSERT(d->size && (d->size & (d->size - 1)) == 0); // size is a power of two + size_t index = qHash(data) & (d->size - 1); + Pair *p = pairs(d); + while (p[index].data) { + if (p[index].data == data) + return &p[index].bindingData; + ++index; + if (index == d->size) + index = 0; + } + return nullptr; + } + QPropertyBindingData *getAndCreate(QUntypedPropertyData *data) + { + if (!d) + reallocate(8); + else if (d->used*2 >= d->size) + reallocate(d->size*2); + Q_ASSERT(d->size && (d->size & (d->size - 1)) == 0); // size is a power of two + size_t index = qHash(data) & (d->size - 1); + Pair *p = pairs(d); + while (p[index].data) { + if (p[index].data == data) + return &p[index].bindingData; + ++index; + if (index == d->size) + index = 0; + } + ++d->used; + new (p + index) Pair{data, QPropertyBindingData()}; + return &p[index].bindingData; + } + + void destroy() + { + if (!d) + return; + Pair *p = pairs(d); + for (size_t i = 0; i < d->size; ++i) { + if (p->data) + p->~Pair(); + ++p; + } + free(d); + } +}; + +/*! + \class QBindingStorage + \internal + + QBindingStorage acts as a storage for property binding related data in QObject. + Any property in a QObject can be made bindable, by using the Q_BINDABLE_PROPERTY_DATA + macro to declare the data storage. Then implement a setter and getter for the property + and declare it as a Q_PROPERTY as usual. Finally make it bindable, but using + the Q_BINDABLE_PROPERTY macro after the declaration of the setter and getter. +*/ + +QBindingStorage::QBindingStorage() +{ + currentlyEvaluatingBinding = ¤tBindingEvaluationState; +} + +QBindingStorage::~QBindingStorage() +{ + QBindingStoragePrivate(d).destroy(); +} + +void QBindingStorage::maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const +{ + QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data); + auto storage = *currentlyEvaluatingBinding ? + QBindingStoragePrivate(d).getAndCreate(dd) : + QBindingStoragePrivate(d).get(dd); + if (!storage) + return; + if (auto *binding = storage->binding()) + binding->evaluateIfDirtyAndReturnTrueIfValueChanged(); + storage->registerWithCurrentlyEvaluatingBinding(); +} + +QPropertyBindingData *QBindingStorage::bindingData(const QUntypedPropertyData *data) const +{ + return QBindingStoragePrivate(d).get(data); +} + +QPropertyBindingData *QBindingStorage::bindingData(QUntypedPropertyData *data, bool create) +{ + auto storage = create ? + QBindingStoragePrivate(d).getAndCreate(data) : + QBindingStoragePrivate(d).get(data); + return storage; +} + + QT_END_NAMESPACE diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h index 85bdfdcd56..bb89dbf94f 100644 --- a/src/corelib/kernel/qproperty.h +++ b/src/corelib/kernel/qproperty.h @@ -721,6 +721,27 @@ public: } }; +struct QBindingStatus; + +struct QBindingStorageData; +class Q_CORE_EXPORT QBindingStorage +{ + mutable QBindingStorageData *d = nullptr; + QBindingStatus *bindingStatus = nullptr; + + template<typename Class, typename T, auto Offset, auto Setter> + friend class QObjectCompatProperty; +public: + QBindingStorage(); + ~QBindingStorage(); + + bool isEmpty() { return !d; } + + void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const; + QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const; + QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create); +}; + QT_END_NAMESPACE #endif // QPROPERTY_H diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h index d93db988f9..1085c037e6 100644 --- a/src/corelib/kernel/qproperty_p.h +++ b/src/corelib/kernel/qproperty_p.h @@ -120,13 +120,13 @@ public: QString description; }; -struct BindingEvaluationState +struct QBindingEvaluationState { - BindingEvaluationState(QPropertyBindingPrivate *binding); - ~BindingEvaluationState(); + QBindingEvaluationState(QPropertyBindingPrivate *binding); + ~QBindingEvaluationState(); QPropertyBindingPrivate *binding; - BindingEvaluationState *previousState = nullptr; - BindingEvaluationState **currentState = nullptr; + QBindingEvaluationState *previousState = nullptr; + QBindingEvaluationState **currentState = nullptr; }; class Q_CORE_EXPORT QPropertyBindingPrivate : public QSharedData diff --git a/tests/auto/other/toolsupport/tst_toolsupport.cpp b/tests/auto/other/toolsupport/tst_toolsupport.cpp index 52fa821be7..7acf1ad08d 100644 --- a/tests/auto/other/toolsupport/tst_toolsupport.cpp +++ b/tests/auto/other/toolsupport/tst_toolsupport.cpp @@ -126,9 +126,9 @@ void tst_toolsupport::offsets_data() #ifdef Q_PROCESSOR_X86 // x86 32-bit has weird alignment rules. Refer to QtPrivate::AlignOf in // qglobal.h for more details. - data << 180 << 288; + data << 188 << 304; #else - data << 184 << 288; + data << 192 << 304; #endif } #endif |