qtbase/src/corelib/kernel/qproperty.cpp
Fabian Kosmale 7ddba3c23f Fix proxy-data handling
This addresses two different issues:
- Firstly, we were casting the resolved binding data pointer to
  QPropertyProxyBindingData, instead of the d_ptr of
  QPropertyBindingData. Fix this by introducing a helper function,
  and consistently using it to access the proxy data.
- Secondly, we were not resetting the originalBindingData when the
  pointed to object was destoyed. Fix that, too.

Task-number: QTBUG-110899
Change-Id: I7691c9df5cc26e761f6b0e5f16d152f7f2183208
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
(cherry picked from commit 55ca636180db2b7870b5b519c4163487b672a9f1)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
2023-02-07 21:27:51 +00:00

2359 lines
82 KiB
C++

// 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"
#include <qscopedvaluerollback.h>
#include <QScopeGuard>
#include <QtCore/qloggingcategory.h>
#include <QThread>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQPropertyBinding, "qt.qproperty.binding");
using namespace QtPrivate;
void QPropertyBindingPrivatePtr::destroyAndFreeMemory()
{
QPropertyBindingPrivate::destroyAndFreeMemory(static_cast<QPropertyBindingPrivate *>(d));
}
void QPropertyBindingPrivatePtr::reset(QtPrivate::RefCounted *ptr) noexcept
{
if (ptr != d) {
if (ptr)
ptr->ref++;
auto *old = qExchange(d, ptr);
if (old && (--old->ref == 0))
QPropertyBindingPrivate::destroyAndFreeMemory(static_cast<QPropertyBindingPrivate *>(d));
}
}
void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer)
{
if (auto *b = binding()) {
observer->prev = &b->firstObserver.ptr;
observer->next = b->firstObserver.ptr;
if (observer->next)
observer->next->prev = &observer->next;
b->firstObserver.ptr = observer;
} else {
auto &d = ptr->d_ref();
Q_ASSERT(!(d & QPropertyBindingData::BindingBit));
auto firstObserver = reinterpret_cast<QPropertyObserver*>(d);
observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
observer->next = firstObserver;
if (observer->next)
observer->next->prev = &observer->next;
d = reinterpret_cast<quintptr>(observer);
}
}
/*!
\internal
QPropertyDelayedNotifications is used to manage delayed notifications in grouped property updates.
It acts as a pool allocator for QPropertyProxyBindingData, and has methods to manage delayed
notifications.
\sa beginPropertyUpdateGroup, endPropertyUpdateGroup
*/
struct QPropertyDelayedNotifications
{
// we can't access the dynamic page size as we need a constant value
// use 4096 as a sensible default
static constexpr inline auto PageSize = 4096;
int ref = 0;
QPropertyDelayedNotifications *next = nullptr; // in case we have more than size dirty properties...
qsizetype used = 0;
// Size chosen to avoid allocating more than one page of memory, while still ensuring
// that we can store many delayed properties without doing further allocations
static constexpr qsizetype size = (PageSize - 3*sizeof(void *))/sizeof(QPropertyProxyBindingData);
QPropertyProxyBindingData delayedProperties[size];
/*!
\internal
This method is called when a property attempts to notify its observers while inside of a
property update group. Instead of actually notifying, it replaces \a bindingData's d_ptr
with a QPropertyProxyBindingData.
\a bindingData and \a propertyData are the binding data and property data of the property
whose notify call gets delayed.
\sa QPropertyBindingData::notifyObservers
*/
void addProperty(const QPropertyBindingData *bindingData, QUntypedPropertyData *propertyData) {
if (bindingData->isNotificationDelayed())
return;
auto *data = this;
while (data->used == size) {
if (!data->next)
// add a new page
data->next = new QPropertyDelayedNotifications;
data = data->next;
}
auto *delayed = data->delayedProperties + data->used;
*delayed = QPropertyProxyBindingData { bindingData->d_ptr, bindingData, propertyData };
++data->used;
// preserve the binding bit for faster access
quintptr bindingBit = bindingData->d_ptr & QPropertyBindingData::BindingBit;
bindingData->d_ptr = reinterpret_cast<quintptr>(delayed) | QPropertyBindingData::DelayedNotificationBit | bindingBit;
Q_ASSERT(bindingData->d_ptr > 3);
if (!bindingBit) {
if (auto observer = reinterpret_cast<QPropertyObserver *>(delayed->d_ptr))
observer->prev = reinterpret_cast<QPropertyObserver **>(&delayed->d_ptr);
}
}
/*!
\internal
Called in Qt::endPropertyUpdateGroup. For the QPropertyProxyBindingData at position
\a index, it
\list
\li restores the original binding data that was modified in addProperty and
\li evaluates any bindings which depend on properties that were changed inside
the group.
\endlist
Change notifications are sent later with notify (following the logic of separating
binding updates and notifications used in non-deferred updates).
*/
[[nodiscard]] PendingBindingObserverList evaluateBindings(qsizetype index, QBindingStatus *status) {
PendingBindingObserverList bindingObservers;
auto *delayed = delayedProperties + index;
auto *bindingData = delayed->originalBindingData;
if (!bindingData)
return bindingObservers;
bindingData->d_ptr = delayed->d_ptr;
Q_ASSERT(!(bindingData->d_ptr & QPropertyBindingData::DelayedNotificationBit));
if (!bindingData->hasBinding()) {
if (auto observer = reinterpret_cast<QPropertyObserver *>(bindingData->d_ptr))
observer->prev = reinterpret_cast<QPropertyObserver **>(&bindingData->d_ptr);
}
QPropertyBindingDataPointer bindingDataPointer{bindingData};
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
observer.evaluateBindings(bindingObservers, status);
return bindingObservers;
}
/*!
\internal
Called in Qt::endPropertyUpdateGroup. For the QPropertyProxyBindingData at position
\a i, it
\list
\li resets the proxy binding data and
\li sends any pending notifications.
\endlist
*/
void notify(qsizetype index) {
auto *delayed = delayedProperties + index;
auto *bindingData = delayed->originalBindingData;
if (!bindingData)
return;
delayed->originalBindingData = nullptr;
delayed->d_ptr = 0;
QPropertyBindingDataPointer bindingDataPointer{bindingData};
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
observer.notify(delayed->propertyData);
}
};
Q_CONSTINIT static thread_local QBindingStatus bindingStatus;
/*!
\since 6.2
\relates QProperty
Marks the beginning of a property update group. Inside this group,
changing a property does neither immediately update any dependent properties
nor does it trigger change notifications.
Those are instead deferred until the group is ended by a call to endPropertyUpdateGroup.
Groups can be nested. In that case, the deferral ends only after the outermost group has been
ended.
\note Change notifications are only send after all property values affected by the group have
been updated to their new values. This allows re-establishing a class invariant if multiple
properties need to be updated, preventing any external observer from noticing an inconsistent
state.
\sa Qt::endPropertyUpdateGroup
*/
void Qt::beginPropertyUpdateGroup()
{
QPropertyDelayedNotifications *& groupUpdateData = bindingStatus.groupUpdateData;
if (!groupUpdateData)
groupUpdateData = new QPropertyDelayedNotifications;
++groupUpdateData->ref;
}
/*!
\since 6.2
\relates QProperty
Ends a property update group. If the outermost group has been ended, and deferred
binding evaluations and notifications happen now.
\warning Calling endPropertyUpdateGroup without a preceding call to beginPropertyUpdateGroup
results in undefined behavior.
\sa Qt::beginPropertyUpdateGroup
*/
void Qt::endPropertyUpdateGroup()
{
auto status = &bindingStatus;
QPropertyDelayedNotifications *& groupUpdateData = status->groupUpdateData;
auto *data = groupUpdateData;
Q_ASSERT(data->ref);
if (--data->ref)
return;
groupUpdateData = nullptr;
// update all delayed properties
auto start = data;
while (data) {
for (qsizetype i = 0; i < data->used; ++i) {
PendingBindingObserverList bindingObserves = data->evaluateBindings(i, status);
Q_UNUSED(bindingObserves);
// ### TODO: Use bindingObservers for notify
}
data = data->next;
}
// notify all delayed properties
data = start;
while (data) {
for (qsizetype i = 0; i < data->used; ++i)
data->notify(i);
auto *next = data->next;
delete data;
data = next;
}
}
// 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)
firstObserver.unlink();
if (vtable->size)
vtable->destroy(reinterpret_cast<std::byte *>(this)
+ 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)
destroyAndFreeMemory(this);
}
void QPropertyBindingPrivate::evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
{
if (!status)
status = &bindingStatus;
return evaluateRecursive_inline(bindingObservers, status);
}
void QPropertyBindingPrivate::notifyRecursive()
{
if (!pendingNotify)
return;
pendingNotify = false;
Q_ASSERT(!updating);
updating = true;
if (firstObserver) {
firstObserver.noSelfDependencies(this);
firstObserver.notify(propertyDataPtr);
}
if (hasStaticObserver)
staticObserverCallback(propertyDataPtr);
updating = false;
}
void QPropertyBindingPrivate::notifyNonRecursive(const PendingBindingObserverList &bindingObservers)
{
notifyNonRecursive();
for (auto &&bindingObserver: bindingObservers) {
bindingObserver.binding()->notifyNonRecursive();
}
}
QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRecursive()
{
if (!pendingNotify)
return Delayed;
pendingNotify = false;
Q_ASSERT(!updating);
updating = true;
if (firstObserver) {
firstObserver.noSelfDependencies(this);
firstObserver.notifyOnlyChangeHandler(propertyDataPtr);
}
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)
{
std::byte *mem = new std::byte[QPropertyBindingPrivate::getSizeEnsuringAlignment() + vtable->size]();
d = new(mem) QPropertyBindingPrivate(metaType, vtable, std::move(location));
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)
return QPropertyBindingError();
return static_cast<QPropertyBindingPrivate *>(d.get())->bindingError();
}
/*!
Returns the meta-type of the binding.
If the QUntypedProperyBinding is null, an invalid QMetaType is returned.
*/
QMetaType QUntypedPropertyBinding::valueMetaType() const
{
if (!d)
return QMetaType();
return static_cast<QPropertyBindingPrivate *>(d.get())->valueMetaType();
}
QPropertyBindingData::~QPropertyBindingData()
{
QPropertyBindingDataPointer d{this};
if (isNotificationDelayed())
proxyData()->originalBindingData = nullptr;
for (auto observer = d.firstObserver(); observer;) {
auto next = observer.nextObserver();
observer.unlink();
observer = next;
}
if (auto binding = d.binding())
binding->unlinkAndDeref();
}
QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyBinding &binding,
QUntypedPropertyData *propertyDataPtr,
QPropertyObserverCallback staticObserverCallback,
QtPrivate::QPropertyBindingWrapper guardCallback)
{
QPropertyBindingPrivatePtr oldBinding;
QPropertyBindingPrivatePtr newBinding = binding.d;
QPropertyBindingDataPointer d{this};
QPropertyObserverPointer observer;
auto &data = d_ref();
if (auto *existingBinding = d.binding()) {
if (existingBinding == newBinding.data())
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
if (existingBinding->isUpdating()) {
existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")});
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
}
oldBinding = QPropertyBindingPrivatePtr(existingBinding);
observer = static_cast<QPropertyBindingPrivate *>(oldBinding.data())->takeObservers();
static_cast<QPropertyBindingPrivate *>(oldBinding.data())->unlinkAndDeref();
data = 0;
} else {
observer = d.firstObserver();
}
if (newBinding) {
newBinding.data()->addRef();
data = reinterpret_cast<quintptr>(newBinding.data());
data |= BindingBit;
auto newBindingRaw = static_cast<QPropertyBindingPrivate *>(newBinding.data());
newBindingRaw->setProperty(propertyDataPtr);
if (observer)
newBindingRaw->prependObserver(observer);
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
PendingBindingObserverList bindingObservers;
newBindingRaw->evaluateRecursive(bindingObservers);
newBindingRaw->notifyNonRecursive(bindingObservers);
} else if (observer) {
d.setObservers(observer.ptr);
} else {
data = 0;
}
if (oldBinding)
static_cast<QPropertyBindingPrivate *>(oldBinding.data())->detachFromProperty();
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
}
QPropertyBindingData::QPropertyBindingData(QPropertyBindingData &&other) : d_ptr(std::exchange(other.d_ptr, 0))
{
QPropertyBindingDataPointer::fixupAfterMove(this);
}
BindingEvaluationState::BindingEvaluationState(QPropertyBindingPrivate *binding, QBindingStatus *status)
: binding(binding)
{
Q_ASSERT(status);
QBindingStatus *s = status;
// 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;
previousState = *currentState;
*currentState = this;
binding->clearDependencyObservers();
}
CompatPropertySafePoint::CompatPropertySafePoint(QBindingStatus *status, QUntypedPropertyData *property)
: property(property)
{
// store a pointer to the currentBindingEvaluationState to avoid a TLS lookup in
// the destructor (as these come with a non zero cost)
currentState = &status->currentCompatProperty;
previousState = *currentState;
*currentState = this;
currentlyEvaluatingBindingList = &bindingStatus.currentlyEvaluatingBinding;
bindingState = *currentlyEvaluatingBindingList;
*currentlyEvaluatingBindingList = nullptr;
}
QPropertyBindingPrivate *QPropertyBindingPrivate::currentlyEvaluatingBinding()
{
auto currentState = bindingStatus.currentlyEvaluatingBinding ;
return currentState ? currentState->binding : nullptr;
}
// ### Unused, kept for BC with 6.0
void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *) const
{
}
void QPropertyBindingData::removeBinding_helper()
{
QPropertyBindingDataPointer d{this};
auto *existingBinding = d.binding();
Q_ASSERT(existingBinding);
if (existingBinding->isSticky()) {
return;
}
auto observer = existingBinding->takeObservers();
d_ref() = 0;
if (observer)
d.setObservers(observer.ptr);
existingBinding->unlinkAndDeref();
}
void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding() const
{
auto currentState = bindingStatus.currentlyEvaluatingBinding;
if (!currentState)
return;
registerWithCurrentlyEvaluatingBinding_helper(currentState);
}
void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentState) const
{
QPropertyBindingDataPointer d{this};
QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
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()) {
if (notifyObserver_helper(propertyDataPtr, storage, observer, bindingObservers) == Evaluated) {
// evaluateBindings() can trash the observers. We need to re-fetch here.
if (QPropertyObserverPointer observer = d.firstObserver())
observer.notifyOnlyChangeHandler(propertyDataPtr);
for (auto &&bindingObserver: bindingObservers)
bindingObserver.binding()->notifyNonRecursive();
}
}
}
QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_helper
(
QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage,
QPropertyObserverPointer observer,
PendingBindingObserverList &bindingObservers) const
{
#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 *data)
{
aliasData = data;
next.setTag(ObserverIsAlias);
}
/*! \internal
*/
void QPropertyObserver::setSource(const QPropertyBindingData &property)
{
QPropertyObserverPointer d{this};
QPropertyBindingDataPointer propPrivate{&property};
d.observeProperty(propPrivate);
}
QPropertyObserver::~QPropertyObserver()
{
QPropertyObserverPointer d{this};
d.unlink();
}
QPropertyObserver::QPropertyObserver(QPropertyObserver &&other) noexcept
{
binding = std::exchange(other.binding, {});
next = std::exchange(other.next, {});
prev = std::exchange(other.prev, {});
if (next)
next->prev = &next;
if (prev)
prev.setPointer(this);
}
QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexcept
{
if (this == &other)
return *this;
QPropertyObserverPointer d{this};
d.unlink();
binding = nullptr;
binding = std::exchange(other.binding, {});
next = std::exchange(other.next, {});
prev = std::exchange(other.prev, {});
if (next)
next->prev = &next;
if (prev)
prev.setPointer(this);
return *this;
}
/*!
\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)
{
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
ptr->changeHandler = changeHandler;
ptr->next.setTag(QPropertyObserver::ObserverNotifiesChangeHandler);
}
void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *binding)
{
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
ptr->binding = binding;
ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
}
/*!
\internal
The same as as setBindingToNotify, but assumes that the tag is already correct.
*/
void QPropertyObserverPointer::setBindingToNotify_unsafe(QPropertyBindingPrivate *binding)
{
Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding);
ptr->binding = binding;
}
/*!
\class QPropertyObserverNodeProtector
\internal
QPropertyObserverNodeProtector is a RAII wrapper which takes care of the internal switching logic
for QPropertyObserverPointer::notify (described ibidem)
*/
/*!
\fn QPropertyObserverNodeProtector::notify(QUntypedPropertyData *propertyDataPtr)
\internal
\a propertyDataPtr is a pointer to the observed property's property data
*/
#ifndef QT_NO_DEBUG
void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *binding)
{
auto observer = const_cast<QPropertyObserver*>(ptr);
// See also comment in notify()
while (observer) {
if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding)
if (observer->binding == binding) {
qCritical("Property depends on itself!");
break;
}
observer = observer->next.data();
}
}
#endif
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) {
bindingObservers.push_back(observer);
auto bindingToEvaluate = observer->binding;
QPropertyObserverNodeProtector protector(observer);
bindingToEvaluate->evaluateRecursive_inline(bindingObservers, status);
next = protector.next();
}
observer = next;
}
}
void QPropertyObserverPointer::observeProperty(QPropertyBindingDataPointer property)
{
if (ptr->prev)
unlink();
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) {
d = new QPropertyBindingErrorPrivate;
d->type = type;
d->description = description;
}
}
/*!
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)
return QPropertyBindingError::NoError;
return d->type;
}
/*!
Returns a descriptive error message for the QPropertyBindingError if
it has been set.
*/
QString QPropertyBindingError::description() const
{
if (!d)
return QString();
return d->description;
}
/*!
\class QPropertyData
\inmodule QtCore
\brief The QPropertyData class is a helper class for properties with automatic property bindings.
\since 6.0
\ingroup tools
QPropertyData\<T\> is a common base class for classes that can hold properties with automatic
data bindings. It mainly wraps the stored data, and offers low level access to that data.
The low level access to the data provided by this class bypasses the binding mechanism, and should be
used with care, as updates to the values will not get propagated to any bindings that depend on this
property.
You should usually call value() and setValue() on QProperty<T> or QObjectBindableProperty<T>, not use
the low level mechanisms provided in this class.
*/
/*! \fn template <typename T> QPropertyData<T>::parameter_type QPropertyData<T>::valueBypassingBindings() const
Returns the data stored in this property.
\note As this will bypass any binding evaluation it might return an outdated value if a
binding is set on this property. Using this method will also not register the property
access with any currently executing binding.
*/
/*! \fn template <typename T> void QPropertyData<T>::setValueBypassingBindings(parameter_type v)
Sets the data value stored in this property to \a v.
\note Using this method will bypass any potential binding registered for this property.
*/
/*! \fn template <typename T> void QPropertyData<T>::setValueBypassingBindings(rvalue_ref v)
\overload
Sets the data value stored in this property to \a v.
\note Using this method will bypass any potential binding registered for this property.
*/
/*!
\class QUntypedBindable
\inmodule QtCore
\brief QUntypedBindable is a uniform interface over bindable properties like \c QProperty\<T\>
and \c QObjectBindableProperty of any type \c T.
\since 6.0
\ingroup tools
QUntypedBindable is a fully type-erased generic interface to wrap bindable properties.
You can use it to interact with properties without knowing their type nor caring what
kind of bindable property they are (e.g. QProperty or QObjectBindableProperty).
For most use cases, using QBindable\<T\> (which is generic over the property implementation
but has a fixed type) should be preferred.
*/
/*!
\fn QUntypedBindable::QUntypedBindable()
Default-constructs a QUntypedBindable. It is in an invalid state.
\sa isValid()
*/
/*!
\fn template<typename Property> QUntypedBindable::QUntypedBindable(Property *property)
Constructs a QUntypedBindable from the property \a property. If Property is const,
the QUntypedBindable will be read only. If \a property is null, the QUntypedBindable
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
\internal
Returns true if the underlying property's binding can be queried
with binding() and, if not read-only, changed with setBinding.
Only QObjectComputedProperty currently leads to this method returning
false.
\sa isReadOnly()
*/
/*!
\fn QUntypedPropertyBinding QUntypedBindable::makeBinding(const QPropertyBindingSourceLocation &location) const
Creates a binding returning the underlying properties' value, using a specified source \a location.
*/
/*!
\fn void QUntypedBindable::observe(QPropertyObserver *observer)
\internal
Installs the observer on the underlying property.
*/
/*!
\fn template<typename Functor> QPropertyChangeHandler<Functor> QUntypedBindable::onValueChanged(Functor f) const
Installs \a f as a change handler. Whenever the underlying property changes, \a f will be called, as
long as the returned \c QPropertyChangeHandler and the property are kept alive.
On each value change, the handler is either called immediately, or deferred, depending on the context.
\sa onValueChanged(), subscribe()
*/
/*!
\fn template<typename Functor> QPropertyChangeHandler<Functor> QUntypedBindable::subscribe(Functor f) const
Behaves like a call to \a f followed by \c onValueChanged(f),
\sa onValueChanged()
*/
/*!
\fn template<typename Functor> QPropertyNotifier QUntypedBindable::addNotifier(Functor f)
Installs \a f as a change handler. Whenever the underlying property changes, \a f will be called, as
long as the returned \c QPropertyNotifier and the property are kept alive.
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()
*/
/*!
\fn QUntypedPropertyBinding QUntypedBindable::binding() const
Returns the underlying property's binding if there is any, or a default
constructed QUntypedPropertyBinding otherwise.
\sa hasBinding()
*/
/*!
\fn QUntypedPropertyBinding QUntypedBindable::takeBinding()
Removes the currently set binding from the property and returns it.
Returns a default-constructed QUntypedPropertyBinding if no binding is set.
\since 6.1
*/
/*!
\fn bool QUntypedBindable::setBinding(const QUntypedPropertyBinding &binding)
Sets the underlying property's binding to \a binding. This does not have any effect
if the QUntypedBindable is read-only, null or if \a binding's type does match the
underlying property's type.
\return \c true when the binding was successfully set.
//! \sa QUntypedPropertyBinding::valueMetaType()
*/
/*!
\fn bool QUntypedBindable::hasBinding() const
Returns \c true if the underlying property has a binding.
*/
/*!
\fn QMetaType QUntypedBindable::metaType() const
\since 6.2
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()
//! \sa QUntypedPropertyBinding::valueMetaType()
*/
/*!
\class QBindable
\inmodule QtCore
\brief QBindable is a wrapper class around binding-enabled properties. It allows type-safe
operations while abstracting the differences between the various property classes away.
\inherits QUntypedBindable
\ingroup tools
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
constructed from the QProperty. The returned QBindable allows users of the property to set
and query bindings of the property, without having to know the exact kind of binding-enabled
property used.
\snippet code/src_corelib_kernel_qproperty.cpp 0
\snippet code/src_corelib_kernel_qproperty.cpp 3
\sa QMetaProperty::isBindable, QProperty, QObjectBindableProperty,
QObjectComputedProperty, {Qt Bindable Properties}
*/
/*!
\fn template<typename T> QPropertyBinding<T> QBindable<T>::makeBinding(const QPropertyBindingSourceLocation &location) const
Constructs a binding evaluating to the underlying property's value, using a specified source
\a location.
*/
/*!
\fn template <typename T> QPropertyBinding<T> QBindable<T>::binding() const
Returns the currently set binding of the underlying property. If the property does not
have a binding, the returned \c QPropertyBinding<T> will be invalid.
\sa setBinding, hasBinding
//! \sa QPropertyBinding::isValid()
*/
/*!
\fn template <typename T> QPropertyBinding<T> QBindable<T>::takeBinding()
Removes the currently set binding of the underlying property and returns it.
If the property does not have a binding, the returned \c QPropertyBinding<T> will be invalid.
\sa binding, setBinding, hasBinding
//! \sa QPropertyBinding::isValid()
*/
/*!
\fn template <typename T> void QBindable<T>::setBinding(const QPropertyBinding<T> &binding)
Sets the underlying property's binding to \a binding. Does nothing if the QBindable is
read-only or invalid.
\sa binding, isReadOnly(), isValid()
//! \sa QPropertyBinding::isValid()
*/
/*!
\fn template <typename T> template <typename Functor> QPropertyBinding<T> QBindable<T>::setBinding(Functor f);
\overload
Creates a \c QPropertyBinding<T> from \a f, and sets it as the underlying property's binding.
*/
/*!
\fn template <typename T> T QBindable<T>::value() const
Returns the underlying property's current value. If the QBindable is invalid,
a default constructed \c T is returned.
\sa isValid()
*/
/*!
\fn template <typename T> void QBindable<T>::setValue(const T &value)
Sets the underlying property's value to \a value. This removes any currenltly set
binding from it. This function has no effect if the QBindable is read-only or invalid.
\sa isValid(), isReadOnly(), setBinding()
*/
/*!
\class QProperty
\inmodule QtCore
\brief The QProperty class is a template class that enables automatic property bindings.
\since 6.0
\ingroup tools
QProperty\<T\> is one of the classes implementing \l {Qt Bindable Properties}.
It is a container that holds an instance of T. You can assign
a value to it and you can read it via the value() function or the T conversion
operator. You can also tie the property to an expression that computes the value
dynamically, the binding expression. It is represented as a C++ lambda and
can be used to express relationships between different properties in your
application.
*/
/*!
\fn template <typename T> QProperty<T>::QProperty()
Constructs a property with a default constructed instance of T.
*/
/*!
\fn template <typename T> explicit QProperty<T>::QProperty(const T &initialValue)
Constructs a property with the provided \a initialValue.
*/
/*!
\fn template <typename T> explicit QProperty<T>::QProperty(T &&initialValue)
Move-Constructs a property with the provided \a initialValue.
*/
/*!
\fn template <typename T> QProperty<T>::QProperty(QProperty<T> &&other)
Move-constructs a QProperty instance, making it point at the same object that
\a other was pointing to.
*/
/*!
\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.
*/
/*!
\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.
*/
/*!
\fn template <typename T> QProperty<T>::~QProperty()
Destroys the property.
*/
/*!
\fn template <typename T> T QProperty<T>::value() const
Returns the value of the property. This may evaluate a binding expression that
is tied to this property, before returning the value.
*/
/*!
\fn template <typename T> void QProperty<T>::setValue(rvalue_ref newValue)
\fn template <typename T> void QProperty<T>::setValue(parameter_type newValue)
Assigns \a newValue to this property and removes the property's associated
binding, if present.
*/
/*!
\fn template <typename T> QProperty<T> &QProperty<T>::operator=(rvalue_ref newValue)
\fn template <typename T> QProperty<T> &QProperty<T>::operator=(parameter_type newValue)
Assigns \a newValue to this property and returns a reference to this QProperty.
*/
/*!
\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.
*/
/*!
\fn template <typename T> template <typename Functor> QPropertyBinding<T> QProperty<T>::setBinding(Functor f)
\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.
\sa {Formulating a Property Binding}
*/
/*!
\fn template <typename T> QPropertyBinding<T> bool QProperty<T>::setBinding(const QUntypedPropertyBinding &newBinding)
\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.
Returns true if the type of this property is the same as the type the binding
function returns; false otherwise.
*/
/*!
\fn template <typename T> QPropertyBinding<T> QProperty<T>::binding() const
Returns the binding expression that is associated with this property. A
default constructed QPropertyBinding<T> will be returned if no such
association exists.
*/
/*!
\fn template <typename T> QPropertyBinding<T> QProperty<T>::takeBinding()
Disassociates the binding expression from this property and returns it. After
calling this function, the value of the property will only change if you
assign a new value to it, or when a new binding is set.
*/
/*!
\fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QProperty<T>::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 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 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.
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.
*/
/*!
\fn template <typename T> template <typename Functor> QPropertyNotifier QProperty<T>::addNotifier(Functor f)
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 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.
\sa onValueChanged(), subscribe()
*/
/*!
\fn template <typename T> QtPrivate::QPropertyBindingData &QProperty<T>::bindingData() const
\internal
*/
/*!
\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.
\since 6.0
\ingroup tools
QObjectBindableProperty is a generic container that holds an
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 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}.
In order to invoke the change signal on property changes, use
QObjectBindableProperty and pass the change signal as a callback.
A simple example is given in the following.
\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.
Use the Q_OBJECT_BINDABLE_PROPERTY macro in the class declaration to declare
the property as bindable.
\snippet code/src_corelib_kernel_qproperty.cpp 0
If you need to directly initialize the property with some non-default value,
you can use the Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS macro. It accepts a
value for the initialization as one of its parameters.
\snippet code/src_corelib_kernel_qproperty.cpp 1
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS does not support multiple arguments
directly. If your property requires multiple arguments for initialization,
please explicitly call the specific constructor.
\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.
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.
\sa {Qt's Property System}, {Qt Bindable Properties}
*/
/*!
\macro Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(containingClass, type, name, initialvalue, signal)
\since 6.0
\relates QObjectBindableProperty
\brief Declares a \l QObjectBindableProperty inside \a containingClass
of type \a type with name \a name which is initialized to \a initialvalue.
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}
*/
/*!
\class QObjectCompatProperty
\inmodule QtCore
\brief The QObjectCompatProperty class is a template class to help port old
properties to the bindable property system.
\since 6.0
\ingroup tools
\internal
QObjectCompatProperty is a generic container that holds an
instance of \c T and behaves mostly like QProperty, just like
QObjectBindableProperty. It's one of the Qt internal classes implementing
\l {Qt Bindable Properties}. Like QObjectBindableProperty,
QObjectCompatProperty stores its management data structure in the surrounding
QObject. The last template parameter specifies a method (of the owning
class) to be called when the property is changed through the binding.
This is usually a setter.
As explained in \l {Qt Bindable Properties}, getters and setters for bindable
properties have to be almost trivial to be correct. However, in legacy code,
there is often complex logic in the setter. QObjectCompatProperty is a helper
to port these properties to the bindable property system.
With QObjectCompatProperty, the same rules as described in
\l {Bindable Property Getters and Setters} hold for the getter.
For the setter, the rules are different. It remains that every possible code
path in the setter must write to the underlying QObjectCompatProperty,
otherwise calling the setter might not remove a pre-existing binding, as
it should. However, as QObjectCompatProperty will call the setter on every
change, the setter is allowed to contain code like updating class internals
or emitting signals. Every write to the QObjectCompatProperty has to
be analyzed carefully to comply with the rules given in
\l {Writing to a Bindable Property}.
\section2 Properties with Virtual Setters
Some of the pre-existing Qt classes (for example, \l QAbstractProxyModel)
have properties with virtual setters. Special care must be taken when
making such properties bindable.
For the binding to work properly, the property must be correctly handled in
all reimplemented methods of each derived class.
Unless the derived class has access to the underlying property object, the
base implementation \e must be called for the binding to work correctly.
If the derived class can directly access the property instance, there is no
need to explicitly call the base implementation, but the property's value
\e must be correctly updated.
Refer to \l {Bindable Properties with Virtual Setters and Getters} for more
details.
In both cases the expected behavior \e must be documented in the property's
documentation, so that users can correctly override the setter.
Properties for which these conditions cannot be met should not be made
bindable.
\sa Q_OBJECT_COMPAT_PROPERTY, QObjectBindableProperty, {Qt's Property System}, {Qt Bindable
Properties}
*/
/*!
\macro Q_OBJECT_COMPAT_PROPERTY(containingClass, type, name, callback)
\since 6.0
\relates QObjectCompatProperty
\internal
\brief Declares a \l QObjectCompatProperty inside \a containingClass
of type \a type with name \a name. The argument \a callback specifies
a setter function to be called when the property is changed through the binding.
\sa QObjectBindableProperty, {Qt's Property System}, {Qt Bindable Properties}
*/
/*!
\macro Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(containingClass, type, name, callback, value)
\since 6.0
\relates QObjectCompatProperty
\internal
\brief Declares a \l QObjectCompatProperty inside of \a containingClass
of type \a type with name \a name. The argument \a callback specifies
a setter function to be called when the property is changed through the binding.
\a value specifies an initialization value.
*/
/*!
\class QObjectComputedProperty
\inmodule QtCore
\brief The QObjectComputedProperty class is a template class to help port old
properties to the bindable property system.
\since 6.0
\ingroup tools
QObjectComputedProperty is a read-only property which is recomputed on each read.
It does not store the computed value.
It is one of the Qt internal classes implementing \l {Qt Bindable Properties}.
QObjectComputedProperty is usually not used directly, instead an instance of it is created by
using the Q_OBJECT_COMPUTED_PROPERTY macro.
See the following example.
\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
should be trivial and only return the value of the QObjectComputedProperty object.
The callback given to the QObjectComputedProperty should usually be a private
method which is only called by the QObjectComputedProperty.
No setter is required or allowed, as QObjectComputedProperty is read-only.
To correctly participate in dependency handling, QObjectComputedProperty
has to know when its value, the result of the callback given to it, might
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
on the QObjectComputedProperty object.
This will inform dependent properties about the potential change.
Note that calling markDirty 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
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, QProperty, QObjectBindableProperty,
{Qt's Property System}, {Qt Bindable Properties}
*/
/*!
\macro Q_OBJECT_COMPUTED_PROPERTY(containingClass, type, name, callback)
\since 6.0
\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.
\sa QObjectBindableProperty, {Qt's Property System}, {Qt Bindable Properties}
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty()
Constructs a property with a default constructed instance of T.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> explicit QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(const T &initialValue)
Constructs a property with the provided \a initialValue.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> explicit QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(T &&initialValue)
Move-Constructs a property with the provided \a initialValue.
*/
/*!
\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.
*/
/*!
\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
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.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::~QObjectBindableProperty()
Destroys the property.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> T QObjectBindableProperty<Class, T, offset, Callback>::value() const
Returns the value of the property. This may evaluate a binding expression that
is tied to this property, before returning the value.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::setValue(parameter_type newValue)
\fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::setValue(rvalue_ref newValue)
Assigns \a newValue to this property and removes the property's associated
binding, if present. If the property value changes as a result, calls the
Callback function on \a owner.
*/
/*!
\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.
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.
\sa Qt::beginPropertyUpdateGroup(), setValueBypassingBindings()
*/
/*!
\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.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::setBinding(Functor f)
\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.
\sa {Formulating a Property Binding}
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> bool QObjectBindableProperty<Class, T, offset, Callback>::setBinding(const QUntypedPropertyBinding &newBinding)
\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.
Returns \c true if the type of this property is the same as the type the binding
function returns; \c false otherwise.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> bool QObjectBindableProperty<Class, T, offset, Callback>::hasBinding() const
Returns true if the property is associated with a binding; false otherwise.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::binding() const
Returns the binding expression that is associated with this property. A
default constructed QPropertyBinding<T> will be returned if no such
association exists.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::takeBinding()
Disassociates the binding expression from this property and returns it. After
calling this function, the value of the property will only change if you
assign a new value to it, or when a new binding is set.
*/
/*!
\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 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 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.
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 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.
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 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.
\sa onValueChanged(), subscribe()
*/
/*!
\fn template <typename T> QtPrivate::QPropertyBase &QObjectBindableProperty<Class, T, offset, Callback>::propertyBase() const
\internal
*/
/*!
\class QPropertyChangeHandler
\inmodule QtCore
\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.
A handler instance can be transferred between C++ scopes using move semantics.
*/
/*!
\class QPropertyNotifier
\inmodule QtCore
\brief The QPropertyNotifier class controls the lifecycle of change callback installed on a QProperty.
\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.
A handler instance can be transferred between C++ scopes using move semantics.
*/
/*!
\class QPropertyAlias
\inmodule QtCore
\internal
\brief The QPropertyAlias class is a safe alias for a QProperty with same template parameter.
\ingroup tools
QPropertyAlias\<T\> wraps a pointer to a QProperty\<T\> and automatically
invalidates itself when the QProperty\<T\> is destroyed. It forwards all
method invocations to the wrapped property. For example:
\code
QProperty<QString> *name = new QProperty<QString>("John");
QProperty<int> age(41);
QPropertyAlias<QString> nameAlias(name);
QPropertyAlias<int> ageAlias(&age);
QProperty<QString> fullname;
fullname.setBinding([&]() { return nameAlias.value() + " age: " + QString::number(ageAlias.value()); });
qDebug() << fullname.value(); // Prints "John age: 41"
*name = "Emma"; // Marks binding expression as dirty
qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma age: 41"
// Birthday is coming up
ageAlias.setValue(age.value() + 1); // Writes the age property through the alias
qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma age: 42"
delete name; // Leaves the alias in an invalid, but accessible state
nameAlias.setValue("Eve"); // Ignored: nameAlias carries a default-constructed QString now
ageAlias.setValue(92);
qDebug() << fullname.value(); // Re-evaluates the binding expression and prints " age: 92"
\endcode
*/
/*!
\fn template <typename T> QPropertyAlias<T>::QPropertyAlias(QProperty<T> *property)
Constructs a property alias for the given \a property.
*/
/*!
\fn template <typename T> explicit QPropertyAlias<T>::QPropertyAlias(QPropertyAlias<T> *alias)
Constructs a property alias for the property aliased by \a alias.
*/
/*!
\fn template <typename T> T QPropertyAlias<T>::value() const
Returns the value of the aliased property. This may evaluate a binding
expression that is tied to the property, before returning the value.
*/
/*!
\fn template <typename T> QPropertyAlias<T>::operator T() const
Returns the value of the aliased property. This may evaluate a binding
expression that is tied to the property, before returning the value.
*/
/*!
\fn template <typename T> void QPropertyAlias<T>::setValue(const T &newValue)
Assigns \a newValue to the aliased property and removes the property's
associated binding, if present.
*/
/*!
\fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(const T &newValue)
Assigns \a newValue to the aliased property and returns a reference to this
QPropertyAlias.
*/
/*!
\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.
Returns any previous binding associated with the property, or a
default-constructed QPropertyBinding<T>.
*/
/*!
\fn template <typename T> QPropertyBinding<T> bool QPropertyAlias<T>::setBinding(const QUntypedPropertyBinding &newBinding)
\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.
Returns true if the type of this property is the same as the type the binding
function returns; false otherwise.
*/
/*!
\fn template <typename T> template <typename Functor> QPropertyBinding<T> QPropertyAlias<T>::setBinding(Functor f)
\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.
Returns any previous binding associated with the property, or a
default-constructed QPropertyBinding<T>.
\sa {Formulating a Property Binding}
*/
/*!
\fn template <typename T> bool QPropertyAlias<T>::hasBinding() const
Returns true if the aliased property is associated with a binding; false
otherwise.
*/
/*!
\fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::binding() const
Returns the binding expression that is associated with the aliased property. A
default constructed QPropertyBinding<T> will be returned if no such
association exists.
*/
/*!
\fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::takeBinding()
Disassociates the binding expression from the aliased property and returns it.
After calling this function, the value of the property will only change if
you assign a new value to it, or when a new binding is set.
*/
/*!
\fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QPropertyAlias<T>::onValueChanged(Functor f)
Registers the given functor \a f as a callback that shall be called whenever
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 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> 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.
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 returned property change handler object keeps track of the subscription. When it
goes out of scope, the callback is unsubscribed.
*/
/*!
\fn template <typename T> template <typename Functor> QPropertyNotifier QPropertyAlias<T>::addNotifier(Functor f)
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 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.
\sa onValueChanged(), subscribe()
*/
/*!
\fn template <typename T> bool QPropertyAlias<T>::isValid() const
Returns true if the aliased property still exists; false otherwise.
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);
Q_ASSERT(newData->size && (newData->size & (newData->size - 1)) == 0); // size is a power of two
size_t index = qHash(p->data) & (newData->size - 1);
while (pp[index].data) {
++index;
if (index == newData->size)
index = 0;
}
new (pp + index) Pair{p->data, QPropertyBindingData(std::move(p->bindingData))};
}
}
// 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)
{
Q_ASSERT(d);
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 *get(QUntypedPropertyData *data, bool create)
{
if (!d) {
if (!create)
return nullptr;
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;
}
if (!create)
return nullptr;
++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_OBJECT_BINDABLE_PROPERTY
macro to declare it. A setter and a getter for the property and a declaration using
Q_PROPERTY have to be made as usual.
Binding related data will automatically be stored within the QBindingStorage
inside the QObject.
*/
QBindingStorage::QBindingStorage()
{
bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus);
Q_ASSERT(bindingStatus);
}
QBindingStorage::~QBindingStorage()
{
QBindingStoragePrivate(d).destroy();
}
void QBindingStorage::reinitAfterThreadMove()
{
bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus);
Q_ASSERT(bindingStatus);
}
void QBindingStorage::clear()
{
QBindingStoragePrivate(d).destroy();
d = nullptr;
bindingStatus = nullptr;
}
void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const
{
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
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);
if (!currentBinding)
return;
auto storage = QBindingStoragePrivate(d).get(dd, true);
if (!storage)
return;
storage->registerWithCurrentlyEvaluatingBinding(currentBinding);
}
QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const
{
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);
}
namespace QtPrivate {
void initBindingStatusThreadId()
{
bindingStatus.threadId = QThread::currentThreadId();
}
BindingEvaluationState *suspendCurrentBindingStatus()
{
auto ret = bindingStatus.currentlyEvaluatingBinding;
bindingStatus.currentlyEvaluatingBinding = nullptr;
return ret;
}
void restoreBindingStatus(BindingEvaluationState *status)
{
bindingStatus.currentlyEvaluatingBinding = status;
}
/*!
\internal
This function can be used to detect whether we are currently
evaluating a binding. This can e.g. be used to defer the allocation
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;
}
bool isPropertyInBindingWrapper(const QUntypedPropertyData *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 {
void printUnsuitableBindableWarning(QAnyStringView prefix, BindableWarnings::Reason reason)
{
switch (reason) {
case QtPrivate::BindableWarnings::NonBindableInterface:
qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
<< "The QBindable does not allow interaction with the binding.";
break;
case QtPrivate::BindableWarnings::ReadOnlyInterface:
qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
<< "The QBindable is read-only.";
break;
default:
case QtPrivate::BindableWarnings::InvalidInterface:
qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
<< "The QBindable is invalid.";
break;
}
}
void printMetaTypeMismatch(QMetaType actual, QMetaType expected)
{
qCWarning(lcQPropertyBinding) << "setBinding: Could not set binding as the property expects it to be of type"
<< actual.name()
<< "but got" << expected.name() << "instead.";
}
} // namespace BindableWarnings end
/*!
\internal
Returns the binding statusof the current thread.
*/
QBindingStatus* getBindingStatus(QtPrivate::QBindingStatusAccessToken) { return &QT_PREPEND_NAMESPACE(bindingStatus); }
} // namespace QtPrivate end
QT_END_NAMESPACE