summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qproperty.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qproperty.cpp')
-rw-r--r--src/corelib/kernel/qproperty.cpp1234
1 files changed, 771 insertions, 463 deletions
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index f1c8adb8a0..caa9fce787 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qproperty.h"
#include "qproperty_p.h"
@@ -43,6 +7,10 @@
#include <qscopedvaluerollback.h>
#include <QScopeGuard>
#include <QtCore/qloggingcategory.h>
+#include <QThread>
+#include <QtCore/qmetaobject.h>
+
+#include "qobject_p.h"
QT_BEGIN_NAMESPACE
@@ -59,9 +27,9 @@ void QPropertyBindingPrivatePtr::reset(QtPrivate::RefCounted *ptr) noexcept
{
if (ptr != d) {
if (ptr)
- ptr->ref++;
- auto *old = qExchange(d, ptr);
- if (old && (--old->ref == 0))
+ ptr->addRef();
+ auto *old = std::exchange(d, ptr);
+ if (old && !old->deref())
QPropertyBindingPrivate::destroyAndFreeMemory(static_cast<QPropertyBindingPrivate *>(d));
}
}
@@ -95,7 +63,7 @@ void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer)
notifications.
\sa beginPropertyUpdateGroup, endPropertyUpdateGroup
- */
+*/
struct QPropertyDelayedNotifications
{
// we can't access the dynamic page size as we need a constant value
@@ -153,7 +121,7 @@ struct QPropertyDelayedNotifications
Change notifications are sent later with notify (following the logic of separating
binding updates and notifications used in non-deferred updates).
*/
- void evaluateBindings(int index) {
+ void evaluateBindings(PendingBindingObserverList &bindingObservers, qsizetype index, QBindingStatus *status) {
auto *delayed = delayedProperties + index;
auto *bindingData = delayed->originalBindingData;
if (!bindingData)
@@ -169,7 +137,7 @@ struct QPropertyDelayedNotifications
QPropertyBindingDataPointer bindingDataPointer{bindingData};
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
- observer.evaluateBindings();
+ observer.evaluateBindings(bindingObservers, status);
}
/*!
@@ -181,28 +149,28 @@ struct QPropertyDelayedNotifications
\li sends any pending notifications.
\endlist
*/
- void notify(int index) {
+ void notify(qsizetype index) {
auto *delayed = delayedProperties + index;
- auto *bindingData = delayed->originalBindingData;
- if (!bindingData)
+ if (delayed->d_ptr & QPropertyBindingData::BindingBit)
+ return; // already handled
+ if (!delayed->originalBindingData)
return;
-
delayed->originalBindingData = nullptr;
+
+ QPropertyObserverPointer observer { reinterpret_cast<QPropertyObserver *>(delayed->d_ptr & ~QPropertyBindingData::DelayedNotificationBit) };
delayed->d_ptr = 0;
- QPropertyBindingDataPointer bindingDataPointer{bindingData};
- QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
observer.notify(delayed->propertyData);
}
};
-static thread_local QPropertyDelayedNotifications *groupUpdateData = nullptr;
+Q_CONSTINIT static thread_local QBindingStatus bindingStatus;
/*!
\since 6.2
- \relates template<typename T> QProperty<T>
+ \relates QProperty
Marks the beginning of a property update group. Inside this group,
changing a property does neither immediately update any dependent properties
@@ -217,10 +185,11 @@ static thread_local QPropertyDelayedNotifications *groupUpdateData = nullptr;
properties need to be updated, preventing any external observer from noticing an inconsistent
state.
- \sa Qt::endPropertyUpdateGroup
- */
+ \sa Qt::endPropertyUpdateGroup, QScopedPropertyUpdateGroup
+*/
void Qt::beginPropertyUpdateGroup()
{
+ QPropertyDelayedNotifications *& groupUpdateData = bindingStatus.groupUpdateData;
if (!groupUpdateData)
groupUpdateData = new QPropertyDelayedNotifications;
++groupUpdateData->ref;
@@ -228,7 +197,7 @@ void Qt::beginPropertyUpdateGroup()
/*!
\since 6.2
- \relates template<typename T> QProperty<T>
+ \relates QProperty
Ends a property update group. If the outermost group has been ended, and deferred
binding evaluations and notifications happen now.
@@ -236,33 +205,80 @@ void Qt::beginPropertyUpdateGroup()
\warning Calling endPropertyUpdateGroup without a preceding call to beginPropertyUpdateGroup
results in undefined behavior.
- \sa Qt::beginPropertyUpdateGroup
- */
+ \sa Qt::beginPropertyUpdateGroup, QScopedPropertyUpdateGroup
+*/
void Qt::endPropertyUpdateGroup()
{
+ auto status = &bindingStatus;
+ QPropertyDelayedNotifications *& groupUpdateData = status->groupUpdateData;
auto *data = groupUpdateData;
Q_ASSERT(data->ref);
if (--data->ref)
return;
groupUpdateData = nullptr;
+ // ensures that bindings are kept alive until endPropertyUpdateGroup concludes
+ PendingBindingObserverList bindingObservers;
// update all delayed properties
auto start = data;
while (data) {
- for (int i = 0; i < data->used; ++i)
- data->evaluateBindings(i);
+ for (qsizetype i = 0; i < data->used; ++i)
+ data->evaluateBindings(bindingObservers, i, status);
data = data->next;
}
- // notify all delayed properties
+ // notify all delayed notifications from binding evaluation
+ for (const QBindingObserverPtr &observer: bindingObservers) {
+ QPropertyBindingPrivate *binding = observer.binding();
+ binding->notifyNonRecursive();
+ }
+ // do the same for properties which only have observers
data = start;
while (data) {
- for (int i = 0; i < data->used; ++i)
+ for (qsizetype i = 0; i < data->used; ++i)
data->notify(i);
- auto *next = data->next;
- delete data;
- data = next;
+ delete std::exchange(data, data->next);
}
}
+/*!
+ \since 6.6
+ \class QScopedPropertyUpdateGroup
+ \inmodule QtCore
+ \ingroup tools
+ \brief RAII class around Qt::beginPropertyUpdateGroup()/Qt::endPropertyUpdateGroup().
+
+ This class calls Qt::beginPropertyUpdateGroup() in its constructor and
+ Qt::endPropertyUpdateGroup() in its destructor, making sure the latter
+ function is reliably called even in the presence of early returns or thrown
+ exceptions.
+
+ \note Qt::endPropertyUpdateGroup() may re-throw exceptions thrown by
+ binding evaluations. This means your application may crash
+ (\c{std::terminate()} called) if another exception is causing
+ QScopedPropertyUpdateGroup's destructor to be called during stack
+ unwinding. If you expect exceptions from binding evaluations, use manual
+ Qt::endPropertyUpdateGroup() calls and \c{try}/\c{catch} blocks.
+
+ \sa QProperty
+*/
+
+/*!
+ \fn QScopedPropertyUpdateGroup::QScopedPropertyUpdateGroup()
+
+ Calls Qt::beginPropertyUpdateGroup().
+*/
+
+/*!
+ \fn QScopedPropertyUpdateGroup::~QScopedPropertyUpdateGroup()
+
+ Calls Qt::endPropertyUpdateGroup().
+*/
+
+
+// check everything stored in QPropertyBindingPrivate's union is trivially destructible
+// (though the compiler would also complain if that weren't the case)
+static_assert(std::is_trivially_destructible_v<QPropertyBindingSourceLocation>);
+static_assert(std::is_trivially_destructible_v<std::byte[sizeof(QPropertyBindingSourceLocation)]>);
+
QPropertyBindingPrivate::~QPropertyBindingPrivate()
{
if (firstObserver)
@@ -272,60 +288,51 @@ QPropertyBindingPrivate::~QPropertyBindingPrivate()
+ QPropertyBindingPrivate::getSizeEnsuringAlignment());
}
+void QPropertyBindingPrivate::clearDependencyObservers() {
+ for (size_t i = 0; i < qMin(dependencyObserverCount, inlineDependencyObservers.size()); ++i) {
+ QPropertyObserverPointer p{&inlineDependencyObservers[i]};
+ p.unlink_fast();
+ }
+ if (heapObservers)
+ heapObservers->clear();
+ dependencyObserverCount = 0;
+}
+
+QPropertyObserverPointer QPropertyBindingPrivate::allocateDependencyObserver_slow()
+{
+ ++dependencyObserverCount;
+ if (!heapObservers)
+ heapObservers.reset(new std::vector<QPropertyObserver>());
+ return {&heapObservers->emplace_back()};
+}
+
void QPropertyBindingPrivate::unlinkAndDeref()
{
clearDependencyObservers();
propertyDataPtr = nullptr;
- if (--ref == 0)
+ if (!deref())
destroyAndFreeMemory(this);
}
-void QPropertyBindingPrivate::evaluateRecursive()
+bool QPropertyBindingPrivate::evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
{
- if (updating) {
- error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
- if (isQQmlPropertyBinding)
- errorCallBack(this);
- return;
- }
-
- QScopedValueRollback<bool> updateGuard(updating, true);
-
- /*
- * Evaluating the binding might lead to the binding being broken. This can
- * cause ref to reach zero at the end of the function. However, the
- * updateGuard's destructor will then still trigger, trying to set the
- * updating bool to its old value
- * To prevent this, we create a QPropertyBindingPrivatePtr which ensures
- * that the object is still alive when updateGuard's dtor runs.
- */
- QPropertyBindingPrivatePtr keepAlive {this};
-
- BindingEvaluationState evaluationFrame(this);
+ if (!status)
+ status = &bindingStatus;
+ return evaluateRecursive_inline(bindingObservers, status);
+}
- auto bindingFunctor = reinterpret_cast<std::byte *>(this) +
- QPropertyBindingPrivate::getSizeEnsuringAlignment();
- bool changed = false;
- if (hasBindingWrapper) {
- changed = staticBindingWrapper(metaType, propertyDataPtr,
- {vtable, bindingFunctor});
- } else {
- changed = vtable->call(metaType, propertyDataPtr, bindingFunctor);
+void QPropertyBindingPrivate::notifyNonRecursive(const PendingBindingObserverList &bindingObservers)
+{
+ notifyNonRecursive();
+ for (auto &&bindingObserver: bindingObservers) {
+ bindingObserver.binding()->notifyNonRecursive();
}
- // If there was a change, we must set pendingNotify.
- // If there was not, we must not clear it, as that only should happen in notifyRecursive
- pendingNotify = pendingNotify || changed;
- if (!changed || !firstObserver)
- return;
-
- firstObserver.noSelfDependencies(this);
- firstObserver.evaluateBindings();
}
-void QPropertyBindingPrivate::notifyRecursive()
+QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRecursive()
{
if (!pendingNotify)
- return;
+ return Delayed;
pendingNotify = false;
Q_ASSERT(!updating);
updating = true;
@@ -336,10 +343,30 @@ void QPropertyBindingPrivate::notifyRecursive()
if (hasStaticObserver)
staticObserverCallback(propertyDataPtr);
updating = false;
+ return Sent;
}
+/*!
+ Constructs a null QUntypedPropertyBinding.
+
+ \sa isNull()
+*/
QUntypedPropertyBinding::QUntypedPropertyBinding() = default;
+/*!
+ \fn template<typename Functor>
+ QUntypedPropertyBinding(QMetaType metaType, Functor &&f, const QPropertyBindingSourceLocation &location)
+
+ \internal
+*/
+
+/*!
+ \internal
+
+ Constructs QUntypedPropertyBinding. Assumes that \a metaType, \a function and \a vtable match.
+ Unless a specialization of \c BindingFunctionVTable is used, this function should never be called
+ directly.
+*/
QUntypedPropertyBinding::QUntypedPropertyBinding(QMetaType metaType, const BindingFunctionVTable *vtable, void *function,
const QPropertyBindingSourceLocation &location)
{
@@ -348,42 +375,77 @@ QUntypedPropertyBinding::QUntypedPropertyBinding(QMetaType metaType, const Bindi
vtable->moveConstruct(mem + QPropertyBindingPrivate::getSizeEnsuringAlignment(), function);
}
+/*!
+ Move-constructs a QUntypedPropertyBinding from \a other.
+
+ \a other is left in a null state.
+ \sa isNull()
+*/
QUntypedPropertyBinding::QUntypedPropertyBinding(QUntypedPropertyBinding &&other)
: d(std::move(other.d))
{
}
+/*!
+ Copy-constructs a QUntypedPropertyBinding from \a other.
+*/
QUntypedPropertyBinding::QUntypedPropertyBinding(const QUntypedPropertyBinding &other)
: d(other.d)
{
}
+/*!
+ Copy-assigns \a other to this QUntypedPropertyBinding.
+*/
QUntypedPropertyBinding &QUntypedPropertyBinding::operator=(const QUntypedPropertyBinding &other)
{
d = other.d;
return *this;
}
+/*!
+ Move-assigns \a other to this QUntypedPropertyBinding.
+
+ \a other is left in a null state.
+ \sa isNull
+*/
QUntypedPropertyBinding &QUntypedPropertyBinding::operator=(QUntypedPropertyBinding &&other)
{
d = std::move(other.d);
return *this;
}
+/*!
+ \internal
+*/
QUntypedPropertyBinding::QUntypedPropertyBinding(QPropertyBindingPrivate *priv)
: d(priv)
{
}
+/*!
+ Destroys the QUntypedPropertyBinding.
+*/
QUntypedPropertyBinding::~QUntypedPropertyBinding()
{
}
+/*!
+ Returns \c true if the \c QUntypedPropertyBinding is null.
+ This is only true for default-constructed and moved-from instances.
+
+ \sa isNull()
+*/
bool QUntypedPropertyBinding::isNull() const
{
return !d;
}
+/*!
+ Returns the error state of the binding.
+
+ \sa QPropertyBindingError
+*/
QPropertyBindingError QUntypedPropertyBinding::error() const
{
if (!d)
@@ -391,6 +453,10 @@ QPropertyBindingError QUntypedPropertyBinding::error() const
return static_cast<QPropertyBindingPrivate *>(d.get())->bindingError();
}
+/*!
+ Returns the meta-type of the binding.
+ If the QUntypedPropertyBinding is null, an invalid QMetaType is returned.
+*/
QMetaType QUntypedPropertyBinding::valueMetaType() const
{
if (!d)
@@ -401,6 +467,8 @@ QMetaType QUntypedPropertyBinding::valueMetaType() const
QPropertyBindingData::~QPropertyBindingData()
{
QPropertyBindingDataPointer d{this};
+ if (isNotificationDelayed())
+ proxyData()->originalBindingData = nullptr;
for (auto observer = d.firstObserver(); observer;) {
auto next = observer.nextObserver();
observer.unlink();
@@ -447,8 +515,9 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
newBindingRaw->prependObserver(observer);
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
- newBindingRaw->evaluateRecursive();
- newBindingRaw->notifyRecursive();
+ PendingBindingObserverList bindingObservers;
+ newBindingRaw->evaluateRecursive(bindingObservers);
+ newBindingRaw->notifyNonRecursive(bindingObservers);
} else if (observer) {
d.setObservers(observer.ptr);
} else {
@@ -466,14 +535,11 @@ QPropertyBindingData::QPropertyBindingData(QPropertyBindingData &&other) : d_ptr
QPropertyBindingDataPointer::fixupAfterMove(this);
}
-static thread_local QBindingStatus bindingStatus;
-
BindingEvaluationState::BindingEvaluationState(QPropertyBindingPrivate *binding, QBindingStatus *status)
: binding(binding)
{
+ Q_ASSERT(status);
QBindingStatus *s = status;
- if (!s)
- s = &bindingStatus;
// store a pointer to the currentBindingEvaluationState to avoid a TLS lookup in
// the destructor (as these come with a non zero cost)
currentState = &s->currentlyEvaluatingBinding;
@@ -537,54 +603,90 @@ void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(Binding
{
QPropertyBindingDataPointer d{this};
+ if (currentState->alreadyCaptureProperties.contains(this))
+ return;
+ else
+ currentState->alreadyCaptureProperties.push_back(this);
+
QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
- dependencyObserver.setBindingToNotify(currentState->binding);
- dependencyObserver.observeProperty(d);
+ Q_ASSERT(QPropertyObserver::ObserverNotifiesBinding == 0);
+ dependencyObserver.setBindingToNotify_unsafe(currentState->binding);
+ d.addObserver(dependencyObserver.ptr);
}
void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr) const
{
+ notifyObservers(propertyDataPtr, nullptr);
+}
+
+void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage) const
+{
if (isNotificationDelayed())
return;
QPropertyBindingDataPointer d{this};
+ PendingBindingObserverList bindingObservers;
if (QPropertyObserverPointer observer = d.firstObserver()) {
- auto *delay = groupUpdateData;
- if (delay) {
- delay->addProperty(this, propertyDataPtr);
- return;
+ if (notifyObserver_helper(propertyDataPtr, storage, observer, bindingObservers) == Evaluated) {
+ /* evaluateBindings() can trash the observers. We need to re-fetch here.
+ "this" might also no longer be valid in case we have a QObjectBindableProperty
+ and consequently d isn't either (this happens when binding evaluation has
+ caused the binding storage to resize.
+ If storage is nullptr, then there is no dynamically resizable storage,
+ and we cannot run into the issue.
+ */
+ if (storage)
+ d = QPropertyBindingDataPointer {storage->bindingData(propertyDataPtr)};
+ if (QPropertyObserverPointer observer = d.firstObserver())
+ observer.notify(propertyDataPtr);
+ for (auto &&bindingObserver: bindingObservers)
+ bindingObserver.binding()->notifyNonRecursive();
}
- observer.evaluateBindings();
- } else {
- return;
}
-
- // evaluateBindings() can trash the observers. We need to re-fetch here.
- if (QPropertyObserverPointer observer = d.firstObserver())
- observer.notify(propertyDataPtr);
}
-int QPropertyBindingDataPointer::observerCount() const
+QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_helper
+(
+ QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage,
+ QPropertyObserverPointer observer,
+ PendingBindingObserverList &bindingObservers) const
{
- int count = 0;
- for (auto observer = firstObserver(); observer; observer = observer.nextObserver())
- ++count;
- return count;
+#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
+ QBindingStatus *status = storage ? storage->bindingStatus : nullptr;
+ if (!status || status->threadId != QThread::currentThreadId())
+ status = &bindingStatus;
+#else
+ Q_UNUSED(storage);
+ QBindingStatus *status = &bindingStatus;
+#endif
+ if (QPropertyDelayedNotifications *delay = status->groupUpdateData) {
+ delay->addProperty(this, propertyDataPtr);
+ return Delayed;
+ }
+
+ observer.evaluateBindings(bindingObservers, status);
+ return Evaluated;
}
+
QPropertyObserver::QPropertyObserver(ChangeHandler changeHandler)
{
QPropertyObserverPointer d{this};
d.setChangeHandler(changeHandler);
}
-QPropertyObserver::QPropertyObserver(QUntypedPropertyData *)
+#if QT_DEPRECATED_SINCE(6, 6)
+QPropertyObserver::QPropertyObserver(QUntypedPropertyData *data)
{
- // ### Qt 7: Remove, currently left for binary compatibility
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
+ aliasData = data;
+ next.setTag(ObserverIsAlias);
+ QT_WARNING_POP
}
+#endif
/*! \internal
- */
+*/
void QPropertyObserver::setSource(const QPropertyBindingData &property)
{
QPropertyObserverPointer d{this};
@@ -629,15 +731,20 @@ QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexc
return *this;
}
-void QPropertyObserverPointer::unlink()
-{
- if (ptr->next)
- ptr->next->prev = ptr->prev;
- if (ptr->prev)
- ptr->prev.setPointer(ptr->next.data());
- ptr->next = nullptr;
- ptr->prev.clear();
-}
+/*!
+ \fn QPropertyObserverPointer::unlink()
+ \internal
+ Unlinks
+ */
+
+
+/*!
+ \fn QPropertyObserverPointer::unlink_fast()
+ \internal
+ Like unlink, but does not handle ObserverIsAlias.
+ Must only be called in places where we know that we are not dealing
+ with such an observer.
+ */
void QPropertyObserverPointer::setChangeHandler(QPropertyObserver::ChangeHandler changeHandler)
{
@@ -646,96 +753,28 @@ void QPropertyObserverPointer::setChangeHandler(QPropertyObserver::ChangeHandler
ptr->next.setTag(QPropertyObserver::ObserverNotifiesChangeHandler);
}
-void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *binding)
+/*!
+ \internal
+ The same as setBindingToNotify, but assumes that the tag is already correct.
+ */
+void QPropertyObserverPointer::setBindingToNotify_unsafe(QPropertyBindingPrivate *binding)
{
- Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
+ Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding);
ptr->binding = binding;
- ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
}
/*!
+ \class QPropertyObserverNodeProtector
\internal
QPropertyObserverNodeProtector is a RAII wrapper which takes care of the internal switching logic
for QPropertyObserverPointer::notify (described ibidem)
*/
-struct [[nodiscard]] QPropertyObserverNodeProtector {
- QPropertyObserverBase m_placeHolder;
- QPropertyObserverNodeProtector(QPropertyObserver *observer)
- {
- // insert m_placeholder after observer into the linked list
- QPropertyObserver *next = observer->next.data();
- m_placeHolder.next = next;
- observer->next = static_cast<QPropertyObserver *>(&m_placeHolder);
- if (next)
- next->prev = &m_placeHolder.next;
- m_placeHolder.prev = &observer->next;
- m_placeHolder.next.setTag(QPropertyObserver::ObserverIsPlaceholder);
- }
-
- QPropertyObserver *next() const { return m_placeHolder.next.data(); }
-
- ~QPropertyObserverNodeProtector() {
- QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)};
- d.unlink();
- }
-};
-/*! \internal
+/*!
+ \fn QPropertyObserverNodeProtector::notify(QUntypedPropertyData *propertyDataPtr)
+ \internal
\a propertyDataPtr is a pointer to the observed property's property data
- */
-void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
-{
- auto observer = const_cast<QPropertyObserver*>(ptr);
- /*
- * The basic idea of the loop is as follows: We iterate over all observers in the linked list,
- * and execute the functionality corresponding to their tag.
- * However, complication arise due to the fact that the triggered operations might modify the list,
- * which includes deletion and move of the current and next nodes.
- * Therefore, we take a few safety precautions:
- * 1. Before executing any action which might modify the list, we insert a placeholder node after the current node.
- * As that one is stack allocated and owned by us, we can rest assured that it is
- * still there after the action has executed, and placeHolder->next points to the actual next node in the list.
- * Note that taking next at the beginning of the loop does not work, as the execuated action might either move
- * or delete that node.
- * 2. After the triggered action has finished, we can use the next pointer in the placeholder node as a safe way to
- * retrieve the next node.
- * 3. Some care needs to be taken to avoid infinite recursion with change handlers, so we add an extra test there, that
- * checks whether we're already have the same change handler in our call stack. This can be done by checking whether
- * the node after the current one is a placeholder node.
- */
- while (observer) {
- QPropertyObserver *next = observer->next.data();
- switch (QPropertyObserver::ObserverTag(observer->next.tag())) {
- case QPropertyObserver::ObserverNotifiesChangeHandler:
- {
- auto handlerToCall = observer->changeHandler;
- // prevent recursion
- if (next && next->next.tag() == QPropertyObserver::ObserverIsPlaceholder) {
- observer = next->next.data();
- continue;
- }
- // handlerToCall might modify the list
- QPropertyObserverNodeProtector protector(observer);
- handlerToCall(observer, propertyDataPtr);
- next = protector.next();
- break;
- }
- case QPropertyObserver::ObserverNotifiesBinding:
- {
- auto bindingToNotify = observer->binding;
- QPropertyObserverNodeProtector protector(observer);
- bindingToNotify->notifyRecursive();
- next = protector.next();
- break;
- }
- case QPropertyObserver::ObserverIsPlaceholder:
- // recursion is already properly handled somewhere else
- break;
- default: Q_UNREACHABLE();
- }
- observer = next;
- }
-}
+*/
#ifndef QT_NO_DEBUG
void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *binding)
@@ -755,17 +794,20 @@ void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *bindi
}
#endif
-void QPropertyObserverPointer::evaluateBindings()
+void QPropertyObserverPointer::evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
{
+ Q_ASSERT(status);
auto observer = const_cast<QPropertyObserver*>(ptr);
// See also comment in notify()
while (observer) {
QPropertyObserver *next = observer->next.data();
if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) {
- auto bindingToEvaluate = observer->binding;
+ auto bindingToEvaluate = observer->binding;
QPropertyObserverNodeProtector protector(observer);
- bindingToEvaluate->evaluateRecursive();
+ QBindingObserverPtr bindingObserver(observer); // binding must not be gone after evaluateRecursive_inline
+ if (bindingToEvaluate->evaluateRecursive_inline(bindingObservers, status))
+ bindingObservers.push_back(std::move(bindingObserver));
next = protector.next();
}
@@ -780,10 +822,61 @@ void QPropertyObserverPointer::observeProperty(QPropertyBindingDataPointer prope
property.addObserver(ptr);
}
+/*!
+ \class QPropertyBindingError
+ \inmodule QtCore
+ \ingroup tools
+ \since 6.0
+
+ QPropertyBindingError is used by \l{The Property System}{the property
+ system} to report errors that occurred when a binding was evaluated. Use \l
+ type() to query which error occurred, and \l
+ description() to extract an error message which might contain
+ more details.
+ If there is no error, QPropertyBindingError has type
+ \c QPropertyBindingError::NoError and \c hasError() returns false.
+
+ \code
+ extern QProperty<int> prop;
+
+ QPropertyBindingError error = prop.binding().error();
+ if (error.hasError())
+ qDebug() << error.description();
+ \endcode
+*/
+
+/*!
+ \enum QPropertyBindingError::Type
+
+ This enum specifies which error occurred.
+
+ \value NoError
+ No error occurred while evaluating the binding.
+ \value BindingLoop
+ Binding evaluation was stopped because a property depended on its own
+ value.
+ \value EvaluationError
+ Binding evaluation was stopped for any other reason than a binding loop.
+ For example, this value is used in the QML engine when an exception occurs
+ while a binding is evaluated.
+ \value UnknownError
+ A generic error type used when neither of the other values is suitable.
+ Calling \l description() might provide details.
+*/
+
+/*!
+ Default constructs QPropertyBindingError.
+ hasError() will return false, type will return \c NoError and
+ \l description() will return an empty string.
+*/
QPropertyBindingError::QPropertyBindingError()
{
}
+/*!
+ Constructs a QPropertyBindingError of type \a type with \a description as its
+ description.
+*/
QPropertyBindingError::QPropertyBindingError(Type type, const QString &description)
{
if (type != NoError) {
@@ -793,32 +886,54 @@ QPropertyBindingError::QPropertyBindingError(Type type, const QString &descripti
}
}
+/*!
+ Copy-constructs QPropertyBindingError from \a other.
+*/
QPropertyBindingError::QPropertyBindingError(const QPropertyBindingError &other)
: d(other.d)
{
}
+/*!
+ Copies \a other to this QPropertyBindingError.
+*/
QPropertyBindingError &QPropertyBindingError::operator=(const QPropertyBindingError &other)
{
d = other.d;
return *this;
}
+/*!
+ Move-constructs QPropertyBindingError from \a other.
+ \a other will be left in its default state.
+*/
QPropertyBindingError::QPropertyBindingError(QPropertyBindingError &&other)
: d(std::move(other.d))
{
}
+/*!
+ Move-assigns \a other to this QPropertyBindingError.
+ \a other will be left in its default state.
+*/
QPropertyBindingError &QPropertyBindingError::operator=(QPropertyBindingError &&other)
{
d = std::move(other.d);
return *this;
}
+/*!
+ Destroys the QPropertyBindingError.
+*/
QPropertyBindingError::~QPropertyBindingError()
{
}
+/*!
+ Returns the type of the QPropertyBindingError.
+
+ \sa QPropertyBindingError::Type
+*/
QPropertyBindingError::Type QPropertyBindingError::type() const
{
if (!d)
@@ -826,6 +941,10 @@ QPropertyBindingError::Type QPropertyBindingError::type() const
return d->type;
}
+/*!
+ Returns a descriptive error message for the QPropertyBindingError if
+ it has been set.
+*/
QString QPropertyBindingError::description() const
{
if (!d)
@@ -907,21 +1026,21 @@ QString QPropertyBindingError::description() const
will be invalid.
\sa isValid(), isReadOnly()
- */
+*/
/*!
\fn bool QUntypedBindable::isValid() const
Returns true if the QUntypedBindable is valid. Methods called on an invalid
QUntypedBindable generally have no effect, unless otherwise noted.
- */
+*/
/*!
\fn bool QUntypedBindable::isReadOnly() const
\since 6.1
Returns true if the QUntypedBindable is read-only.
- */
+*/
/*!
\fn bool QUntypedBindable::isBindable() const
@@ -933,7 +1052,7 @@ QString QPropertyBindingError::description() const
false.
\sa isReadOnly()
- */
+*/
/*!
\fn QUntypedPropertyBinding QUntypedBindable::makeBinding(const QPropertyBindingSourceLocation &location) const
@@ -1021,7 +1140,8 @@ QString QPropertyBindingError::description() const
Returns the metatype of the property from which the QUntypedBindable was created.
If the bindable is invalid, an invalid metatype will be returned.
- \sa isValid() //!, QUntypedPropertyBinding::valueMetaType()
+ \sa isValid()
+ //! \sa QUntypedPropertyBinding::valueMetaType()
*/
/*!
@@ -1033,7 +1153,8 @@ QString QPropertyBindingError::description() const
\ingroup tools
- QBindable\<T\> helps to integrate Qt's traditional Q_PROPERTY with binding-enabled properties.
+ QBindable\<T\> helps to integrate Qt's traditional Q_PROPERTY with
+ \l {Qt Bindable Properties}{binding-enabled} properties.
If a property is backed by a QProperty, QObjectBindableProperty or QObjectComputedProperty,
you can add \c BINDABLE bindablePropertyName to the Q_PROPERTY
declaration, where bindablePropertyName is a function returning an instance of QBindable
@@ -1044,7 +1165,40 @@ QString QPropertyBindingError::description() const
\snippet code/src_corelib_kernel_qproperty.cpp 0
\snippet code/src_corelib_kernel_qproperty.cpp 3
- \sa QMetaProperty::isBindable, QProperty, QObjectBindableProperty
+ \sa QMetaProperty::isBindable, QProperty, QObjectBindableProperty,
+ QObjectComputedProperty, {Qt Bindable Properties}
+*/
+
+/*!
+ \fn template<typename T> QBindable<T>::QBindable(QObject *obj, const char *property)
+
+ Constructs a QBindable for the \l Q_PROPERTY \a property on \a obj. The property must
+ have a notify signal but does not need to have \c BINDABLE in its \c Q_PROPERTY
+ definition, so even binding unaware \c {Q_PROPERTY}s can be bound or used in binding
+ expressions. You must use \c QBindable::value() in binding expressions instead of the
+ normal property \c READ function (or \c MEMBER) to enable dependency tracking if the
+ property is not \c BINDABLE. When binding using a lambda, you may prefer to capture the
+ QBindable by value to avoid the cost of calling this constructor in the binding
+ expression.
+ This constructor should not be used to implement \c BINDABLE for a Q_PROPERTY, as the
+ resulting Q_PROPERTY will not support dependency tracking. To make a property that is
+ usable directly without reading through a QBindable use \l QProperty or
+ \l QObjectBindableProperty.
+
+ \code
+ QProperty<QString> displayText;
+ QDateTimeEdit *dateTimeEdit = findDateTimeEdit();
+ QBindable<QDateTime> dateTimeBindable(dateTimeEdit, "dateTime");
+ displayText.setBinding([dateTimeBindable](){ return dateTimeBindable.value().toString(); });
+ \endcode
+
+ \sa QProperty, QObjectBindableProperty, {Qt Bindable Properties}
+*/
+
+/*!
+ \fn template<typename T> QBindable<T>::QBindable(QObject *obj, const QMetaProperty &property)
+
+ See \l QBindable::QBindable(QObject *obj, const char *property)
*/
/*!
@@ -1125,6 +1279,15 @@ QString QPropertyBindingError::description() const
dynamically, the binding expression. It is represented as a C++ lambda and
can be used to express relationships between different properties in your
application.
+
+ \note For QML, it's important to expose the \l QProperty in \l Q_PROPERTY
+ with the BINDABLE keyword. As a result, the QML engine uses
+ it as the bindable interface to set up the property binding. In turn, the
+ binding can then be interacted with C++ via the normal API:
+ QProperty<T>::onValueChanged, QProperty::takeBinding and QBindable::hasBinding
+ If the property is BINDABLE, the engine will use the change-tracking
+ inherent to the C++ property system for getting notified about changes, and it
+ won't rely on signals being emitted.
*/
/*!
@@ -1155,20 +1318,20 @@ QString QPropertyBindingError::description() const
/*!
\fn template <typename T> QProperty<T>::QProperty(const QPropertyBinding<T> &binding)
- Constructs a property that is tied to the provided \a binding expression. The
- first time the property value is read, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read.
+ Constructs a property that is tied to the provided \a binding expression.
+ The property's value is set to the result of evaluating the new binding.
+ Whenever a dependency of the binding changes, the binding will be re-evaluated,
+ and the property's value gets updated accordingly.
*/
/*!
\fn template <typename T> template <typename Functor> QProperty<T>::QProperty(Functor &&f)
- Constructs a property that is tied to the provided binding expression \a f. The
- first time the property value is read, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read.
-*/
+ Constructs a property that is tied to the provided binding expression \a f.
+ The property's value is set to the result of evaluating the new binding.
+ Whenever a dependency of the binding changes, the binding will be re-evaluated,
+ and the property's value gets updated accordingly.
+ */
/*!
\fn template <typename T> QProperty<T>::~QProperty()
@@ -1199,23 +1362,13 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn template <typename T> QProperty<T> &QProperty<T>::operator=(const QPropertyBinding<T> &newBinding)
-
- Associates the value of this property with the provided \a newBinding
- expression and returns a reference to this property. The first time the
- property value is read, the binding is evaluated. Whenever a dependency of the
- binding changes, the binding will be re-evaluated the next time the value of
- this property is read.
-*/
-
-/*!
\fn template <typename T> QPropertyBinding<T> QProperty<T>::setBinding(const QPropertyBinding<T> &newBinding)
Associates the value of this property with the provided \a newBinding
- expression and returns the previously associated binding. The first time the
- property value is read, the binding is evaluated. Whenever a dependency of the
- binding changes, the binding will be re-evaluated the next time the value of
- this property is read.
+ expression and returns the previously associated binding. The property's value
+ is set to the result of evaluating the new binding. Whenever a dependency of
+ the binding changes, the binding will be re-evaluated, and the property's
+ value gets updated accordingly.
*/
/*!
@@ -1223,10 +1376,10 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of this property with the provided functor \a f and
- returns the previously associated binding. The first time the property value
- is read, the binding is evaluated by invoking the call operator () of \a f.
- Whenever a dependency of the binding changes, the binding will be re-evaluated
- the next time the value of this property is read.
+ returns the previously associated binding. The property's value is set to the
+ result of evaluating the new binding. Whenever a dependency of the binding
+ changes, the binding will be re-evaluated, and the property's value gets
+ updated accordingly.
\sa {Formulating a Property Binding}
*/
@@ -1236,9 +1389,10 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of this property with the provided \a newBinding
- expression. The first time the property value is read, the binding is evaluated.
- Whenever a dependency of the binding changes, the binding will be re-evaluated
- the next time the value of this property is read.
+ expression. The property's value is set to the result of evaluating the new
+ binding. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
Returns true if the type of this property is the same as the type the binding
function returns; false otherwise.
@@ -1267,27 +1421,29 @@ QString QPropertyBindingError::description() const
the value of the property changes. On each value change, the handler
is either called immediately, or deferred, depending on the context.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the registration. When it
- goes out of scope, the callback is de-registered.
+ The returned property change handler object keeps track of the registration.
+ When it goes out of scope, the callback is de-registered.
*/
/*!
\fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QProperty<T>::subscribe(Functor f)
- Subscribes the given functor \a f as a callback that is called immediately and whenever
- the value of the property changes in the future. On each value change, the handler
- is either called immediately, or deferred, depending on the context.
+ Subscribes the given functor \a f as a callback that is called immediately and
+ whenever the value of the property changes in the future. On each value
+ change, the handler is either called immediately, or deferred, depending on
+ the context.
- The callback \a f is expected to be a type that can be copied and has a plain call
- operator() without any parameters. This means that you can provide a C++ lambda expression,
- a std::function or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that can be copied and has a plain
+ call operator() without any parameters. This means that you can provide a C++
+ lambda expression, a std::function or even a custom struct with a call
+ operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
*/
/*!
@@ -1296,15 +1452,16 @@ QString QPropertyBindingError::description() const
Subscribes the given functor \a f as a callback that is called whenever
the value of the property changes.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
- This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
- It can therefore more easily be stored, e.g. as a member in a class.
+ This method is in some cases easier to use than onValueChanged(), as the
+ returned object is not a template. It can therefore more easily be stored,
+ e.g. as a member in a class.
\sa onValueChanged(), subscribe()
*/
@@ -1317,8 +1474,9 @@ QString QPropertyBindingError::description() const
/*!
\class QObjectBindableProperty
\inmodule QtCore
- \brief The QObjectBindableProperty class is a template class that enables automatic property bindings
- for property data stored in QObject derived classes.
+ \brief The QObjectBindableProperty class is a template class that enables
+ automatic property bindings for property data stored in QObject derived
+ classes.
\since 6.0
\ingroup tools
@@ -1327,13 +1485,13 @@ QString QPropertyBindingError::description() const
instance of T and behaves mostly like \l QProperty.
It is one of the classes implementing \l {Qt Bindable Properties}.
Unlike QProperty, it stores its management data structure in
- the sourrounding QObject.
+ the surrounding QObject.
The extra template parameters are used to identify the surrounding
class and a member function of that class acting as a change handler.
- You can use QObjectBindableProperty to add binding support to code that uses Q_PROPERTY.
- The getter and setter methods must be adapted carefully according to the
- rules described in \l {Bindable Property Getters and Setters}.
+ You can use QObjectBindableProperty to add binding support to code that uses
+ Q_PROPERTY. The getter and setter methods must be adapted carefully according
+ to the rules described in \l {Bindable Property Getters and Setters}.
In order to invoke the change signal on property changes, use
QObjectBindableProperty and pass the change signal as a callback.
@@ -1342,8 +1500,8 @@ QString QPropertyBindingError::description() const
\snippet code/src_corelib_kernel_qproperty.cpp 4
- QObjectBindableProperty is usually not used directly, instead an instance of it is created by
- using the Q_OBJECT_BINDABLE_PROPERTY macro.
+ QObjectBindableProperty is usually not used directly, instead an instance of
+ it is created by using the Q_OBJECT_BINDABLE_PROPERTY macro.
Use the Q_OBJECT_BINDABLE_PROPERTY macro in the class declaration to declare
the property as bindable.
@@ -1362,23 +1520,27 @@ QString QPropertyBindingError::description() const
\snippet code/src_corelib_kernel_qproperty.cpp 2
- The change handler can optionally accept one argument, of the same type as the property,
- in which case it is passed the new value of the property. Otherwise, it should take no
- arguments.
+ The change handler can optionally accept one argument, of the same type as the
+ property, in which case it is passed the new value of the property. Otherwise,
+ it should take no arguments.
If the property does not need a changed notification, you can leave out the
"NOTIFY xChanged" in the Q_PROPERTY macro as well as the last argument
of the Q_OBJECT_BINDABLE_PROPERTY and Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS
macros.
+
+ \sa Q_OBJECT_BINDABLE_PROPERTY, Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS,
+ QProperty, QObjectComputedProperty, {Qt's Property System}, {Qt Bindable
+ Properties}
*/
/*!
\macro Q_OBJECT_BINDABLE_PROPERTY(containingClass, type, name, signal)
\since 6.0
\relates QObjectBindableProperty
- \brief Declares a \l QObjectBindableProperty inside \a containingClass
- of type \a type with name \a name. If the optional argument \a signal is given,
- this signal will be emitted when the property is marked dirty.
+ \brief Declares a \l QObjectBindableProperty inside \a containingClass of type
+ \a type with name \a name. If the optional argument \a signal is given, this
+ signal will be emitted when the property is marked dirty.
\sa {Qt's Property System}, {Qt Bindable Properties}
*/
@@ -1488,7 +1650,6 @@ QString QPropertyBindingError::description() const
properties to the bindable property system.
\since 6.0
\ingroup tools
- \internal
QObjectComputedProperty is a read-only property which is recomputed on each read.
It does not store the computed value.
@@ -1498,42 +1659,7 @@ QString QPropertyBindingError::description() const
See the following example.
- \code
- class Client{};
-
- class MyClassPrivate : public QObjectPrivate
- {
- public:
- QList<Client> clients;
- bool hasClientsActualCalculation() const { return clients.size() > 0; }
- Q_OBJECT_COMPUTED_PROPERTY(MyClassPrivate, bool, hasClientsData,
- &MyClassPrivate::hasClientsActualCalculation)
- };
-
- class MyClass : public QObject
- {
- // add q-object macro here (confuses qdoc if we do it here)
- Q_PROPERTY(bool hasClients READ hasClients STORED false BINDABLE bindableHasClients)
- public:
- QBindable<bool> bindableHasClients()
- {
- return QBindable<bool>(&d_func()->hasClientsData);
- }
- bool hasClients() const
- {
- return d_func()->hasClientsData.value();
- }
- void addClient(const Client &c)
- {
- Q_D(MyClass);
- d->clients.push_back(c);
- // notify that the value could have changed
- d->hasClientsData.markDirty();
- }
- private:
- Q_DECLARE_PRIVATE(MyClass)
- };
- \endcode
+ \snippet code/src_corelib_kernel_qproperty.cpp 5
The rules for getters in \l {Bindable Property Getters and Setters}
also apply for QObjectComputedProperty. Especially, the getter
@@ -1548,27 +1674,26 @@ QString QPropertyBindingError::description() const
have changed. Whenever a bindable property used in the callback changes,
this happens automatically. If the result of the callback might change
because of a change in a value which is not a bindable property,
- it is the developer's responsibility to call markDirty
+ it is the developer's responsibility to call \c notify
on the QObjectComputedProperty object.
This will inform dependent properties about the potential change.
- Note that calling markDirty might trigger change handlers in dependent
+ Note that calling \c notify might trigger change handlers in dependent
properties, which might in turn use the object the QObjectComputedProperty
- is a member of. So markDirty must not be called when in a transitional
+ is a member of. So \c notify must not be called when in a transitional
or invalid state.
QObjectComputedProperty is not suitable for use with a computation that depends
on any input that might change without notice, such as the contents of a file.
- \sa Q_OBJECT_COMPUTED_PROPERTY, QObjectBindableProperty, {Qt's Property System},
- {Qt Bindable Properties}
+ \sa Q_OBJECT_COMPUTED_PROPERTY, QProperty, QObjectBindableProperty,
+ {Qt's Property System}, {Qt Bindable Properties}
*/
/*!
\macro Q_OBJECT_COMPUTED_PROPERTY(containingClass, type, name, callback)
\since 6.0
- \relates QObjectCompatProperty
- \internal
+ \relates QObjectComputedProperty
\brief Declares a \l QObjectComputedProperty inside \a containingClass
of type \a type with name \a name. The argument \a callback specifies
a GETTER function to be called when the property is evaluated.
@@ -1597,30 +1722,35 @@ QString QPropertyBindingError::description() const
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Class *owner, const QPropertyBinding<T> &binding)
- Constructs a property that is tied to the provided \a binding expression. The
- first time the property value is read, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read. When the property value changes \a
- owner is notified via the Callback function.
+ Constructs a property that is tied to the provided \a binding expression.
+ The property's value is set to the result of evaluating the new binding.
+ Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
+ When the property value changes, \a owner is notified via the Callback
+ function.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Class *owner, QPropertyBinding<T> &&binding)
- Constructs a property that is tied to the provided \a binding expression. The
- first time the property value is read, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read. When the property value changes \a
+ Constructs a property that is tied to the provided \a binding expression.
+ The property's value is set to the result of evaluating the new binding.
+ Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
+ When the property value changes, \a
owner is notified via the Callback function.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Functor &&f)
- Constructs a property that is tied to the provided binding expression \a f. The
- first time the property value is read, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read.
+ Constructs a property that is tied to the provided binding expression \a f.
+ The property's value is set to the result of evaluating the new binding.
+ Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
*/
/*!
@@ -1648,14 +1778,15 @@ QString QPropertyBindingError::description() const
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::notify()
- Programmatically signals a change of the property. Any binding which depend on it will
- be notified, and if the property has a signal, it will be emitted.
+ Programmatically signals a change of the property. Any binding which depend on
+ it will be notified, and if the property has a signal, it will be emitted.
- This can be useful in combination with setValueBypassingBindings to defer signalling the change
- until a class invariant has been restored.
+ This can be useful in combination with setValueBypassingBindings to defer
+ signalling the change until a class invariant has been restored.
- \note If this property has a binding (i.e. hasBinding() returns true), that binding is not reevaluated when
- notify() is called. Any binding depending on this property is still reevaluated as usual.
+ \note If this property has a binding (i.e. hasBinding() returns true), that
+ binding is not reevaluated when notify() is called. Any binding depending on
+ this property is still reevaluated as usual.
\sa Qt::beginPropertyUpdateGroup(), setValueBypassingBindings()
*/
@@ -1664,11 +1795,12 @@ QString QPropertyBindingError::description() const
\fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::setBinding(const QPropertyBinding<T> &newBinding)
Associates the value of this property with the provided \a newBinding
- expression and returns the previously associated binding. The first time the
- property value is read, the binding is evaluated. Whenever a dependency of the
- binding changes, the binding will be re-evaluated the next time the value of
- this property is read. When the property value changes, the owner is notified
- via the Callback function.
+ expression and returns the previously associated binding.
+ The property's value is set to the result of evaluating the new binding. Whenever a dependency of
+ the binding changes, the binding will be re-evaluated,
+ and the property's value gets updated accordingly.
+ When the property value changes, the owner
+ is notified via the Callback function.
*/
/*!
@@ -1676,11 +1808,13 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of this property with the provided functor \a f and
- returns the previously associated binding. The first time the property value
- is read, the binding is evaluated by invoking the call operator () of \a f.
- Whenever a dependency of the binding changes, the binding will be re-evaluated
- the next time the value of this property is read. When the property value
- changes, the owner is notified via the Callback function.
+ returns the previously associated binding. The property's value is set to the
+ result of evaluating the new binding by invoking the call operator \c{()} of \a
+ f. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
+ When the property value changes, the owner is notified via the Callback
+ function.
\sa {Formulating a Property Binding}
*/
@@ -1690,12 +1824,13 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of this property with the provided \a newBinding
- expression. The first time the property value is read, the binding is evaluated.
- Whenever a dependency of the binding changes, the binding will be re-evaluated
- the next time the value of this property is read.
+ expression. The property's value is set to the result of evaluating the new
+ binding. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
- Returns \c true if the type of this property is the same as the type the binding
- function returns; \c false otherwise.
+
+ Returns \c true if the type of this property is the same as the type the
+ binding function returns; \c false otherwise.
*/
/*!
@@ -1725,47 +1860,49 @@ QString QPropertyBindingError::description() const
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyChangeHandler<T, Functor> QObjectBindableProperty<Class, T, offset, Callback>::onValueChanged(Functor f)
Registers the given functor \a f as a callback that shall be called whenever
- the value of the property changes. On each value change, the handler
- is either called immediately, or deferred, depending on the context.
+ the value of the property changes. On each value change, the handler is either
+ called immediately, or deferred, depending on the context.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the registration. When it
- goes out of scope, the callback is de-registered.
+ The returned property change handler object keeps track of the registration.
+ When it goes out of scope, the callback is de-registered.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyChangeHandler<T, Functor> QObjectBindableProperty<Class, T, offset, Callback>::subscribe(Functor f)
- Subscribes the given functor \a f as a callback that is called immediately and whenever
- the value of the property changes in the future. On each value change, the handler
- is either called immediately, or deferred, depending on the context.
+ Subscribes the given functor \a f as a callback that is called immediately and
+ whenever the value of the property changes in the future. On each value
+ change, the handler is either called immediately, or deferred, depending on
+ the context.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyNotifier QObjectBindableProperty<Class, T, offset, Callback>::addNotifier(Functor f)
- Subscribes the given functor \a f as a callback that is called whenever
- the value of the property changes.
+ Subscribes the given functor \a f as a callback that is called whenever the
+ value of the property changes.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
- This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
- It can therefore more easily be stored, e.g. as a member in a class.
+ This method is in some cases easier to use than onValueChanged(), as the
+ returned object is not a template. It can therefore more easily be stored,
+ e.g. as a member in a class.
\sa onValueChanged(), subscribe()
*/
@@ -1778,13 +1915,15 @@ QString QPropertyBindingError::description() const
/*!
\class QPropertyChangeHandler
\inmodule QtCore
- \brief The QPropertyChangeHandler class controls the lifecycle of change callback installed on a QProperty.
+ \brief The QPropertyChangeHandler class controls the lifecycle of change
+ callback installed on a QProperty.
\ingroup tools
- QPropertyChangeHandler\<Functor\> is created when registering a
- callback on a QProperty to listen to changes to the property's value, using QProperty::onValueChanged
- and QProperty::subscribe. As long as the change handler is alive, the callback remains installed.
+ QPropertyChangeHandler\<Functor\> is created when registering a callback on a
+ QProperty to listen to changes to the property's value, using
+ QProperty::onValueChanged and QProperty::subscribe. As long as the change
+ handler is alive, the callback remains installed.
A handler instance can be transferred between C++ scopes using move semantics.
*/
@@ -1796,9 +1935,9 @@ QString QPropertyBindingError::description() const
\ingroup tools
- QPropertyNotifier is created when registering a
- callback on a QProperty to listen to changes to the property's value, using QProperty::addNotifier.
- As long as the change handler is alive, the callback remains installed.
+ QPropertyNotifier is created when registering a callback on a QProperty to
+ listen to changes to the property's value, using QProperty::addNotifier. As
+ long as the change handler is alive, the callback remains installed.
A handler instance can be transferred between C++ scopes using move semantics.
*/
@@ -1808,7 +1947,8 @@ QString QPropertyBindingError::description() const
\inmodule QtCore
\internal
- \brief The QPropertyAlias class is a safe alias for a QProperty with same template parameter.
+ \brief The QPropertyAlias class is a safe alias for a QProperty with same
+ template parameter.
\ingroup tools
@@ -1886,33 +2026,14 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(T &&newValue)
- \overload
-
- Assigns \a newValue to the aliased property and returns a reference to this
- QPropertyAlias.
-*/
-
-/*!
- \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(const QPropertyBinding<T> &newBinding)
- \overload
-
- Associates the value of the aliased property with the provided \a newBinding
- expression and returns a reference to this alias. The first time the
- property value is read, either from the property itself or from any alias, the
- binding is evaluated. Whenever a dependency of the binding changes, the
- binding will be re-evaluated the next time the value of this property is read.
-*/
-
-/*!
\fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::setBinding(const QPropertyBinding<T> &newBinding)
Associates the value of the aliased property with the provided \a newBinding
expression and returns any previous binding the associated with the aliased
- property. The first time the property value is read, either from the property
- itself or from any alias, the binding is evaluated. Whenever a dependency of
- the binding changes, the binding will be re-evaluated the next time the value
- of this property is read.
+ property.The property's value is set to the result of evaluating the new
+ binding. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
Returns any previous binding associated with the property, or a
default-constructed QPropertyBinding<T>.
@@ -1923,10 +2044,10 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of the aliased property with the provided \a newBinding
- expression. The first time the property value is read, either from the
- property itself or from any alias, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read.
+ expression. The property's value is set to the result of evaluating the new
+ binding. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
Returns true if the type of this property is the same as the type the binding
function returns; false otherwise.
@@ -1937,10 +2058,10 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of the aliased property with the provided functor \a f
- expression. The first time the property value is read, either from the
- property itself or from any alias, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read.
+ expression. The property's value is set to the result of evaluating the new
+ binding. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
Returns any previous binding associated with the property, or a
default-constructed QPropertyBinding<T>.
@@ -1978,9 +2099,9 @@ QString QPropertyBindingError::description() const
the value of the aliased property changes. On each value change, the handler
is either called immediately, or deferred, depending on the context.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
The returned property change handler object keeps track of the registration. When it
goes out of scope, the callback is de-registered.
@@ -1989,16 +2110,17 @@ QString QPropertyBindingError::description() const
/*!
\fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QPropertyAlias<T>::subscribe(Functor f)
- Subscribes the given functor \a f as a callback that is called immediately and whenever
- the value of the aliased property changes in the future. On each value change, the handler
- is either called immediately, or deferred, depending on the context.
+ Subscribes the given functor \a f as a callback that is called immediately and
+ whenever the value of the aliased property changes in the future. On each
+ value change, the handler is either called immediately, or deferred, depending
+ on the context.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
*/
/*!
@@ -2007,15 +2129,16 @@ QString QPropertyBindingError::description() const
Subscribes the given functor \a f as a callback that is called whenever
the value of the aliased property changes.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
- This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
- It can therefore more easily be stored, e.g. as a member in a class.
+ This method is in some cases easier to use than onValueChanged(), as the
+ returned object is not a template. It can therefore more easily be stored,
+ e.g. as a member in a class.
\sa onValueChanged(), subscribe()
*/
@@ -2167,16 +2290,17 @@ QBindingStorage::~QBindingStorage()
QBindingStoragePrivate(d).destroy();
}
-void QBindingStorage::clear()
+void QBindingStorage::reinitAfterThreadMove()
{
- QBindingStoragePrivate(d).destroy();
- d = nullptr;
+ bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus);
+ Q_ASSERT(bindingStatus);
}
-// ### Unused, retained for BC with 6.0
-void QBindingStorage::maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const
+void QBindingStorage::clear()
{
- registerDependency_helper(data);
+ QBindingStoragePrivate(d).destroy();
+ d = nullptr;
+ bindingStatus = nullptr;
}
void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const
@@ -2184,9 +2308,20 @@ void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data
Q_ASSERT(bindingStatus);
// Use ::bindingStatus to get the binding from TLS. This is required, so that reads from
// another thread do not register as dependencies
- auto *currentBinding = QT_PREPEND_NAMESPACE(bindingStatus).currentlyEvaluatingBinding;
+ QtPrivate::BindingEvaluationState *currentBinding;
+#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
+ const bool threadMatches = (QThread::currentThreadId() == bindingStatus->threadId);
+ if (Q_LIKELY(threadMatches))
+ currentBinding = bindingStatus->currentlyEvaluatingBinding;
+ else
+ currentBinding = QT_PREPEND_NAMESPACE(bindingStatus).currentlyEvaluatingBinding;
+#else
+ currentBinding = QT_PREPEND_NAMESPACE(bindingStatus).currentlyEvaluatingBinding;
+#endif
QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data);
- auto storage = QBindingStoragePrivate(d).get(dd, /*create=*/ currentBinding != nullptr);
+ if (!currentBinding)
+ return;
+ auto storage = QBindingStoragePrivate(d).get(dd, true);
if (!storage)
return;
storage->registerWithCurrentlyEvaluatingBinding(currentBinding);
@@ -2198,6 +2333,11 @@ QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedProperty
return QBindingStoragePrivate(d).get(data);
}
+const QBindingStatus *QBindingStorage::status(QtPrivate::QBindingStatusAccessToken) const
+{
+ return bindingStatus;
+}
+
QPropertyBindingData *QBindingStorage::bindingData_helper(QUntypedPropertyData *data, bool create)
{
return QBindingStoragePrivate(d).get(data, create);
@@ -2205,6 +2345,13 @@ QPropertyBindingData *QBindingStorage::bindingData_helper(QUntypedPropertyData *
namespace QtPrivate {
+
+
+void initBindingStatusThreadId()
+{
+ bindingStatus.threadId = QThread::currentThreadId();
+}
+
BindingEvaluationState *suspendCurrentBindingStatus()
{
auto ret = bindingStatus.currentlyEvaluatingBinding;
@@ -2224,7 +2371,7 @@ void restoreBindingStatus(BindingEvaluationState *status)
of extra data for a QPropertyBindingStorage in a getter.
Note that this function accesses TLS storage, and is therefore soemwhat
costly to call.
- */
+*/
bool isAnyBindingEvaluating()
{
return bindingStatus.currentlyEvaluatingBinding != nullptr;
@@ -2232,8 +2379,10 @@ bool isAnyBindingEvaluating()
bool isPropertyInBindingWrapper(const QUntypedPropertyData *property)
{
- return bindingStatus.currentCompatProperty &&
- bindingStatus.currentCompatProperty->property == property;
+ // Accessing bindingStatus is expensive because it's thread-local. Do it only once.
+ if (const auto current = bindingStatus.currentCompatProperty)
+ return current->property == property;
+ return false;
}
namespace BindableWarnings {
@@ -2266,6 +2415,165 @@ void printMetaTypeMismatch(QMetaType actual, QMetaType expected)
} // namespace BindableWarnings end
+/*!
+ \internal
+ Returns the binding statusof the current thread.
+ */
+QBindingStatus* getBindingStatus(QtPrivate::QBindingStatusAccessToken) { return &QT_PREPEND_NAMESPACE(bindingStatus); }
+
+namespace PropertyAdaptorSlotObjectHelpers {
+void getter(const QUntypedPropertyData *d, void *value)
+{
+ auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ adaptor->bindingData().registerWithCurrentlyEvaluatingBinding();
+ auto mt = adaptor->metaProperty().metaType();
+ mt.destruct(value);
+ mt.construct(value, adaptor->metaProperty().read(adaptor->object()).data());
+}
+
+void setter(QUntypedPropertyData *d, const void *value)
+{
+ auto adaptor = static_cast<QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ adaptor->bindingData().removeBinding();
+ adaptor->metaProperty().write(adaptor->object(),
+ QVariant(adaptor->metaProperty().metaType(), value));
+}
+
+QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d)
+{
+ auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ return QUntypedPropertyBinding(adaptor->bindingData().binding());
+}
+
+bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
+ QtPrivate::QPropertyBindingFunction binding, QUntypedPropertyData *temp,
+ void *value)
+{
+ auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ type.destruct(value);
+ type.construct(value, adaptor->metaProperty().read(adaptor->object()).data());
+ if (binding.vtable->call(type, temp, binding.functor)) {
+ adaptor->metaProperty().write(adaptor->object(), QVariant(type, value));
+ return true;
+ }
+ return false;
+}
+
+QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding,
+ QPropertyBindingWrapper wrapper)
+{
+ auto adaptor = static_cast<QPropertyAdaptorSlotObject *>(d);
+ return adaptor->bindingData().setBinding(binding, d, nullptr, wrapper);
+}
+
+void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer)
+{
+ observer->setSource(static_cast<const QPropertyAdaptorSlotObject *>(d)->bindingData());
+}
+}
+
+QPropertyAdaptorSlotObject::QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty &p)
+ : QSlotObjectBase(&impl), obj(o), metaProperty_(p)
+{
+}
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+void QPropertyAdaptorSlotObject::impl(int which, QSlotObjectBase *this_, QObject *r, void **a,
+ bool *ret)
+#else
+void QPropertyAdaptorSlotObject::impl(QSlotObjectBase *this_, QObject *r, void **a, int which,
+ bool *ret)
+#endif
+{
+ auto self = static_cast<QPropertyAdaptorSlotObject *>(this_);
+ switch (which) {
+ case Destroy:
+ delete self;
+ break;
+ case Call:
+ if (!self->bindingData_.hasBinding())
+ self->bindingData_.notifyObservers(self);
+ break;
+ case Compare:
+ case NumOperations:
+ Q_UNUSED(r);
+ Q_UNUSED(a);
+ Q_UNUSED(ret);
+ break;
+ }
+}
+
} // namespace QtPrivate end
+QUntypedBindable::QUntypedBindable(QObject *obj, const QMetaProperty &metaProperty,
+ const QtPrivate::QBindableInterface *i)
+ : iface(i)
+{
+ if (!obj)
+ return;
+
+ if (!metaProperty.isValid()) {
+ qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property is not valid";
+ return;
+ }
+
+ if (metaProperty.isBindable()) {
+ *this = metaProperty.bindable(obj);
+ return;
+ }
+
+ if (!metaProperty.hasNotifySignal()) {
+ qCWarning(lcQPropertyBinding)
+ << "QUntypedBindable: Property" << metaProperty.name() << "has no notify signal";
+ return;
+ }
+
+ auto metatype = iface->metaType();
+ if (metaProperty.metaType() != metatype) {
+ qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
+ << "of type" << metaProperty.metaType().name()
+ << "does not match requested type" << metatype.name();
+ return;
+ }
+
+ // Test for name pointer equality proves it's exactly the same property
+ if (obj->metaObject()->property(metaProperty.propertyIndex()).name() != metaProperty.name()) {
+ qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
+ << "does not belong to this object";
+ return;
+ }
+
+ // Get existing binding data if it exists
+ auto adaptor = QObjectPrivate::get(obj)->getPropertyAdaptorSlotObject(metaProperty);
+
+ if (!adaptor) {
+ adaptor = new QPropertyAdaptorSlotObject(obj, metaProperty);
+
+ auto c = QObjectPrivate::connect(obj, metaProperty.notifySignalIndex(), obj, adaptor,
+ Qt::DirectConnection);
+ Q_ASSERT(c);
+ }
+
+ data = adaptor;
+}
+
+QUntypedBindable::QUntypedBindable(QObject *obj, const char *property,
+ const QtPrivate::QBindableInterface *i)
+ : QUntypedBindable(
+ obj,
+ [=]() -> QMetaProperty {
+ if (!obj)
+ return {};
+ auto propertyIndex = obj->metaObject()->indexOfProperty(property);
+ if (propertyIndex < 0) {
+ qCWarning(lcQPropertyBinding)
+ << "QUntypedBindable: No property named" << property;
+ return {};
+ }
+ return obj->metaObject()->property(propertyIndex);
+ }(),
+ i)
+{
+}
+
QT_END_NAMESPACE