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.cpp1002
1 files changed, 570 insertions, 432 deletions
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index fa22c6f498..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"
@@ -44,6 +8,9 @@
#include <QScopeGuard>
#include <QtCore/qloggingcategory.h>
#include <QThread>
+#include <QtCore/qmetaobject.h>
+
+#include "qobject_p.h"
QT_BEGIN_NAMESPACE
@@ -60,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));
}
}
@@ -154,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, QBindingStatus *status) {
+ void evaluateBindings(PendingBindingObserverList &bindingObservers, qsizetype index, QBindingStatus *status) {
auto *delayed = delayedProperties + index;
auto *bindingData = delayed->originalBindingData;
if (!bindingData)
@@ -170,7 +137,7 @@ struct QPropertyDelayedNotifications
QPropertyBindingDataPointer bindingDataPointer{bindingData};
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
- observer.evaluateBindings(status);
+ observer.evaluateBindings(bindingObservers, status);
}
/*!
@@ -182,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 QBindingStatus bindingStatus;
+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
@@ -218,7 +185,7 @@ static thread_local QBindingStatus bindingStatus;
properties need to be updated, preventing any external observer from noticing an inconsistent
state.
- \sa Qt::endPropertyUpdateGroup
+ \sa Qt::endPropertyUpdateGroup, QScopedPropertyUpdateGroup
*/
void Qt::beginPropertyUpdateGroup()
{
@@ -230,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.
@@ -238,7 +205,7 @@ 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()
{
@@ -249,24 +216,69 @@ void Qt::endPropertyUpdateGroup()
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, status);
+ 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)
@@ -276,25 +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(QBindingStatus *status)
+bool QPropertyBindingPrivate::evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
{
if (!status)
status = &bindingStatus;
- return evaluateRecursive_inline(status);
+ return evaluateRecursive_inline(bindingObservers, status);
+}
+
+void QPropertyBindingPrivate::notifyNonRecursive(const PendingBindingObserverList &bindingObservers)
+{
+ notifyNonRecursive();
+ for (auto &&bindingObserver: bindingObservers) {
+ bindingObserver.binding()->notifyNonRecursive();
+ }
}
-void QPropertyBindingPrivate::notifyRecursive()
+QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRecursive()
{
if (!pendingNotify)
- return;
+ return Delayed;
pendingNotify = false;
Q_ASSERT(!updating);
updating = true;
@@ -305,6 +343,7 @@ void QPropertyBindingPrivate::notifyRecursive()
if (hasStaticObserver)
staticObserverCallback(propertyDataPtr);
updating = false;
+ return Sent;
}
/*!
@@ -416,7 +455,7 @@ QPropertyBindingError QUntypedPropertyBinding::error() const
/*!
Returns the meta-type of the binding.
- If the QUntypedProperyBinding is null, an invalid QMetaType is returned.
+ If the QUntypedPropertyBinding is null, an invalid QMetaType is returned.
*/
QMetaType QUntypedPropertyBinding::valueMetaType() const
{
@@ -428,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();
@@ -474,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 {
@@ -561,6 +603,11 @@ 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();
Q_ASSERT(QPropertyObserver::ObserverNotifiesBinding == 0);
dependencyObserver.setBindingToNotify_unsafe(currentState->binding);
@@ -578,37 +625,49 @@ void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr
return;
QPropertyBindingDataPointer d{this};
+ PendingBindingObserverList bindingObservers;
if (QPropertyObserverPointer observer = d.firstObserver()) {
- auto status = storage ? storage->bindingStatus : nullptr;
- QPropertyDelayedNotifications *delay;
-#ifndef QT_HAS_FAST_CURRENT_THREAD_ID
+ 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();
+ }
+ }
+}
+
+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
- if (!status || status->threadId != QThread::currentThreadId())
- status = &bindingStatus;
+ Q_UNUSED(storage);
+ QBindingStatus *status = &bindingStatus;
#endif
- delay = status->groupUpdateData;
- if (delay) {
- delay->addProperty(this, propertyDataPtr);
- return;
- }
- observer.evaluateBindings(status);
- } else {
- return;
+ if (QPropertyDelayedNotifications *delay = status->groupUpdateData) {
+ delay->addProperty(this, propertyDataPtr);
+ return Delayed;
}
- // evaluateBindings() can trash the observers. We need to re-fetch here.
- if (QPropertyObserverPointer observer = d.firstObserver())
- observer.notify(propertyDataPtr);
+ observer.evaluateBindings(bindingObservers, status);
+ return Evaluated;
}
-int QPropertyBindingDataPointer::observerCount() const
-{
- int count = 0;
- for (auto observer = firstObserver(); observer; observer = observer.nextObserver())
- ++count;
- return count;
-}
QPropertyObserver::QPropertyObserver(ChangeHandler changeHandler)
{
@@ -616,11 +675,15 @@ QPropertyObserver::QPropertyObserver(ChangeHandler changeHandler)
d.setChangeHandler(changeHandler);
}
+#if QT_DEPRECATED_SINCE(6, 6)
QPropertyObserver::QPropertyObserver(QUntypedPropertyData *data)
{
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
aliasData = data;
next.setTag(ObserverIsAlias);
+ QT_WARNING_POP
}
+#endif
/*! \internal
*/
@@ -668,37 +731,20 @@ QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexc
return *this;
}
-#define UNLINK_COMMON \
- 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
*/
-void QPropertyObserverPointer::unlink()
-{
- UNLINK_COMMON
- if (ptr->next.tag() == QPropertyObserver::ObserverIsAlias)
- ptr->aliasData = nullptr;
-}
+
/*!
+ \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::unlink_fast()
-{
- Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsAlias);
- UNLINK_COMMON
-}
-#undef UNLINK_COMMON
void QPropertyObserverPointer::setChangeHandler(QPropertyObserver::ChangeHandler changeHandler)
{
@@ -707,16 +753,9 @@ void QPropertyObserverPointer::setChangeHandler(QPropertyObserver::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.
+ The same as setBindingToNotify, but assumes that the tag is already correct.
*/
void QPropertyObserverPointer::setBindingToNotify_unsafe(QPropertyBindingPrivate *binding)
{
@@ -725,90 +764,17 @@ void QPropertyObserverPointer::setBindingToNotify_unsafe(QPropertyBindingPrivate
}
/*!
+ \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_fast();
- }
-};
-
-/*! \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;
- case QPropertyObserver::ObserverIsAlias:
- break;
- default: Q_UNREACHABLE();
- }
- observer = next;
- }
-}
#ifndef QT_NO_DEBUG
void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *binding)
@@ -828,7 +794,7 @@ void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *bindi
}
#endif
-void QPropertyObserverPointer::evaluateBindings(QBindingStatus *status)
+void QPropertyObserverPointer::evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
{
Q_ASSERT(status);
auto observer = const_cast<QPropertyObserver*>(ptr);
@@ -839,7 +805,9 @@ void QPropertyObserverPointer::evaluateBindings(QBindingStatus *status)
if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) {
auto bindingToEvaluate = observer->binding;
QPropertyObserverNodeProtector protector(observer);
- bindingToEvaluate->evaluateRecursive_inline(status);
+ 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();
}
@@ -1185,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
@@ -1196,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)
*/
/*!
@@ -1277,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.
*/
/*!
@@ -1307,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()
@@ -1351,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.
*/
/*!
@@ -1375,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}
*/
@@ -1388,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.
@@ -1419,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.
*/
/*!
@@ -1448,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()
*/
@@ -1469,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
@@ -1479,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.
@@ -1494,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.
@@ -1514,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}
*/
@@ -1640,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.
@@ -1650,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
@@ -1700,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.
@@ -1749,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.
+
*/
/*!
@@ -1800,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()
*/
@@ -1816,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.
*/
/*!
@@ -1828,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}
*/
@@ -1842,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.
*/
/*!
@@ -1877,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()
*/
@@ -1930,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.
*/
@@ -1948,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.
*/
@@ -1960,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
@@ -2038,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>.
@@ -2075,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.
@@ -2089,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>.
@@ -2130,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.
@@ -2141,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.
*/
/*!
@@ -2159,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()
*/
@@ -2319,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
@@ -2361,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);
@@ -2402,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 {
@@ -2436,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