summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/kernel/qobject.cpp22
-rw-r--r--src/corelib/kernel/qobject.h11
-rw-r--r--src/corelib/kernel/qobject_p.h12
-rw-r--r--src/corelib/kernel/qproperty.cpp167
-rw-r--r--src/corelib/kernel/qproperty.h21
-rw-r--r--src/corelib/kernel/qproperty_p.h10
-rw-r--r--tests/auto/other/toolsupport/tst_toolsupport.cpp4
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 = &currentBindingEvaluationState;
+}
+
+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