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.cpp107
1 files changed, 80 insertions, 27 deletions
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 8b14390bc6..caa9fce787 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -27,9 +27,9 @@ void QPropertyBindingPrivatePtr::reset(QtPrivate::RefCounted *ptr) noexcept
{
if (ptr != d) {
if (ptr)
- ptr->ref++;
+ ptr->addRef();
auto *old = std::exchange(d, ptr);
- if (old && (--old->ref == 0))
+ if (old && !old->deref())
QPropertyBindingPrivate::destroyAndFreeMemory(static_cast<QPropertyBindingPrivate *>(d));
}
}
@@ -185,7 +185,7 @@ Q_CONSTINIT 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()
{
@@ -205,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()
{
@@ -239,6 +239,41 @@ void Qt::endPropertyUpdateGroup()
}
}
+/*!
+ \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>);
@@ -275,7 +310,7 @@ void QPropertyBindingPrivate::unlinkAndDeref()
{
clearDependencyObservers();
propertyDataPtr = nullptr;
- if (--ref == 0)
+ if (!deref())
destroyAndFreeMemory(this);
}
@@ -420,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
{
@@ -568,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);
@@ -588,7 +628,15 @@ void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr
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.
+ /* 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)
@@ -630,8 +678,10 @@ QPropertyObserver::QPropertyObserver(ChangeHandler 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
@@ -703,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)
{
@@ -1142,13 +1185,20 @@ QString QPropertyBindingError::description() const
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 \c \l QBindable::QBindable(QObject *obj, const char *property)
+ See \l QBindable::QBindable(QObject *obj, const char *property)
*/
/*!
@@ -1230,15 +1280,13 @@ QString QPropertyBindingError::description() const
can be used to express relationships between different properties in your
application.
- \note In the case of QML it is important that \l QProperty needs to be exposed
- 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 be then interacted with C++ via the normal API like:
-
+ \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, then the engine will use the change-tracking
- inherent to the C++ property system for getting notified about changes; and
+ 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.
*/
@@ -1626,13 +1674,13 @@ 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
@@ -2429,8 +2477,13 @@ QPropertyAdaptorSlotObject::QPropertyAdaptorSlotObject(QObject *o, const QMetaPr
{
}
+#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) {